fix(stream-bubble): shrink bubble to text width and equalise bubble-to-bubble gap at ~14px regardless of same-sender grouping

This commit is contained in:
heaven 2026-05-28 00:30:54 +03:00
parent 67ee378b39
commit 53acca3755
3 changed files with 30 additions and 14 deletions

View file

@ -6,8 +6,11 @@ import { useStreamLayoutDebug } from './streamDebug';
import { ScreenSize, useScreenSizeContext } from '../../../hooks/useScreenSize'; import { ScreenSize, useScreenSizeContext } from '../../../hooks/useScreenSize';
import { Time } from '../Time'; import { Time } from '../Time';
// Stream rows use a fixed `S400` gap so the rail-bridge offsets in // Day-divider rows fall back to this `S400` MessageBase spacing variant
// layout.css.ts (StreamRailBridgeY = S400) match the gap between rows. // (see RoomTimeline.renderDayDivider). Message rows force collapse=true
// in Message.tsx so their marginTop drops to 0 — the rail-bridge offsets
// still resolve against `StreamRailBridgeY = S400` and the small overlap
// over the tighter gap stays hidden by the dot halo.
export const STREAM_MESSAGE_SPACING = '400' as const; export const STREAM_MESSAGE_SPACING = '400' as const;
// Sample timestamp used by the day-divider's invisible track-1 placeholder. // Sample timestamp used by the day-divider's invisible track-1 placeholder.
@ -159,10 +162,7 @@ export const StreamLayout = as<'div', StreamLayoutProps>(
)} )}
ref={railRef} ref={railRef}
/> />
<span <span className={classNames(css.StreamDotHalo, css.StreamHeaderDotHalo)} ref={dotRef}>
className={classNames(css.StreamDotHalo, css.StreamHeaderDotHalo)}
ref={dotRef}
>
<span <span
className={css.StreamDotFill} className={css.StreamDotFill}
style={{ backgroundColor: dotColor, opacity: dotOpacity }} style={{ backgroundColor: dotColor, opacity: dotOpacity }}

View file

@ -209,8 +209,13 @@ export const StreamRoot = recipe({
columnGap: StreamRowGapVar, columnGap: StreamRowGapVar,
paddingLeft: StreamRowPadVar, paddingLeft: StreamRowPadVar,
alignItems: 'flex-start', alignItems: 'flex-start',
paddingTop: config.space.S100, // Vertical padding tuned to ~3px (down from S100/4px) so the
paddingBottom: config.space.S100, // bubble-to-bubble gap reads tighter. Per-row contribution to the
// gap drops from 8px to 7px on each side — combined with
// MessageBase's S100 top/bot the total bubble↔bubble gap is
// 4 + 3 + 0 (collapsed marginTop) + 3 + 4 = 14px ≈ S400/1.14.
paddingTop: toRem(3),
paddingBottom: toRem(3),
paddingRight: config.space.S400, paddingRight: config.space.S400,
}, },
variants: { variants: {
@ -435,13 +440,16 @@ export const StreamBubble = recipe({
borderRadius: `${toRem(4)} ${toRem(16)} ${toRem(16)} ${toRem(16)}`, borderRadius: `${toRem(4)} ${toRem(16)} ${toRem(16)} ${toRem(16)}`,
}, },
}, },
// Mobile fills the message column (block 100%); desktop fits content // Both breakpoints fit content (inline-block + fit-content + max-width
// (inline-block fit-content). Branched via useScreenSizeContext, not // 100% of the message column). Per user feedback бабл должен быть «по
// CSS media queries — see docs/plans/dm_1x1_redesign.md §5.5. // размеру текстового сообщения», not stretched to the column's right
// edge on mobile. Padding still tightens on mobile (S300 vs 15px) to
// keep the bubble visually compact on narrow viewports.
compact: { compact: {
true: { true: {
display: 'block', display: 'inline-block',
width: '100%', width: 'fit-content',
maxWidth: '100%',
paddingLeft: config.space.S300, paddingLeft: config.space.S300,
paddingRight: config.space.S300, paddingRight: config.space.S300,
}, },

View file

@ -896,7 +896,15 @@ export const Message = as<'div', MessageProps>(
className={classNames(css.MessageBase, className)} className={classNames(css.MessageBase, className)}
tabIndex={0} tabIndex={0}
space={layout === 'channel' ? CHANNEL_MESSAGE_SPACING : STREAM_MESSAGE_SPACING} space={layout === 'channel' ? CHANNEL_MESSAGE_SPACING : STREAM_MESSAGE_SPACING}
collapse={collapse} // Stream rows always render with collapsed marginTop (=0) so the
// bubble-to-bubble gap is uniform regardless of same-sender grouping.
// Total gap = 4×S100 (StreamRoot + MessageBase vertical padding on
// both sides) = 16px. The rail-bridge in layout.css.ts is still S400
// each side — it overshoots slightly across the smaller gap, but the
// overlap lands inside DotColumn behind the dot halo and stays
// invisible. `collapse` still drives avatar/header visibility inside
// ChannelLayout — channel mode keeps the original behaviour.
collapse={layout === 'channel' ? collapse : true}
highlight={highlight} highlight={highlight}
selected={!!menuAnchor || !!emojiBoardAnchor} selected={!!menuAnchor || !!emojiBoardAnchor}
{...props} {...props}