# SAV Design > Shared design system for Swiss Aerospace Ventures apps (Cruiser, contakeeper and future products). It is the Cruiser app look — shadcn/ui style base-sera on Base UI, Tailwind v4, baseColor neutral (oklch), radius 0.625rem, fonts Inter (--font-sans), Figtree (--font-heading) and Geist Mono (--font-geist-mono), lucide icons — distributed as a shadcn registry under the @sav namespace. Components are copied into each app as source files (shadcn model), so local edits stay possible. Gallery: https://design.sasv.ch/ Registry catalogue: https://design.sasv.ch/r/registry.json Machine-readable item list: https://design.sasv.ch/r/index.json Item JSON: https://design.sasv.ch/r/{name}.json ## Rules - No new colours and no new typefaces. State colours are aliases of existing tokens (foreground, muted-foreground, destructive, primary). - Sharp corners: the Sera style sets rounded-none on controls; do not add radii. - Focusable controls are at least 16px on mobile (`text-base md:text-sm`) so iOS does not zoom on focus. - Brand-neutral: app-sidebar and brand-logo take the app name, nav groups, logo assets and callbacks as props. ## Add items to an app 1. Start the app with the same base: `npx shadcn@latest init -p b1HxL3L3Fg` (preset code for base-sera, neutral, Inter/Figtree, lucide) or match components.json: style base-sera, tailwind.baseColor neutral, iconLibrary lucide, menuColor default, menuAccent subtle. 2. Register the namespace in components.json: ```json { "registries": { "@sav": "https://design.sasv.ch/r/{name}.json" } } ``` or run `npx shadcn@latest registry add @sav=https://design.sasv.ch/r/{name}.json`. 3. Install the theme once: `npx shadcn@latest add @sav/tokens` (writes the token block and the three fonts). 4. Add what the page needs: `npx shadcn@latest add @sav/`, e.g. `npx shadcn@latest add @sav/data-panel @sav/stat-card`. Dependencies between items resolve automatically. 5. Update later with `npx shadcn@latest add @sav/ --overwrite`; inspect first with `--diff`. 6. Pin a release by pointing the namespace at a snapshot: `https://design.sasv.ch/r/v1/{name}.json`. ## Tokens Design tokens, fonts and the theme block every SAV app starts from. - [tokens](https://design.sasv.ch/r/tokens.json) (registry:theme): The SAV theme: neutral oklch colour tokens for light and dark mode, radius 0.625rem, sidebar and chart tokens, and the font mappings (Inter → --font-sans, Figtree → --font-heading, Geist Mono → --font-geist-mono). Byte-equivalent to the Cruiser app's globals.css token block. - [font-inter](https://design.sasv.ch/r/font-inter.json) (registry:font): Body typeface. Google font Inter on the --font-sans variable (next/font/google in Next.js, @fontsource-variable/inter elsewhere). - [font-figtree](https://design.sasv.ch/r/font-figtree.json) (registry:font): Heading typeface. Google font Figtree on the --font-heading variable, used through the `font-heading` utility. - [font-geist-mono](https://design.sasv.ch/r/font-geist-mono.json) (registry:font): Code and metrics typeface. Google font Geist Mono on the --font-geist-mono variable, mapped to --font-mono in @theme inline. ## UI primitives shadcn/ui base-sera primitives on Base UI, regenerated with the current CLI and carrying the Cruiser deltas. - [alert-dialog](https://design.sasv.ch/r/alert-dialog.json) (registry:ui): Modal confirmation dialog that interrupts the user; sharp-cornered Sera variant on Base UI AlertDialog. - [avatar](https://design.sasv.ch/r/avatar.json) (registry:ui): Image avatar with automatic initials fallback and three sizes; Base UI Avatar. - [badge](https://design.sasv.ch/r/badge.json) (registry:ui): Uppercase tracking label in default, secondary, destructive and outline variants; renders as any element via `render`. - [breadcrumb](https://design.sasv.ch/r/breadcrumb.json) (registry:ui): Hierarchy trail with separators, ellipsis and link items. - [button](https://design.sasv.ch/r/button.json) (registry:ui): Uppercase tracking button in six variants and eight sizes on Base UI Button. Cruiser delta: `nativeButton` defaults to false when `render` is used so `render={}` keeps anchor semantics. - [card](https://design.sasv.ch/r/card.json) (registry:ui): Sharp-cornered content container with header, title, description, action, content and footer slots; `size="sm"` for stat tiles. - [chart](https://design.sasv.ch/r/chart.json) (registry:ui): Recharts wrapper: ChartContainer with CSS-variable colour config, tooltip and legend styled with the design tokens. - [checkbox](https://design.sasv.ch/r/checkbox.json) (registry:ui): Square checkbox with an enlarged hit area; Base UI Checkbox. - [dialog](https://design.sasv.ch/r/dialog.json) (registry:ui): Modal dialog with overlay, header, footer and optional close button; Base UI Dialog. - [dropdown-menu](https://design.sasv.ch/r/dropdown-menu.json) (registry:ui): Menu with items, checkbox and radio items, groups, labels, shortcuts and submenus; Base UI Menu. - [input](https://design.sasv.ch/r/input.json) (registry:ui): Underlined text input (bottom border only). Mobile rule: text-base below md so iOS does not zoom on focus. - [label](https://design.sasv.ch/r/label.json) (registry:ui): Uppercase tracking form label that relaxes to sentence case next to a checkbox, radio or switch. - [searchable-select](https://design.sasv.ch/r/searchable-select.json) (registry:ui): Single-select typeahead on Base UI Combobox: case- and diacritic-insensitive filtering over label and keywords, keyboard support, 16px controls on mobile. Cruiser original (not in shadcn/ui). - [select](https://design.sasv.ch/r/select.json) (registry:ui): Underlined select with groups, labels and scroll buttons; Base UI Select. Cruiser delta: trigger text is text-base below md (no iOS zoom). - [separator](https://design.sasv.ch/r/separator.json) (registry:ui): Horizontal or vertical divider; Base UI Separator. - [sheet](https://design.sasv.ch/r/sheet.json) (registry:ui): Side panel sliding in from any edge, with header and footer; Base UI Dialog. - [sidebar](https://design.sasv.ch/r/sidebar.json) (registry:ui): Composable sidebar shell: provider, rail, collapsible icon mode, mobile sheet, groups, menus, sub-menus and skeletons. - [skeleton](https://design.sasv.ch/r/skeleton.json) (registry:ui): Pulsing placeholder block for loading states. - [sonner](https://design.sasv.ch/r/sonner.json) (registry:ui): Toast notifications via sonner, themed through next-themes and the popover tokens, with lucide status icons. - [table](https://design.sasv.ch/r/table.json) (registry:ui): Data table primitives (header, body, footer, row, head, cell, caption) with a scrolling wrapper. - [textarea](https://design.sasv.ch/r/textarea.json) (registry:ui): Underlined auto-sizing textarea (field-sizing: content); text-base below md. - [tooltip](https://design.sasv.ch/r/tooltip.json) (registry:ui): Tooltip with provider, arrow and positioning; Base UI Tooltip. - [use-mobile](https://design.sasv.ch/r/use-mobile.json) (registry:hook): matchMedia hook for the 768px breakpoint used by the sidebar. ## App patterns Brand-neutral app-shell and page patterns ported from the Cruiser app. - [app-sidebar](https://design.sasv.ch/r/app-sidebar.json) (registry:component): Authenticated app-shell navigation: brand block, labelled nav groups with active state from usePathname, user menu with links and sign-out callback; icon rail on desktop, drawer on mobile. Brand, groups, user and callbacks are props. - [brand-logo](https://design.sasv.ch/r/brand-logo.json) (registry:component): Theme-aware wordmark: a light-mode and a dark-mode asset passed as props, swapped with the dark variant. - [data-panel](https://design.sasv.ch/r/data-panel.json) (registry:component): Sharp bordered container for tables and lists (bg-card + ring, horizontal scroll built in) with an optional DataPanelHeader title/action row. - [document-viewer](https://design.sasv.ch/r/document-viewer.json) (registry:component): Dialog viewer for images and PDFs: zoom, drag-to-pan, multi-page navigation, download, pdf.js rendering with iframe fallback; InlineDocumentView for embedding without the dialog. - [empty-state](https://design.sasv.ch/r/empty-state.json) (registry:component): Dashed-border centered empty/no-results panel with optional icon, description and action. - [notification-bell](https://design.sasv.ch/r/notification-bell.json) (registry:component): Bell button with unread badge and a dropdown of recent notifications; optional polling through a fetchSummary callback, type labels and link targets as props. - [page-header](https://design.sasv.ch/r/page-header.json) (registry:component): Module page header: heading-font title, optional description, right-aligned action slot, bottom separator. - [pdf-pages](https://design.sasv.ch/r/pdf-pages.json) (registry:component): Renders every page of a PDF to canvases with pdf.js, fit-to-width with zoom and device-pixel-ratio aware; lazy-loads the pdf.js module and worker. - [skeletons](https://design.sasv.ch/r/skeletons.json) (registry:component): Route-level loading skeletons: StatGridSkeleton, TableSkeleton, CardsGridSkeleton, PageHeaderSkeleton. - [stat-card](https://design.sasv.ch/r/stat-card.json) (registry:component): KPI tile: uppercase label, large tabular value, optional icon, hint and monochrome delta arrow. - [user-avatar](https://design.sasv.ch/r/user-avatar.json) (registry:component): Avatar from an optional image URL with first+last-name (or email) initials fallback; serializable props only. ## Imports after installation - Primitives: `@/components/ui/` (the `ui` alias from components.json). - Patterns: `@/components/` (the `components` alias). - Hook: `@/hooks/use-mobile`. - `cn` comes from the `cn` package (shadcn 4.x convention) and is installed automatically.