DESIGN.md
Rama master · réplica de lectura del repositorio original.
---
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.