跳转到内容

UI dimensions

UI dimensions (shell / layout / interaction)

Section titled “UI dimensions (shell / layout / interaction)”

Portal UI is designed along three orthogonal dimensions. Visual foundations, components, and interaction patterns all adapt through this lens. Phone size does not imply compact layout; Tauri does not imply touch.

Agent API tables and hard bans → .agent/rules/ui-dimensions.md. Implementation entry points are listed there; this page is the product narrative.

ConceptWhat it isCode
ShellPortal runtime host (browser / Tauri; build targets web / desktop / mobile)src/portal/app/*; portal-sdk (getShellKind, ShellApi, buildTarget)
app frameModule rail / bottom tabs, settings chromesrc/client/app-frame (AppFrame); follows viewport, not Shell

Do not call app frame “Shell”. Do not derive rail vs bottom tabs from Shell kind.

DimensionControlsValuesHow decidedStability
ShellCapabilities (native APIs, files, push, IPC)web / tauri (+ buildTarget)Build + runtime (getShellKind / getShellBuildTarget)Fixed per install
LayoutPresentation (wide/narrow chrome, nav IA)compact / expandedCSS matchMedia (viewport)Changes with window size
InteractionInput paradigmtouch / pointerprimaryInput(pointer: fine) + (hover: hover)Usually stable; can follow peripherals

Colloquial “mobile layout / desktop layout” means compact / expanded — not phone shell / desktop shell.

  • Dimensions are orthogonal: none may imply the other two.
  • Forbidden: isMobile = getShellKind() === "tauri" (or similar mixes).
  • Forbidden: one isMobile / isDesktop flag driving both layout and interaction.
  • Components pick APIs by responsibility; do not hand-roll isTauri && matchMedia(...) to choose menus.

Use for: file/FS bridges, notifications, Habitat settings visibility, hash navigation quirks, keyboard inset / safe-area host differences.

Do not use Shell kind to lock main nav (rail vs tabs), hover affordances, or Enter-to-send.

Unsupported capabilities return null / false; UI degrades.

Use for: compact bottom nav + drawer vs expanded rail + multi-column; Dialog vs Sheet presentation; list-detail stacking; settings chrome (tabs vs sidebar via detectSettingsChromePlatform()).

Common layout patterns:

  • List-Detail — side-by-side when expanded; stack + route when compact (ListDetailLayout)
  • Grid-List — multi-column vs single column
  • Modal-Sheet — centered Dialog when expanded; bottom Sheet when compact (presentation = layout; gesture = interaction)
  • Sidebar-Drawer — fixed sidebar vs hamburger + drawer; compact viewport-fixed layers must clear --app-bottom-nav-h

Settings section fields may follow shell (resolveSettingsContentPlatform()); settings chrome follows layout.

Use for: ContextMenu vs ActionSheet / long-press; hover-revealed actions; Enter send vs newline; minimum hit targets.

Conventions:

  • touch — hit targets ≥44px; no hover-only affordances; long-press / ActionSheet
  • pointer — hover, right-click ContextMenu
  • External keyboard on a pad does not flip interaction to pointer; strategy stays touch
  • Keyboard-open detection is interaction; WebView resize differences may use shell helpers (e.g. useKeyboardInset)
DimensionValue
Shellweb or tauri (how the user opened Portal)
LayoutLive viewport (landscape → expanded, portrait → compact)
Interactiontouch (primaryInput: "touch")

When specifying visuals, components, or patterns, document:

  1. Dimension-invariant — shared contract
  2. By layout — compact vs expanded (or N/A)
  3. By interaction — pointer vs touch (or N/A)
  4. By shell — capability-only differences (or N/A)
  5. Forbidden mixes — e.g. Shell deciding hover; viewport width deciding ContextMenu vs ActionSheet