import { style } from '@vanilla-extract/css'; import { color, toRem } from 'folds'; import { VOJO_HORSESHOE_GAP_PX, VOJO_HORSESHOE_RADIUS_PX } from '../../../styles/horseshoe'; // Re-exported so the TSX can pick up the constants without crossing the // vanilla-extract / runtime boundary twice. Mirror of the canonical // MobileSettingsHorseshoe css module. export const HORSESHOE_RADIUS_PX = VOJO_HORSESHOE_RADIUS_PX; export const HORSESHOE_GAP_PX = VOJO_HORSESHOE_GAP_PX; // Outer container — anchor for the two absolutely-positioned panes // (`appBody` and `silhouette`). `flex: 1` fills PageNav's inner column // slot. `overflow: hidden` clips the rounded carves against the // container's bg, which is painted inline with the void colour when // the sheet is active. See MobileSettingsHorseshoe.css.ts for the full // rationale on every property here — kept verbatim except for the file // header. // // `marginTop: -var(--vojo-safe-top)` extends the container UP over the // status-bar safe-top zone, and the compensating // `paddingTop: var(--vojo-safe-top)` on `appBody` keeps the wrapped // channels list anchored at the same visual Y. Mirror of the same // pair in `MobileSettingsHorseshoe.css.ts::container` — purpose is // (1) the workspace-sheet clip-path mask on `appBody` carves into an // opaque surface that already paints through the status-bar strip, // and (2) `appBody`'s bg paints the safe-top strip in the same // `SurfaceVariant.Container` tone as the pager's static header so the // system-tray backdrop stays consistent across surfaces. // // The curtain itself never paints into the safe-top zone — pin // gesture clamps it at `top: 0` of the stage, see // `components/stream-header/geometry.ts::PIN_TRAVEL_PX`. export const container = style({ position: 'relative', display: 'flex', flex: 1, flexDirection: 'column', minWidth: 0, minHeight: 0, overflow: 'hidden', marginTop: 'calc(-1 * var(--vojo-safe-top, 0px))', }); // Wrapped children (StreamHeader → ChannelsList → WorkspaceFooter). // Stays put — the bottom is carved away by an animated // `clip-path: inset(...)` with rounded BL/BR. `backgroundColor` is // load-bearing: the container is painted with the void colour when the // sheet is active, so without an opaque bg the void would bleed through // every transparent gap between list rows. See canonical for the full // reasoning on clip-path vs translate vs flex-shrink. // // `paddingTop: var(--vojo-safe-top)` reserves status-bar space INSIDE // appBody — compensates the `container.marginTop: -safe-top` shift so // the wrapped flex children (StreamHeader.stage) stay anchored at the // same visual Y as before. `backgroundColor` is `SurfaceVariant.Container` // (not `Background.Container`) so the now-visible safe-top zone of // appBody matches the tone `PageNav-inner` shows in Bots / Channels-root // (which use `surface="surfaceVariant"`), giving the curtain's darker // `Background.Container` tone a visible strip to over-paint on drag-up. export const appBody = style({ position: 'absolute', top: 0, left: 0, right: 0, bottom: 0, display: 'flex', flexDirection: 'column', minWidth: 0, minHeight: 0, backgroundColor: color.SurfaceVariant.Container, paddingTop: 'var(--vojo-safe-top, 0px)', willChange: 'clip-path', }); // Workspace switcher sheet surface. Anchored at the bottom of the // container; height animates 0 → railHeight as the user drags / clicks. // `SurfaceVariant.Container` matches the chat-pane tone and keeps the // safe-area / handle gaps from reading as dark stripes on edge-to-edge // Android — same rationale as the canonical settings horseshoe. export const silhouette = style({ position: 'absolute', bottom: 0, left: 0, right: 0, display: 'flex', flexDirection: 'column', overflow: 'hidden', backgroundColor: color.SurfaceVariant.Container, willChange: 'height, border-top-left-radius, border-top-right-radius', }); // Top-anchored panel content. Padding-bottom reserves Android nav-bar // inset so the create-space row never tucks under the gesture pill. export const panelContent = style({ position: 'absolute', top: 0, left: 0, right: 0, boxSizing: 'border-box', display: 'flex', flexDirection: 'column', paddingBottom: 'env(safe-area-inset-bottom, 0px)', }); // 20px drag-to-close band at the top of the silhouette. The ONLY // drag-down origin once the sheet is open — touches on the spaces list // below this strip are not drag-sensitive, so internal scroll keeps // working without a gesture conflict. export const panelHandle = style({ flexShrink: 0, height: toRem(20), display: 'flex', alignItems: 'center', justifyContent: 'center', cursor: 'grab', touchAction: 'none', userSelect: 'none', selectors: { '&:active': { cursor: 'grabbing' }, }, }); export const panelHandleBar = style({ width: toRem(36), height: toRem(4), borderRadius: toRem(4), backgroundColor: color.Background.Container, }); // Holds the workspace switcher list. `flex: 1` so it grows below the // 20px handle to fill the remaining panel height; internal Scroll inside // the switcher handles overflow when the user has many orphan spaces. export const panelBody = style({ flex: 1, display: 'flex', flexDirection: 'column', minHeight: 0, minWidth: 0, });