diff --git a/src/app/components/stream-header/StreamHeader.css.ts b/src/app/components/stream-header/StreamHeader.css.ts index 6a5cff1d..b2303c47 100644 --- a/src/app/components/stream-header/StreamHeader.css.ts +++ b/src/app/components/stream-header/StreamHeader.css.ts @@ -228,8 +228,17 @@ export const handleBar = style({ // up over the inline form. The DirectSelfRow ending up immediately // above the keyboard would block the user's view of the form they're // typing into. +// +// `paddingBottom: env(safe-area-inset-bottom)` lifts DirectSelfRow / +// WorkspaceFooter above Android 3-button nav in edge-to-edge mode. +// The inset zone paints in the curtain's `Background.Container` bg +// (curtain has `overflow: hidden`), so the system bar reads as a +// continuation of the curtain tone — no visible seam. The keyboard +// `height: 0; overflow: hidden` collapse above also clips the +// padding region — keyboard handling preserved. export const bottomPinnedSlot = style({ flexShrink: 0, + paddingBottom: 'env(safe-area-inset-bottom, 0px)', }); // Segment button (Direct / Channels / Bots). diff --git a/src/app/features/room/RoomView.css.ts b/src/app/features/room/RoomView.css.ts index 03085920..d9c1020c 100644 --- a/src/app/features/room/RoomView.css.ts +++ b/src/app/features/room/RoomView.css.ts @@ -1,10 +1,6 @@ import { globalStyle, style } from '@vanilla-extract/css'; import { color, toRem } from 'folds'; -import { - Editor, - EditorTextarea, - EditorTextareaScroll, -} from '../../components/editor/Editor.css'; +import { Editor, EditorTextarea, EditorTextareaScroll } from '../../components/editor/Editor.css'; import { VOJO_HORSESHOE_RADIUS_PX } from '../../styles/horseshoe'; // Main chat composer — Dawn canon (stream-v2-dawn.jsx line 285-307): a @@ -28,6 +24,13 @@ export const ChatComposer = style({}); // slide/fade transition driven by the `data-hidden` attribute set from // React state. CSS class (not inline `transition`) so the // `prefers-reduced-motion` media query can disable the motion. +// +// `env(safe-area-inset-bottom)` for Android 3-button-nav clearance lives +// on the INNER `ChatComposer` div (see `RoomView.tsx`), not here. The +// wrapper is the `ResizeObserver` target for `composerHeight`, and +// `entry.contentRect` returns content-box (excludes the observed node's +// own padding); padding here would inflate border-box without growing +// the reported height, letting the last message slip behind the inset. export const ComposerOverlay = style({ position: 'absolute', left: 0, diff --git a/src/app/features/room/RoomView.tsx b/src/app/features/room/RoomView.tsx index 4b4b0198..3428055e 100644 --- a/src/app/features/room/RoomView.tsx +++ b/src/app/features/room/RoomView.tsx @@ -174,8 +174,13 @@ export function RoomView({ eventId }: { eventId?: string }) { >
{tombstoneEvent ? ( diff --git a/src/app/features/room/RoomViewTyping.css.ts b/src/app/features/room/RoomViewTyping.css.ts index 7c01249d..467aa514 100644 --- a/src/app/features/room/RoomViewTyping.css.ts +++ b/src/app/features/room/RoomViewTyping.css.ts @@ -14,6 +14,12 @@ export const RoomViewTyping = style([ DefaultReset, { padding: `0 ${config.space.S500}`, + // Lift typing text above the Android 3-button nav for the + // composer-hidden window (thread drawer open / scroll-hide). The + // main composer sits at `bottom: 0` with its own inset and fully + // covers this strip at rest, so the duplicate inset is invisible + // in normal flow. + paddingBottom: 'env(safe-area-inset-bottom, 0px)', width: '100%', backgroundColor: color.SurfaceVariant.Container, color: color.Surface.OnContainer, diff --git a/src/app/features/room/ThreadDrawer.css.ts b/src/app/features/room/ThreadDrawer.css.ts index 1144749e..a4572469 100644 --- a/src/app/features/room/ThreadDrawer.css.ts +++ b/src/app/features/room/ThreadDrawer.css.ts @@ -1,9 +1,6 @@ import { style } from '@vanilla-extract/css'; import { color, config, toRem } from 'folds'; -import { - VOJO_HORSESHOE_GAP_PX, - VOJO_HORSESHOE_RADIUS_PX, -} from '../../styles/horseshoe'; +import { VOJO_HORSESHOE_GAP_PX, VOJO_HORSESHOE_RADIUS_PX } from '../../styles/horseshoe'; // Desktop wrapper for the resizable thread drawer. Sizing and the // absolutely-positioned resize handle live here; the inner aside @@ -231,9 +228,15 @@ export const ThreadCounterText = style({ // reapplied to the inner wrap in `ThreadDrawer.tsx` so the same // `globalStyle` rules (`Surface.Container` bg, 32px radius, dark // touch-hover gate) reach the Editor inside. +// +// Bottom side adds `env(safe-area-inset-bottom)` so the Send button +// clears Android 3-button nav in edge-to-edge mode, mirroring the +// main `ChatComposer` in `RoomView.tsx`. export const ThreadComposer = style({ flexShrink: 0, - padding: `0 ${toRem(VOJO_HORSESHOE_GAP_PX)} ${toRem(VOJO_HORSESHOE_GAP_PX)}`, + padding: `0 ${toRem(VOJO_HORSESHOE_GAP_PX)} calc(${toRem( + VOJO_HORSESHOE_GAP_PX + )} + env(safe-area-inset-bottom, 0px))`, }); // Bubble chrome itself lives in `Channel.css.ts` and applies via the diff --git a/src/app/styles/global.css.ts b/src/app/styles/global.css.ts index 409554b0..13852b23 100644 --- a/src/app/styles/global.css.ts +++ b/src/app/styles/global.css.ts @@ -17,8 +17,16 @@ import { color } from 'folds'; // Bottom inset is owned per-component: surfaces that anchor // interactive content at the screen bottom and need 3-button-nav / // home-indicator clearance read `env(safe-area-inset-bottom)` -// themselves (e.g. `SyncIndicator.css.ts`). The chat surface / -// composer extend flush to `body_bottom` by design. +// themselves (e.g. `SyncIndicator.css.ts`, +// `StreamHeader.bottomPinnedSlot` for DirectSelfRow / WorkspaceFooter, +// `RoomView.tsx` ChatComposer inner div, the `panelContent` rules in +// MobileSettings / ChannelsWorkspace / MobileMediaViewer horseshoes). +// The chat overlay wrapper (`ComposerOverlay`) itself stays `bottom: 0` +// flush — the inset lives on the inner card padding so +// `ResizeObserver.contentRect` keeps `composerHeight` in sync with +// the visible overlay height for `RoomTimeline.bottomOverlayHeight`. +// Bot widgets are responsible for their own gesture-pill clearance +// (see `BotShell.css.ts::Shell`). globalStyle(':root', { vars: { '--vojo-safe-top': 'env(safe-area-inset-top, 0px)',