--- version: alpha name: Spacewave-design-system description: "Spacewave is a dark, local-first application environment with a polished workbench feel: warm near-black surfaces, OKLCH brand reds, solid cards, glass only on floating panels, compact tab chrome, dense object viewers, and a small amount of responsive motion. The app should feel precise and technical without becoming generic developer-tool gray. Marketing and onboarding surfaces may be more expressive, but they still share the same dark canvas, solid low-contrast cards, Manrope typography, Lucide icons, and brand-tinted interaction states." colors: brand: "#ff9ea5" brand-highlight: "#ffa8ae" primary: "#de3955" primary-foreground: "#ffffff" violet: "#948ae3" link-hover: "{colors.violet}" accent: "#f63d6e" success: "#06ff8c" warning: "#fdba8c" error: "#ff8f8f" error-text: "#ffb3b3" error-bg: "#ff8f8f" error-border: "#ff8f8f" destructive: "{colors.error}" destructive-foreground: "{colors.primary-foreground}" foreground: "#ffffff" foreground-alt: "#e1dddd" text-primary: "#f7f1ff" text-secondary: "#e8e3e3" text-muted: "#bcb5b5" background: "#1d1b1b" background-dark: "#0c0b0b" background-card: "#1a1818" background-card-alt: "#161414" background-landing: "#151313" background-get-started: "#181717" background-canvas: "#141212" background-primary: "#1c1a1a" background-secondary: "#211f1f" background-tertiary: "#2a2828" background-panel: "#262324" topbar-back: "#121212" shell-tab-active: "#2a2828" shell-tab-inactive: "#171616" shell-tab-text: "#bcb5b5" shell-tab-text-active: "#ffffff" border: "#464343" window-border: "#ffffff" foreground-border-subtle: "#ffffff" foreground-border: "#ffffff" foreground-border-strong: "#ffffff" tooltip-bg: "#1a1a1a" tooltip-text: "#e6e6e6" tooltip-border: "#ffffff" logo-base: "#030e31" logo-blue: "#37b1dc" logo-pink: "#c35aac" logo-purple: "#83298c" logo-dark: "#050b30" typography: display-xl: fontFamily: "Manrope Variable, -apple-system, BlinkMacSystemFont, Segoe UI, sans-serif" fontSize: 48px fontWeight: 700 lineHeight: 1.12 letterSpacing: 0 display-lg: fontFamily: "Manrope Variable, -apple-system, BlinkMacSystemFont, Segoe UI, sans-serif" fontSize: 36px fontWeight: 700 lineHeight: 1.16 letterSpacing: 0 page-title: fontFamily: "Manrope Variable, -apple-system, BlinkMacSystemFont, Segoe UI, sans-serif" fontSize: 18px fontWeight: 600 lineHeight: 1.35 letterSpacing: 0 figure: fontFamily: "Manrope Variable, -apple-system, BlinkMacSystemFont, Segoe UI, sans-serif" fontSize: 24px fontWeight: 500 lineHeight: 1.25 letterSpacing: 0 panel-title: fontFamily: "Manrope Variable, -apple-system, BlinkMacSystemFont, Segoe UI, sans-serif" fontSize: 14px fontWeight: 600 lineHeight: 1.4 letterSpacing: 0 section-title: fontFamily: "Manrope Variable, -apple-system, BlinkMacSystemFont, Segoe UI, sans-serif" fontSize: 12px fontWeight: 500 lineHeight: 1.4 letterSpacing: 0 body-md: fontFamily: "Manrope Variable, -apple-system, BlinkMacSystemFont, Segoe UI, sans-serif" fontSize: 14px fontWeight: 400 lineHeight: 1.6 letterSpacing: 0 body-sm: fontFamily: "Manrope Variable, -apple-system, BlinkMacSystemFont, Segoe UI, sans-serif" fontSize: 12px fontWeight: 400 lineHeight: 1.5 letterSpacing: 0 control-sm: fontFamily: "Manrope Variable, -apple-system, BlinkMacSystemFont, Segoe UI, sans-serif" fontSize: 12px fontWeight: 500 lineHeight: 1 letterSpacing: 0 micro-cap: fontFamily: "Manrope Variable, -apple-system, BlinkMacSystemFont, Segoe UI, sans-serif" fontSize: 10px fontWeight: 600 lineHeight: 1.4 letterSpacing: 0.08em metadata: fontFamily: "Manrope Variable, -apple-system, BlinkMacSystemFont, Segoe UI, sans-serif" fontSize: 9.6px fontWeight: 500 lineHeight: 1.5 letterSpacing: 0 mono: fontFamily: "Pragmasevka, ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, Liberation Mono, Courier New, monospace" fontSize: 12px fontWeight: 400 lineHeight: 1.5 letterSpacing: 0 rounded: menu-button: 3px xs: 4px editor: 6px sm: 8px md: 12px lg: 16px xl: 20px pill: 9999px spacing: xxs: 4px xs: 8px sm: 12px md: 16px lg: 24px xl: 32px xxl: 48px shell-header: 28px panel-header: 36px components: app-canvas: backgroundColor: "{colors.background}" textColor: "{colors.foreground}" typography: "{typography.body-md}" rounded: 0px padding: 0px landing-canvas: backgroundColor: "{colors.background-landing}" textColor: "{colors.foreground}" typography: "{typography.body-md}" rounded: 0px padding: 0px glass-card: backgroundColor: "{colors.background-card}" textColor: "{colors.foreground}" typography: "{typography.body-sm}" rounded: "{rounded.sm}" padding: 14px info-card: backgroundColor: "{colors.background-card}" textColor: "{colors.foreground}" typography: "{typography.body-sm}" rounded: "{rounded.sm}" padding: 14px action-card: backgroundColor: "{colors.background-card}" textColor: "{colors.foreground}" typography: "{typography.body-sm}" rounded: "{rounded.sm}" padding: 12px dashboard-button: backgroundColor: "{colors.background}" textColor: "{colors.foreground-alt}" typography: "{typography.control-sm}" rounded: "{rounded.menu-button}" padding: 8px 8px primary-action: backgroundColor: "{colors.background-card}" textColor: "{colors.brand}" typography: "{typography.control-sm}" rounded: "{rounded.xs}" padding: 8px 12px text-input: backgroundColor: "{colors.background}" textColor: "{colors.foreground}" typography: "{typography.body-sm}" rounded: "{rounded.xs}" padding: 8px 12px shell-tab: backgroundColor: "{colors.shell-tab-inactive}" textColor: "{colors.shell-tab-text}" typography: "{typography.control-sm}" rounded: "{rounded.editor} {rounded.editor} 0 0" padding: 0px 8px panel-header: backgroundColor: "{colors.background-primary}" textColor: "{colors.foreground}" typography: "{typography.panel-title}" rounded: 0px padding: 0px 16px wizard-shell: backgroundColor: "{colors.background-card}" textColor: "{colors.foreground}" typography: "{typography.body-sm}" rounded: "{rounded.md}" padding: 0px loading-card: backgroundColor: "{colors.background-card}" textColor: "{colors.foreground}" typography: "{typography.body-sm}" rounded: "{rounded.sm}" padding: 14px badge: backgroundColor: "{colors.background-card-alt}" textColor: "{colors.foreground-alt}" typography: "{typography.micro-cap}" rounded: "{rounded.pill}" padding: 2px 8px tooltip: backgroundColor: "{colors.tooltip-bg}" textColor: "{colors.tooltip-text}" typography: "{typography.body-sm}" rounded: "{rounded.xs}" padding: 6px 8px primary-hover: backgroundColor: "{colors.background-card}" textColor: "{colors.brand-highlight}" typography: "{typography.control-sm}" rounded: "{rounded.xs}" padding: 8px 12px primary-drag-affordance: backgroundColor: "{colors.background-dark}" textColor: "{colors.primary}" typography: "{typography.control-sm}" rounded: "{rounded.xs}" padding: 4px 8px primary-foreground-reference: backgroundColor: "{colors.background-dark}" textColor: "{colors.primary-foreground}" typography: "{typography.body-sm}" rounded: "{rounded.xs}" padding: 4px 8px link-hover: backgroundColor: "{colors.background}" textColor: "{colors.link-hover}" typography: "{typography.body-sm}" rounded: "{rounded.xs}" padding: 0px accent-tint: backgroundColor: "{colors.background-dark}" textColor: "{colors.accent}" typography: "{typography.body-sm}" rounded: "{rounded.xs}" padding: 4px 8px status-success: backgroundColor: "{colors.success}" textColor: "{colors.background-dark}" typography: "{typography.micro-cap}" rounded: "{rounded.pill}" padding: 2px 8px status-warning: backgroundColor: "{colors.warning}" textColor: "{colors.background-dark}" typography: "{typography.micro-cap}" rounded: "{rounded.pill}" padding: 2px 8px destructive-callout: backgroundColor: "{colors.background-dark}" textColor: "{colors.error-text}" typography: "{typography.body-sm}" rounded: "{rounded.xs}" padding: 8px 12px destructive-bg-reference: backgroundColor: "{colors.background-dark}" textColor: "{colors.error-bg}" typography: "{typography.body-sm}" rounded: "{rounded.xs}" padding: 4px 8px destructive-border-reference: backgroundColor: "{colors.background-dark}" textColor: "{colors.error-border}" typography: "{typography.body-sm}" rounded: "{rounded.xs}" padding: 4px 8px text-primary-reference: backgroundColor: "{colors.background}" textColor: "{colors.text-primary}" typography: "{typography.body-sm}" rounded: "{rounded.xs}" padding: 0px text-secondary-reference: backgroundColor: "{colors.background}" textColor: "{colors.text-secondary}" typography: "{typography.body-sm}" rounded: "{rounded.xs}" padding: 0px text-muted-reference: backgroundColor: "{colors.background}" textColor: "{colors.text-muted}" typography: "{typography.body-sm}" rounded: "{rounded.xs}" padding: 0px onboarding-card: backgroundColor: "{colors.background-get-started}" textColor: "{colors.foreground}" typography: "{typography.body-sm}" rounded: "{rounded.sm}" padding: 16px destructive-status: backgroundColor: "{colors.background-dark}" textColor: "{colors.error}" typography: "{typography.micro-cap}" rounded: "{rounded.pill}" padding: 2px 8px canvas-panel: backgroundColor: "{colors.background-canvas}" textColor: "{colors.foreground}" typography: "{typography.body-sm}" rounded: "{rounded.sm}" padding: 12px secondary-panel: backgroundColor: "{colors.background-secondary}" textColor: "{colors.foreground}" typography: "{typography.body-sm}" rounded: "{rounded.sm}" padding: 12px tertiary-surface: backgroundColor: "{colors.background-tertiary}" textColor: "{colors.foreground}" typography: "{typography.body-sm}" rounded: "{rounded.sm}" padding: 12px editor-panel: backgroundColor: "{colors.background-panel}" textColor: "{colors.foreground}" typography: "{typography.body-sm}" rounded: "{rounded.editor}" padding: 8px topbar-shell: backgroundColor: "{colors.topbar-back}" textColor: "{colors.foreground}" typography: "{typography.control-sm}" rounded: 0px padding: 0px 8px shell-tab-active: backgroundColor: "{colors.shell-tab-active}" textColor: "{colors.shell-tab-text-active}" typography: "{typography.control-sm}" rounded: "{rounded.editor} {rounded.editor} 0 0" padding: 0px 8px border-reference: backgroundColor: "{colors.border}" textColor: "{colors.foreground}" typography: "{typography.body-sm}" rounded: "{rounded.xs}" padding: 4px 8px window-border-reference: backgroundColor: "{colors.window-border}" textColor: "{colors.background}" typography: "{typography.body-sm}" rounded: "{rounded.xs}" padding: 4px 8px foreground-border-subtle-reference: backgroundColor: "{colors.foreground-border-subtle}" textColor: "{colors.background}" typography: "{typography.body-sm}" rounded: "{rounded.xs}" padding: 4px 8px foreground-border-reference: backgroundColor: "{colors.foreground-border}" textColor: "{colors.background}" typography: "{typography.body-sm}" rounded: "{rounded.xs}" padding: 4px 8px foreground-border-strong-reference: backgroundColor: "{colors.foreground-border-strong}" textColor: "{colors.background}" typography: "{typography.body-sm}" rounded: "{rounded.xs}" padding: 4px 8px tooltip-border-reference: backgroundColor: "{colors.tooltip-border}" textColor: "{colors.tooltip-bg}" typography: "{typography.body-sm}" rounded: "{rounded.xs}" padding: 4px 8px logo-base-reference: backgroundColor: "{colors.logo-base}" textColor: "{colors.logo-blue}" typography: "{typography.body-sm}" rounded: "{rounded.xl}" padding: 8px logo-pink-reference: backgroundColor: "{colors.logo-pink}" textColor: "{colors.logo-dark}" typography: "{typography.body-sm}" rounded: "{rounded.xl}" padding: 8px logo-purple-reference: backgroundColor: "{colors.logo-purple}" textColor: "{colors.foreground}" typography: "{typography.body-sm}" rounded: "{rounded.xl}" padding: 8px allow: - rule: type.size uppercase: true minSize: 10 decision: "Typography: micro-cap labels, badges and figure labels at 10px tracked capitals" --- ## Overview Spacewave's visual language is a dense dark workbench, not a light SaaS shell and not a black marketing poster. The app lives on warmed near-black OKLCH surfaces with white foreground text, soft secondary text, and a red-pink brand accent used sparingly for focus, selected state, and primary action. The strongest visual identity comes from low-contrast solid cards, compact tab chrome, a gradient animated app icon, and precise control density. The system has two related modes. The app mode is compact: panels, ObjectViewer surfaces, Space layouts, session settings, command palettes, and wizards should optimize for repeated work and scanning. The marketing and onboarding mode is more expressive: taller sections, scroll reveal, larger titles, bigger cards, and animated logo treatments are allowed, but the same tokens and interaction rules still apply. **Key Characteristics:** - Warm near-black surfaces instead of neutral slate: `{colors.background}`, `{colors.background-card}`, and `{colors.background-primary}` define the base. - Brand red-pink as an interaction accent, not a full-page wash. Use `{colors.brand}` mostly at 5-15% background opacity or 30-60% border opacity. - Cards in the page flow are solid: a low-opacity card background, a subtle white border, no backdrop blur, and no heavy shadow. Backdrop blur belongs only to surfaces that float over other content: menus, popovers, tooltips, toasts, floating windows, toolbars, and overlays. - Figures sit side by side in one figure strip, not one icon card per number. - Compact app chrome: 28px shell header, 36px detail-panel header, 22px shell tabs, 7px-9px icon buttons, and 12px section labels. - Manrope Variable for UI and display; Pragmasevka only for code, terminals, hashes, object refs, and numeric technical output. - Lucide-style line icons from `react-icons/lu` for UI meaning. Icons should be small, aligned, and inside tinted square or circular containers when they carry row hierarchy. - Motion is functional: hover lifts, reveal fades, loading shine borders, logo parallax, and progress animation. Respect reduced motion. ## Colors > **Source surfaces:** `web/style/app.css`, `app/landing/*`, > `app/session/dashboard/*`, `app/wizard/WizardShell.tsx`, > `web/ui/loading/*`, and `web/style/flexlayout/*`. ### Brand - **Brand** (`{colors.brand}`): the primary Spacewave accent. It reads as a warm red-pink in SDR and becomes richer on P3/HDR displays. - **Brand Highlight** (`{colors.brand-highlight}`): hover and animated accent color. Use it for hover borders, animated logo gradients, and active edge accents. - **Primary** (`{colors.primary}`): stronger accent used by lower-level FlexLayout and shell drag affordances. Prefer `{colors.brand}` for product UI unless the existing shell styling already uses primary. - **Violet / Link Hover** (`{colors.violet}`): hover treatment for links. It is a supporting link color, not a second brand. ### Surface - **Background** (`{colors.background}`): default app canvas. - **Background Dark** (`{colors.background-dark}`): deepest surface, used for code blocks, strong recesses, and boot mockup interiors. - **Background Card** (`{colors.background-card}`): standard card base. It is usually rendered at 20-60% opacity, not as a fully opaque slab. - **Background Card Alt** (`{colors.background-card-alt}`): dense table or comparison surface. - **Background Landing** (`{colors.background-landing}`): landing and session dashboard canvas. - **Background Get Started** (`{colors.background-get-started}`): auth and onboarding card shell. - **Background Primary / Secondary / Panel**: editor, FlexLayout, and panel system surfaces. Use these in shell/object-viewer chrome instead of inventing new dark grays. ### Text - **Foreground** (`{colors.foreground}`): primary text and selected shell labels. - **Foreground Alt** (`{colors.foreground-alt}`): secondary text. Use opacity modifiers heavily: `/70` for useful detail, `/50` for metadata, `/40` for empty state, `/30` for very low-emphasis icons. - **Text Primary / Secondary / Muted**: legacy and shell-facing text tokens still used in editor and FlexLayout surfaces. New app code should prefer `foreground` and `foreground-alt`. ### Semantic - **Success** (`{colors.success}`): confirmation, completed step, positive status. - **Warning** (`{colors.warning}`): caution, expiring or degraded state. - **Destructive** (`{colors.destructive}`): current app utility for destructive, invalid, and failed states. It aliases the error color in CSS; use low-opacity combinations such as `bg-destructive/5`, `border-destructive/20`, and `text-destructive` before reaching for solid fills. - **Error** (`{colors.error}`, `error-text`, `error-bg`, `error-border`): specialized error tokens for components that already own those aliases. New app UI should prefer destructive utilities unless matching an existing error-token component. ### Opacity Rules The design depends on opacity, not new colors. These are the common steps: | Use | Token Pattern | |---|---| | Subtle border | `border-foreground/6` | | Standard header divider | `border-foreground/8` | | Interactive border | `border-foreground/10` | | Hover border | `border-foreground/12` or `/15` | | Strong selected border | `border-brand/30` | | Standard card | `bg-background-card/30` | | Hover card | `bg-background-card/50` or `/60` | | Brand tint | `bg-brand/10`, hover `bg-brand/15` | | Muted text | `text-foreground-alt/50` | | Disabled or empty | `text-foreground-alt/40` | ## Typography ### Font Family Spacewave uses **Manrope Variable** as the default display and UI family. It is geometric enough to feel technical, but softer than a monospace-heavy developer tool. Use `font-display`, `font-heading`, or the default font stack for all ordinary interface text. Use **Pragmasevka** only for code, terminal output, object refs, hashes, diffs, numeric debug values, and other literal technical data. Do not use monospace as a brand decoration. ### Hierarchy | Token | Size | Weight | Line Height | Use | |---|---:|---:|---:|---| | `{typography.display-xl}` | 48px | 700 | 1.12 | Large landing title only | | `{typography.display-lg}` | 36px | 700 | 1.16 | Landing section title | | `{typography.page-title}` | 18px | 600 | 1.35 | Focused page title | | `{typography.figure}` | 24px | 500 | 1.25 | Figure strip values | | `{typography.panel-title}` | 14px | 600 | 1.4 | Panel header title | | `{typography.section-title}` | 12px | 500 | 1.4 | Section heading | | `{typography.body-md}` | 14px | 400 | 1.6 | Body copy and descriptions | | `{typography.body-sm}` | 12px | 400 | 1.5 | Dense UI body | | `{typography.control-sm}` | 12px | 500 | 1 | Buttons and compact controls | | `{typography.micro-cap}` | 10px | 600 | 1.4 | Step labels, badges, nav microcopy | | `{typography.mono}` | 12px | 400 | 1.5 | Code, refs, terminal, hashes | ### Principles - App panels should stay small. Use `text-sm` for panel titles, `text-xs` for section labels, and `text-[0.6rem]` for metadata. - Marketing and onboarding may use `text-3xl`, `text-4xl`, and `text-2xl`, but app chrome should not inherit hero-scale type. - Use uppercase only for micro labels, badges, and small nav items. Do not make normal panel headings uppercase. - Keep letter spacing at 0 for normal text. Use `tracking-widest` only on micro labels such as wizard steps and group labels. - Shell and FlexLayout tab labels are the chrome exception: cramped tab text may use slight negative tracking. Do not carry negative tracking into panels, cards, or form controls. - Use `select-none` on headings, labels, tabs, badges, and non-editable chrome. ## Layout ### App Shell The app shell is a full-height workbench: - Canvas: `{components.app-canvas}`. - Electron top bar: 28px when present. - Shell tab header: `--spacing-shell-header` 28px. - Shell tabs: 22px tall, compact, clipped to 100px max width, rounded only on top corners. - Split workspaces use FlexLayout. Active tabsets get stronger borders and HDR glow where available; inactive tabsets recede. - Nested ObjectViewer layouts must not inherit shell-level tab styles. ### Panels Detail panels follow a consistent structure: ```text +-----------------------------------------------+ | 36px header: icon, title, metadata, actions | +-----------------------------------------------+ | scrollable content: px-4 py-3 | | sections in priority order | | danger zone last | +-----------------------------------------------+ ``` Rules: - Header uses `h-9`, `border-b`, `border-foreground/8`, and `px-4`. - Title uses `text-sm font-semibold tracking-tight`. - Body uses `flex-1 overflow-auto px-4 py-3`. - Section groups use `space-y-3`. - Section heading uses `text-xs font-medium`, icon `h-3.5 w-3.5`, and `gap-1.5`. - Dashboards keep this compact header and its 14px title. A dashboard takes the structure of an Operate screen (one figure strip, then a table of the objects with right-aligned figures and state chips, no row icons) but not a page-scale title. - Do not show empty placeholder sections above sections with real content. ### Focused Pages Use focused centered pages for route-level account, billing, setup, or recovery flows that have one narrow task: - Root: `relative flex h-full w-full items-start justify-center pt-16`. - Content: `w-full max-w-md px-4`. - Page title row: icon `h-5 w-5`, title `text-lg font-semibold tracking-tight`, actions beside it. - Major groups: `space-y-6`, not a dense stack of unrelated cards. ### Landing And Onboarding Landing surfaces are allowed to be more spacious, but still must feel like Spacewave: - Root canvas: `{colors.background-landing}`. - Hero: animated logo, `[SPACEWAVE]` wordmark, bracket-like technical navigation, centered quickstart flow, and short local-first copy. - Sections: `px-4 py-20`, `max-w-5xl`, optional one-pixel separator. - Section labels: uppercase `text-xs font-semibold tracking-widest`. - Section titles: `text-3xl @lg:text-4xl font-semibold` or `font-bold`. - Cards: solid cards with `border-foreground/6 bg-background-card/30` and no backdrop blur. - Scroll reveal: fade + translate from `translate-y-8 opacity-0` to visible. ## Elevation And Depth Spacewave uses low-opacity fills, borders, and subtle motion more than shadows. Backdrop blur marks a surface that floats over other content; a card in the page flow never takes it, because nothing moves behind it. | Level | Treatment | Use | |---|---|---| | 0 | Flat near-black surface | App canvas, editor interiors | | 1 | `bg-background-card/30`, `border-foreground/6` | Info cards, wizards, object panels | | 2 | Level 1 with a hover border and lift | Action cards, landing cards, clickable rows | | 2F | Glass with `backdrop-blur-sm` over content | Popovers, floating windows, toolbars, pills over media | | 3 | Menu shadow plus opaque card | Menus, dropdowns, toasts, overlays | | 4 | Shine border / animated logo glow | Boot and brand moments only | Avoid heavy drop shadows for ordinary cards. Use `shadow-menu` for menus and toasts, and use subtle `shadow-lg` only for modal or login-style overlays. ### Motion - Buttons and cards: `transition-all duration-150` for app UI. - Landing cards and reveal: `duration-300` to `duration-700`. - Hover lift is small: `hover:-translate-y-0.5` or `-translate-y-[1px]`. - Loading shine border and animated logo should stop or reduce when reduced motion is active. - Do not use looping motion to attract attention in ordinary panels. ## Shapes | Token | Value | Use | |---|---:|---| | `{rounded.menu-button}` | 3px | Dense dashboard buttons | | `{rounded.xs}` | 4px | Compact inputs and small inline surfaces | | `{rounded.editor}` | 6px | Shell tabs and editor panel corners | | `{rounded.sm}` | 8px | Standard cards | | `{rounded.md}` | 12px | Wizard shell and route cards | | `{rounded.lg}` | 16px | Logo-adjacent large shells | | `{rounded.xl}` | 20px | Animated logo image corners | | `{rounded.pill}` | 9999px | Badges, pills, progress chips | Do not round dense app controls into large pills. Pills are for badges, status chips, floating bars, and compact overlays. ## Components ### App Canvas **`app-canvas`** is the default full-height app surface. - Background `{colors.background}`. - Text `{colors.foreground}`. - No outer padding. - Child surfaces own their spacing and scrolling. ### Landing Canvas **`landing-canvas`** is used for public, onboarding, and empty-session surfaces. - Background `{colors.background-landing}`. - Allows centered logo/quickstart composition. - May use larger cards and reveal motion. - Must not switch to a light marketing theme. ### Info Card **`info-card`** is the standard static content card. - `border-foreground/6 bg-background-card/30 rounded-lg border p-3.5`. - Use for identifiers, settings summaries, status groups, and non-interactive details. - Do not wrap a component that already renders its own bordered card. ### Action Card **`action-card`** is a clickable row/card with an icon box, label, and description. - Default: `border-foreground/6 bg-background-card/30`. - Hover: `hover:border-foreground/12 hover:bg-background-card/50`. - Icon box usually `bg-foreground/5` or `bg-brand/10`. - Destructive variant uses low-opacity error/destructive border and background. ### Figure Strip **`figure-strip`** shows a few figures side by side in one panel (`web/ui/FigureStrip.tsx`). - One `border-foreground/6 bg-background-card/30 rounded-lg border` panel, figures divided by `divide-foreground/6` hairlines, each `px-3.5 py-3`. - Label: `micro-ten font-semibold tracking-widest uppercase text-foreground-alt`. - Value: `{typography.figure}` with `tabular-nums`; a unit such as "of 4" follows in `text-sm text-foreground-alt`. - An optional line of context under the value in `text-xs`. Leave it out rather than invent one. - No icon boxes, no growth pills, no hover state. A screen has one strip; more figures widen it rather than adding a second. ### Dashboard Button **`dashboard-button`** is dense panel chrome. - `h-7 px-2 rounded-menu-button`. - Border `border-foreground/8`; transparent background. - Hover `bg-foreground/5 border-foreground/15 text-foreground`. - Text `text-xs`; icons `h-3.5 w-3.5` or `h-4 w-4`. - Wrap action buttons in tooltips when icon-only or when the action is not self-evident. ### Primary Action **`primary-action`** is the app's brand-tinted confirm button. - Border `border-brand/30`. - Background `bg-brand/10`. - Hover `border-brand/50 bg-brand/15`. - Text `text-foreground text-xs`. - Height usually `h-7` in wizards and `h-9` on focused pages. - Do not use a solid brand fill for ordinary app actions. - `web/ui/Button.tsx` is a shadcn-compatible low-level primitive. App surfaces should pass this class set, or use a wrapper/owner component that does, when they need the polished `primary-action` pattern. ### Text Input **`text-input`** is the polished input style for forms and wizards. - `border-foreground/10 bg-background/20 text-foreground`. - Placeholder `text-foreground-alt/40`. - Height `h-9`. - Focus `focus-visible:border-brand/50 focus-visible:ring-brand/15`. - Use `font-mono` only when the input is a token, URL, hash, path, or object reference. - `web/ui/Input.tsx` is a shadcn-compatible low-level primitive. App-owned forms should apply this class set or use a surface-specific wrapper for the polished `text-input` pattern. ### Radio Option **`radio-option`** is for two-to-four mutually exclusive choices. - Base `rounded-md border p-2.5 text-left`. - Unselected `border-foreground/10 bg-background/20`. - Hover `hover:border-foreground/20`. - Selected `border-brand/30 bg-brand/5`. - Use the built-in radio dot and optional icon/tag. Do not use `ring-primary` for selection. ### Wizard Shell **`wizard-shell`** is the persistent object-creation shell. - Outer: centered, `px-4 py-10`, scrollable. - Card: `max-w-lg rounded-xl border-foreground/6 bg-background-card/30`. - Header: `h-9 border-b border-foreground/8 px-4`. - Step label: `text-foreground-alt/50 text-[0.6rem] font-medium tracking-widest uppercase`. - Body: `px-4 py-3 space-y-3`. - Footer: `border-t border-foreground/8 px-4 py-3`. - Long-running work becomes a progress step with watched status, not a stuck button. ### Loading Screen **`loading-screen`** owns route-level and boot loading. - A compact planet emblem with a slow orbit sits above the heading on the application background. Route startup, embedded workspaces, and Space mounting share this composition. - Shared critical CSS keeps initial HTML and React handoffs styled before the application stylesheet arrives. - A centered heading and the operation's actual phase indicators stay inside the available panel. Download percentages describe measured transfers, never readiness milestones. - Errors and compact Retry and Back actions remain visible. - The composition adapts to narrow and short viewports. Reduced motion freezes the CSS orbit and emblem pulse. Loading surfaces use ordinary HTML and CSS throughout startup. ### Loading Card **`loading-card`** owns bounded panel/object loading. - Uses the shared `LoadingView` contract: `loading`, `active`, `synced`, or `error`. - Optional progress, rate, activity, retry, and cancel. - Domain wrappers adapt watches/resources to `LoadingView`; primitives stay presentational. - Use render delay for list, grid, and chat loads so sub-300ms waits do not flash loading UI. ### Shell Tab **`shell-tab`** is compact top workbench chrome. - Height 22px in shell layout; 20px in the top-bar-style strip. - Max width 100px, ellipsized text, `font-size: 11px`. - FlexLayout shell tabs use `letter-spacing: -0.01em`; the legacy top-bar-style strip uses `-0.025em`. - Top corners only, no underline pseudo-element. - Inactive tabs are translucent and blurred; selected tabs are opaque and brighter. - Active grid tabsets may receive stronger border or HDR glow. ### Tooltip **`tooltip`** is black glass popover chrome. - Background `bg-black/90` or `{colors.tooltip-bg}`. - Border `border-foreground/20`. - Text white / foreground. - Use for icon-only actions, technical labels, truncated refs, and row explanations. ## Iconography Use `react-icons/lu` Lucide icons for UI controls and sections. Keep icon sizes stable: | Size | Use | |---|---| | `h-2.5 w-2.5` | Tiny badges | | `h-3 w-3` | Secondary row glyphs | | `h-3.5 w-3.5` | Section headings and dense buttons | | `h-4 w-4` | Panel header actions and primary row icons | | `h-5 w-5` | Focused page leading icons | Icon containers: - `size-5 bg-foreground/5` for secondary rows. - `size-7 bg-foreground/5` for compact primary rows. - `size-8 bg-brand/10` for primary actions and status rows. Do not use standalone decorative icons as filler. An icon should clarify a section, action, status, or data type. ## Responsive Behavior Spacewave uses container queries and viewport-height variants heavily. ### Breakpoints And Variants | Variant | Trigger | Use | |---|---|---| | `narrow` | max-width 640px | Collapse dense landing/onboarding surfaces | | `wide` | min-width 1920px | Give large displays more room | | `ultrawide` | min-width 2560px | Avoid over-stretched content | | `tall` | min-height 800px | Center landing hero vertically | | `short` | max-height 580px | Hide or compress footer adornments | | `very-short` | max-height 470px | Hide logo when necessary | | `ultra-short` | max-height 390px | Hide hero wordmark when necessary | | `auth-short` | container max-height 740px | Compress auth/onboarding layout | | `auth-very-short` | container max-height 580px | Top-align compact auth content | | `auth-short-narrow` | container max-height 740px and max-width 950px | Hide secondary auth intro content | | `@xs` | container min-width 20rem | Fine-grained component scale-up | | `@sm` | container min-width 24rem | Fine-grained component scale-up | | `@md` | container min-width 28rem | Fine-grained component scale-up | | `@lg` | container min-width 32rem | Landing and component scale-up | | `@xl` | container min-width 36rem | Fine-grained component scale-up | | `@2xl` | container min-width 42rem | Larger landing spacing | | `@3xl` | container min-width 48rem | Fine-grained component scale-up | | `@4xl` | container min-width 56rem | Fine-grained component scale-up | | `@5xl` | container min-width 64rem | Fine-grained component scale-up | ### Rules - App panels must work inside split panes; design for container width, not only viewport width. - Stable chrome dimensions prevent layout shift: fixed tab heights, fixed header heights, fixed icon boxes, fixed progress widths. - Landing hides or compresses decorative content before controls overlap. - If a surface is too narrow to be usable, show a minimal width gate rather than allowing broken controls. - Text inside buttons and tabs must truncate or wrap intentionally. Do not let labels overflow their containers. ## Do's And Don'ts ### Do - Use the existing OKLCH theme tokens in `web/style/app.css`. - Use opacity modifiers before creating new color tokens. - Use `InfoCard`, `DashboardButton`, `RadioOption`, loading primitives, and wizard shell patterns before creating a local one-off. - Keep app chrome compact, with panel headers at `h-9` and controls at `h-7` or `h-9`. - Put identifiers, object refs, hashes, paths, and code in Pragmasevka mono. - Put destructive actions last, with low-opacity destructive tint and clear copy. - Use watched or streaming state for mutable backend progress and loading. - Respect reduced motion for animated logo, shine borders, and loading effects. ### Don't - Do not make app panels look like landing-page hero sections. - Do not introduce a second accent palette for a local feature. - Do not use light cards or white canvases for normal Spacewave app surfaces. - Do not use `bg-muted`, `ring-primary`, deprecated `text-text-*` tokens, or local gray ramps in new UI. - Do not stack cards inside cards unless the inner element is a real repeated item, modal, or bounded tool. - Do not center large empty states in dense panels; use compact muted rows. - Do not use polling loops for progress or status just to drive UI. - Do not persist wizard state on every keystroke. - Do not hide long-running setup behind one blocking action with no progress. ## Iteration Guide 1. Start with the surface owner: shell, panel, focused page, wizard, loading, landing, or object viewer. 2. Use the component token names directly: `{colors.background-card}`, `{colors.brand}`, `{components.info-card}`, `{components.dashboard-button}`. 3. Preserve density in app surfaces. Use larger spacing only on landing, onboarding, and route-level focused flows. 4. Choose one interaction state per change: default, hover, selected, loading, empty, error, disabled. 5. For mutable state, wire a watched/streaming state owner first, then render with the existing loading or status primitives. 6. Verify app code changes with `bun run tsrs --noEmit` and focused tests for the changed surface. 7. When editing this design document, keep it current-state only. Remove stale compatibility notes rather than documenting old and new systems side by side. ## References - `web/style/app.css`: Tailwind v4 theme variables, fonts, HDR/P3 variants, shell styles, scrollbars, and hero button class. - `web/ui/Button.tsx` and `web/ui/Input.tsx`: shadcn-compatible low-level primitives; polished app action/input patterns are applied through wrappers or source-owned class overrides. - `web/ui/InfoCard.tsx`: standard static card. - `web/ui/DashboardButton.tsx`: compact panel action button. - `web/ui/RadioOption.tsx`: selected/unselected choice row. - `web/ui/FigureStrip.tsx`: figure strip for a screen's figures. - `web/ui/loading/*`: `LoadingView`, `Spinner`, `ProgressBar`, `LoadingInline`, `LoadingCard`, and `LoadingScreen`. - `app/wizard/WizardShell.tsx`: persistent wizard shell. - `app/auth/AuthScreenLayout.tsx`: auth container-query variants. - `app/session/dashboard/SessionDashboard.tsx`: empty-session dashboard and command-palette onboarding surface. - `app/landing/*`: landing page hero, section, CTA, reveal, legal, and community surfaces. - `app/ShellFlexLayout.tsx` and `web/style/flexlayout/*`: workbench tab and split-pane behavior.