Navigation

Generated from DESIGN_SYSTEM.md

Rules

  • Phone top band is a route decision. <main> pads for the fixed phone nav with the one value --shell-mobile-top; a route whose first section is a hero opts into mobile-top-bleed on that section so its own surface fills the band (www /about). Dashboards keep the band. Never pad or pull with a literal 4.5rem in a view.

Sidebar / Drawer:

  • Item spacing: 6pt in iOS (VStack(spacing: 6)) and space-y-1.5 (6px) in Web to provide professional breathing room and modern, clean density.
  • Item vertical padding: 10pt for comfortable tap-targets (minimum 48pt+ row height / touch target).
  • Item font size: 14–14.5pt/px, semibold when active.
  • Active state background: brand-container (pastel blue).
  • Active state text: brand-on-container (brand blue / blue-400 dark).
  • Icon Container Tiles: Raw icons must be enclosed inside a 28x28 rounded-rectangle (cornerRadius: 8) tile for structural visual balance.
  • Inactive: 6% opacity fill (0.06) + 18% opacity stroke outline (0.18) (same themeColor). Icon uses .symbolRenderingMode(.hierarchical) for natural depth shading.
  • Active: solid themeColor fill, no stroke, white icon.
  • Footer language selector: collapsed label shows the full native name (English / ν•œκ΅­μ–΄ / ζ—₯本θͺž). Country selector shows the full local name (λŒ€ν•œλ―Όκ΅­ / United States / ζ—₯本). Never show raw ISO codes as the label.
  • Footer Selectors (Language & Country): Clean and compact. Replace default select boxes with compact menus. To save space, collapsed labels must NOT show emoji flags. Instead, they must use generic system icons (globe for language, mappin.and.ellipse for country) with font size reduced to 9.5pt and container height set to 26pt with 8pt corner radius.
  • Never use solid fill (brandPrimary.opacity(0.12)) for active nav β€” always use brand-container.
  • Header alignment: center-vertical β€” never baseline (baseline misaligns icon with logo).
  • Footer layout: vertical stack β€” never horizontal capsule cramming auth + theme + settings.

Bottom Tab Bar (mobile):

  • Max 5 items
  • Active tab: brand-primary icon + label, brand-container background pill (iOS)
  • Inactive tab: text-secondary / textSecondary color
  • Label always visible β€” no icon-only tabs (Jakob's Law)

Parent-page back navigation:

  • THE standard "go up one level" affordance (detail β†’ catalog, child β†’ parent) is a liquid-glass pill: chevron-left + destination label, h-11 (44pt floor), rounded-full β€” the same floating-chrome family as the mobile top nav's circular buttons and center pill, so back-navigation reads identically across mobile chrome and in-page placements.
  • Web: <PageBackLink href label /> from @yroun/ui (utility back-nav in global.css). iOS: AppNavCircleButton chevron already conforms.
  • Label is the DESTINATION's name, brand-cased ("Yroun Series", a series title) β€” never "Back"/"λ’€λ‘œ".

Page Depth & Up-Navigation β€” structural rule:

The series-detail page shipped with no path back to the series catalog because only the look was canonical β€” presence was left to chance.

  1. Every screen declares its parent. The parent is the screen's logical container in the information architecture (usually the URL's container segment: episode β†’ series detail β†’ series catalog β†’ hub home) β€” never browser history (router.back() is not a parent; it goes wherever the user came from, including another site).
  2. Every screen below a domain root renders its parent affordance (<PageBackLink> web / AppNavCircleButton chevron iOS) at the top of the content column. Domain roots (bottom-tab/sidebar destinations) are exempt β€” their "up" is the tab bar itself.
  3. Documented exemptions only: full-screen immersive surfaces (games, reader mode) may replace the pill with a contextual exit, and modal sheets use their close affordance β€” each exemption named in the surface's code comment.

Live specimens

Parent linkThe pill names the destination, never "Back". This one goes to the overview.
Title tabsTabs that are the card title; the open tab takes the brand container.
Sidebar itemsActive uses the brand container, never a solid fill.