# 09 · Visual Design Language > **Status:** foundational design pass. This is the _visual_ contract — the > counterpart to `docs/spec/` (behavior) and the rest of `docs/architecture/` > (structure). `styles/tokens.css`, `styles/base.css`, component CSS Modules, and > `src/core/vega-themes.ts` implement _to this doc_. > > **Companion:** [`visual-specimen.html`](./visual-specimen.html) — a standalone, > openable "kitchen sink" that renders every token and element with a live > theme/accent switcher. Edit tokens there first, eyeball them, then port the > settled values into `styles/tokens.css`. Astrolabe's look is **inspired by the IBM Design Language / Carbon**, but Carbon is **not a dependency** — we transcribe the values we want and reinterpret the principles in our own words. We borrow IBM's _engineered structure_; we keep _color and theming free_. --- ## 1. Principles IBM's four design principles map almost exactly onto Astrolabe's SOUL ("the spec is the star; the UI is a thin, considered shell"). Restated for us: 1. **Considered** — _remove everything gratuitous._ No decoration that isn't carrying meaning. Whitespace is a feature. 2. **Unified** — a _small fixed kit_ (one type family, a neutral ramp, one accent, a handful of components) reused systematically. Identity comes from consistency, not novelty per screen. 3. **Executed** — _everything communicates, including what we leave out._ Alignment, rhythm, and empty space are decisions, not leftovers. 4. **Progressive** — _every element reduces friction._ If it doesn't help the user read, edit, or find a snippet faster, it doesn't earn its place. …plus our own, where we part ways with IBM: 5. **Structure is rigorous; color is free.** The grid, type scale, spacing, and square geometry are systematic and fixed. Color, accent, and theming are the _expressive_ layer — open, swappable, and meant to be played with. --- ## 2. Deliberate divergences from Carbon What we **borrow** vs. where we **diverge** — recorded so future readers know these were choices, not drift: | Topic | IBM/Carbon | Astrolabe | | ------------------------------- | --------------------------------------------------- | ----------------------------------------------------------------------------------------- | | Adoption | A framework + component lib | **Inspiration only.** Transcribed tokens, our own components | | Structure (grid, type, spacing) | 8px mini unit, modular type scale | **Borrowed wholesale** — it's the rigorous part worth having | | UI chrome corners | ~0–2px (near-square) | **Fully square, `radius: 0`** — one notch more austere/engineered | | Icons | Rounded exteriors, 2px soft corners + 90° interiors | **Kept rounded** (use Carbon's icon set) — the one warm, human touch | | Color | "Blue at the core"; other hues only for purpose | **Dropped.** Color/theming is free and expressive; accent is a token, many themes welcome | | Neutrals | Carbon gray ramp | **Borrowed** — accessible, well-tuned, a good legible base | | Motion | Productive vs. expressive | **Productive only** — subtle, purposeful, reduced-motion-aware | --- ## 3. Tokens All tokens are CSS custom properties on `:root`, themed by overriding them on `[data-theme]` (and, for accent, `[data-accent]`). As of M1.5 the settled values live in `styles/tokens.css`; the specimen remains the sandbox for trying new tokens/themes before porting them across. ### 3.1 Typography — IBM Plex - **Families:** `IBM Plex Sans` for UI, `IBM Plex Mono` for the editor, code, numeric/tabular data, and inline spec fragments. Self-hosted in production via `@fontsource/ibm-plex-sans` + `@fontsource/ibm-plex-mono` (offline/PWA — never a CDN). The specimen uses a CDN purely for preview convenience. - **Scale (px), from Carbon's modular scale:** `12 · 14 · 16 · 18 · 20 · 24 · 28 · 32 · 42`. Body is **14/20** (already our `--font-size-base`). Captions/labels 12. - **Weights:** 400 regular, 600 semibold for emphasis/headings; 300 light reserved for large display only. - **Breathing room:** Plex _"requires space to breathe."_ Don't over-tighten — body line-height ≥ 1.4, default tracking (no negative letter-spacing on text). Flush-left, clear hierarchy. ### 3.2 Spacing — the 8px base unit IBM's product/web rule: _"the 8px mini unit guides everything."_ Every gap, pad, and size is a relationship of 8 (with 2/4 as fine sub-steps): `--space-1: 2px · --space-2: 4px · --space-3: 8px · --space-4: 12px · --space-5: 16px · --space-6: 24px · --space-7: 32px · --space-8: 48px · --space-9: 64px`. > Note: this renumbers our current M0 scale to anchor on 8. The migration is > mechanical (search/replace `--space-*` usages) and lands with the design pass. ### 3.3 Color — role-based, theme-free Color is expressed as **roles**, never raw hexes, so themes can repaint the whole UI by swapping one set of values. Borrowed from Carbon's layering model: | Role token | Meaning | | ---------------------------------------------------- | ----------------------------------------------------------------------------------- | | `--bg` | App canvas (lowest layer) | | `--layer-01` / `--layer-02` | Raised surfaces (panels, cards, popovers) — elevation by lightness step, not shadow | | `--border` / `--border-strong` | Subtle and prominent separators | | `--text` / `--text-secondary` / `--text-placeholder` | Text hierarchy | | `--accent` / `--accent-hover` / `--accent-contrast` | The expressive accent — **swappable**; UI must never hardcode a hue | | `--focus` | Focus-ring color (defaults to `--accent`) | | `--support-error / -success / -warning / -info` | Status only — color = meaning | - **Neutrals** use the Carbon gray ramp (`#f4f4f4 … #161616`) — accessible and legible. **Accent and theming are open**: the specimen ships several accents (indigo, teal, amber, rose) and light/dark themes to prove the system is free, not blue-bound. Pick, add, or invent themes freely. - **Status palette** (borrowed, stable): error `#da1e28`, success `#198038`, warning `#f1c21b`, info `#0043ce` — tuned per theme for contrast. - **Contrast:** target WCAG AA (4.5:1 text, 3:1 large/UI). Accent-on-`--bg` and text-on-`--accent` must both pass for any shipped theme. ### 3.4 Shape & elevation - `--radius: 0` for all chrome (buttons, fields, cards, panels). Square is the look. - **Icons are exempt** — they keep their rounded geometry (Carbon icon set, 2px corners). Icons are SVG, not chrome, so `--radius` doesn't touch them. - **Elevation is lightness, not shadow.** Stack `--bg → --layer-01 → --layer-02`. Shadows, if ever used, are minimal and reserved for true overlays (modals, popovers). - **Borders are 1px**, `--border` subtle by default. ### 3.5 Motion - **Durations (productive):** `--dur-fast: 70ms`, `--dur-fast-2: 110ms`, `--dur-moderate: 150ms`. Nothing slower in the core UI. - **Easing:** standard productive `cubic-bezier(0.2, 0, 0.38, 0.9)`. - **Restraint:** animate only what's vital (state changes, entrances of meaningful elements). No gratuitous motion. All transitions are already neutralized under `@media (prefers-reduced-motion: reduce)` in `base.css`. --- ## 4. Component conventions - **Buttons:** square, 32px (compact) / 40px (default) tall. Variants: **primary** (filled `--accent`), **secondary** (bordered), **ghost** (text-only), **danger** (filled `--support-error`). 600-weight label. Clear hover/active and a visible focus ring. - **Focus ring:** a 2px `--focus` outline (offset 1–2px). Always visible on keyboard focus — accessibility is non-negotiable (principle 4). - **Fields** (text, textarea, select, search): square, 1px `--border`, `--layer-01` fill, accent border + focus ring on focus. Mono font for spec/JSON inputs. - **List rows** (snippet library): compact, full-row hover (`--layer-01`), active row marked by an accent left-border + `--layer-01` fill, secondary metadata in `--text-secondary`. Row-level actions reveal on hover. - **Status indicators:** a small dot/tag for draft vs. published; a dataset glyph when references exist. Status colors only. - **Toasts:** `--layer-02`, 1px border in the support color, square, brief. - **Code / editor surfaces:** `--font-mono`, `--layer-01`, generous line-height. --- ## 5. Charts (`src/core/vega-themes.ts`) The chart `Config` is themed to match the app, per theme: - `background: transparent` (inherits the surface), Plex font for titles/labels, axis/grid colors derived from the neutral ramp + `--text-secondary`. - **Categorical palette** for `range.category` is part of the _free color_ layer — a distinct, colorblind-sequenced set (Carbon's data-viz palette is a good starting point, but not mandatory). Light and dark variants. This is where expressive color earns its keep. - Config is applied **at embed time**, never baked into the user's stored spec. --- ## 6. Implementation map | Artifact | Role | | ------------------------------------------------ | ----------------------------------------------------- | | [`visual-specimen.html`](./visual-specimen.html) | Living preview + token sandbox. Iterate here first | | `styles/tokens.css` | The settled tokens — ported from the specimen in M1.5 | | `styles/base.css` | Font wiring (`@fontsource`), reset, reduced-motion | | component `*.module.css` | Consume tokens only; no raw hexes, no hardcoded hue | | `src/core/vega-themes.ts` | Chart `Config` per theme; categorical palettes | **Order of work:** settle the specimen → port tokens to `tokens.css` → self-host Plex in `base.css` → restyle existing M1 components against the tokens → align `vega-themes.ts`. Verify by rendering the real app, not just the specimen. --- ## 7. Inspiration sources — where to look for more We treat IBM/Carbon as inspiration, so we mine its **source repos**, not the live doc sites. The sites (`carbondesignsystem.com`, `ibm.com/design/language`) are JS-rendered and don't fetch cleanly — **clone the repo and read it locally instead.** Convention: clone under `/Users/oleh/code/reference/` with `git clone --depth 1 https://github.com/carbon-design-system/.git`. | Need | Repo | Where it lives | | -------------------------------------------------------------------------------------------------------------- | ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Principles / the "why"** (philosophy, 2x grid, color rationale, type, motion, icon geometry) | `design-language-website` | `src/pages/`: `philosophy/principles.mdx`, `2x-grid.mdx`, `color.mdx`, `typography/*.mdx`, `animation/overview.mdx`, `iconography/ui-icons/design.mdx` (~1.4 GB clone — image-heavy; the MDX is what we want) | | **Token values** (gray/blue ramps, type scale, font families, motion durations/easings, theme role→value maps) | `carbon` | `packages/colors/src/colors.ts`, `packages/type/src/{scale,fontFamily,fontWeight}.ts`, `packages/motion/src/index.ts`, `packages/themes/src/{white,g100}.ts` | | **Component-level usage guidance** | `carbon-website` | `src/pages/**/*.mdx` | | **Data-viz categorical chart palette** (for `vega-themes.ts` `range.category`) | `carbon-charts` | cloned in M1.5 → `packages/core/scss/_color-palette.scss` (the `'14'` pairing, white + g100); token→hex resolved against `carbon` `packages/colors/src/colors.ts` | > The decisions we made _from_ these sources are captured above (§1–6) and in the > specimen, so we don't need to re-derive them — only return to the repos to extend > the research (e.g. the chart palette, or a component pattern we haven't tackled).