# Samson AI Lab Design System

A dark-first design system for a developer infrastructure product, with a full light mode. It covers two surfaces: a **marketing site** and a **product surface** (the runs dashboard and its sibling views). Every value here is a token — build against token names, not raw hex.

## Source material

One source was provided, and it is the ground truth for everything in this project:

- `uploads/Samson-AI-Lab-Design-System-Trigger-dev.md` — a complete written design system, v2, described in the document as corrected against pixel measurement of rendered screens. It defines the two-layer color model, both surface stacks, the categorical accent key, the diagram grammar, the type scale, spacing/radii/motion, an inventory of 19 component families (§7), the canonical page composition (§8), voice and sample copy (§9), and accessibility rules (§10).

No codebase, Figma file, screenshots, logo files, or partner logos were provided (the Geist font binaries were supplied separately and are self-hosted). Where the document specifies an asset that was not supplied, this system leaves the slot empty and says so rather than inventing one. See **Substitutions and gaps** at the end.

## Product context

The product is a platform for writing durable background tasks in TypeScript: retries, queues, scheduling, human-approval waitpoints, streaming, and full run history, with no request timeouts. It is open source and self-hostable. Two audiences read the same visual language:

- **Marketing site** — argues the case in the product's own language. Real code blocks and schematic diagrams do the work that illustration would do elsewhere.
- **Product surface** — dense, mono-heavy operational views: run tables, run detail with a timeline and log wells, tasks, queues.

The two layers share tokens, type and components but **stack their surfaces in opposite directions** (below). That is the single most important thing to get right.

---

## Content fundamentals

**Second person, active, present tense.** "Write tasks in regular code," never "Tasks can be written." The reader is the one doing the work; the product is the thing they use.

**Concrete over aspirational.** Name the mechanism, not the feeling. "Retries with exponential backoff" beats "rock-solid reliability."

**Sentence case everywhere** — headings, buttons, labels, nav items, table headers. The only uppercase is `font-mono` micro-labels: column headers, eyebrow category labels, diagram node labels, the `NEW` announcement tag. Those are uppercase *and* wide-tracked *and* mono, all three together.

**No exclamation marks.** Banned vocabulary: seamlessly, effortlessly, unlock, supercharge, revolutionize. No em-dash-heavy rhetorical flourishes and no "this, not that" constructions.

**Numbers where you have them.** A specific count beats a paragraph of adjectives: `16.1k` GitHub stars, `1,284` merged pull requests, `500` runs per batch call, `Last 24 hours`.

**Buttons name the outcome.** `Start building`, `Read the docs`, `View example`, `Replay`, `Export`. Never `Submit`, `Click here`, or `Learn more about our solution`.

**Errors state what happened and the next action**, in the interface's voice, without apologising: `TimeoutError: model.score exceeded 30s` followed by `retrying in 4s (attempt 2 of 3)`. No "Oops."

**Empty states are invitations** — name the thing that would appear here and give the button that creates it.

**Every code sample opens with an explanatory comment line.** This is the teaching device: the first line states what the code proves, before any import.

```typescript
// A task that retries automatically and survives redeploys
import { task } from "@samson/sdk";
```

**No emoji.** None appear in the source document, and none are used anywhere in this system — not in copy, not in cards, not as icon substitutes.

**Voice examples, verbatim from the source:**

- Hero: "Build and run long-lived AI workflows without managing servers" / "Samson AI Lab is a platform for writing durable background tasks in TypeScript. Retries, queues, scheduling, and full run history, with no timeouts."
- Section header: "Build resilient AI apps" / "Move long-running work off your server. Add human approval steps and stream results straight to your interface."
- Stat: "No timeouts — Write ordinary code and let it run as long as it needs."
- Closing CTA: "Ready to build?" / "Deploy your first task in a few minutes. No infrastructure to set up."

**Placeholder policy.** All company names, people, quotes and metrics in the source document — and therefore in the UI kits here — are illustrative. Replace them with real attributable content before publishing. Never ship invented social proof.

---

## Visual foundations

### Color model

Two layers. **Layer 1** is the raw palette (`--color-ink-*`, `--color-lime-*`, `--color-lavender-*`, `--color-mint-*`, `--color-sun-*`) and never changes between themes. **Layer 2** is semantic (`--color-background-bright`, `--color-text-dimmed`) and is the only thing a theme overrides — which is why light mode is a ~60-line diff rather than a rewrite. Components reference Layer 2 only.

Three families do three jobs: **neutrals carry structure** (~90% of every screen), **lime carries action** (one primary per viewport, always with `ink-900` text, in both themes), and a **fixed categorical set carries identity**. Missing the third family is the most common way to get this system wrong — it looks monochrome in a static spec and is genuinely polychrome in use.

### The two surface stacks

| Layer | Marketing | Product |
|---|---|---|
| Page | `background-deep` `#121317` | `background-dimmed` `#15171a` |
| Card / panel | `background-dimmed` `#15171a` | `background-bright` `#1a1b1f` |
| Well inside it | `background-deep` `#121317` | `background-deep` `#121317` |
| Hover | `background-hover` `#212327` | `background-hover` `#212327` |
| Borders | `grid-hairline` `#1c1e21` | `grid-bright` `#272a2e` |

Marketing: the page is the darkest thing and cards rise out of it. Product: the page sits one step up and wells drop below it. Pick a stack by context and stay in it for the whole surface.

**In light mode the two stacks collapse into one**, following the shadcn convention: the **page is pure `#ffffff`**, and every raised surface — cards, panels, popovers, inputs, outline buttons, the nav dropdown — is also `#ffffff`. Nothing rises by getting lighter; separation is the `#e4e4e7` border plus `--shadow-card`. A single muted grey does the remaining work: `#f8f8f8` for **wells** (code blocks, diagram wells, terminals, which must read as inset rather than floating) and `#f5f5f5` for **hover** and the `secondary` control fill. Text runs `#09090b` display → `#18181b` bright → `#52525b` body → `#71717a` faint. Neutrals are a zinc ramp with no warmth, so lime and lavender stay clean on white.

| Layer | Light |
|---|---|
| Page, card, panel, input, outline control | `background-deep`, `background-dimmed`, `background-bright`, `input-bg`, `surface-outline` — all `#ffffff` |
| Well (code, diagram, terminal) | `code-bg`, `diagram-well-bg` `#f8f8f8` |
| Hover / secondary fill | `background-hover` `#ebebed`, `surface-control` `#f5f5f5` → `#e4e4e7` |
| Border | `grid-bright` / `border-bright` `#e4e4e7` |
| Lift | `--shadow-card` `0 1px 2px rgba(9,9,11,0.05)` |

### Categorical key

Fifteen permanently assigned hues (amber = agents, blue = tasks/IO, purple = queues, indigo = runs, teal = webhooks, and so on — see `tokens/categorical.css`). Assignment is permanent across sidebar icons, chip icons, chart series, status dots and diagram nodes: a reader learns the key once. Never rotate hues for variety; two adjacent cards sharing a hue is correct if they share a concept. Categorical color appears **at small sizes only** — a 16–20px icon, a 2px indicator, a chart stroke, a 1px node border. Never a large fill, never prose text. Warm hues (yellow, amber, green, emerald, orange) step down in light mode; cool hues already clear 3:1 and stay put.

### Type

**Geist Variable** and **Geist Mono Variable**, two families, no third. Weights 400 / 500 / 600 only. Mono appears wherever content is literally code, an identifier, a version, a metric or a node label — mono in a heading is an exception that must be justified.

**Negative tracking scales with size** and is the most important typographic rule here: `-0.03em` at 7xl down to zero at `base` and below. Four brightness levels with one of each per visual cluster: `text-display` (hero headline only), `text-bright` (headings, card titles, quotes), `text-dimmed` (body, most links), `text-faint` (captions, metadata). The hero being one step brighter than every other heading is most of why the hero reads as the hero.

**Section header pattern:** heading + body in a 440px column at the left, contextual link right-aligned on the same block and baseline-aligned to the last line of body copy. That asymmetry is a recurring signature. Headings never exceed two lines at desktop — constrain the copy rather than shrinking the type.

### Spacing and layout

4px base unit. Tight interiors, wide gutters: `gap-4` dense, `gap-6` feature cards, `gap-8` splits, `gap-16` alternating rows. Page container 1280px with `px-6`/`px-8`; reading column 768px; hero 960px; section text column 440px. Vertical rhythm `py-16` / `py-24` / `py-32` by breakpoint, heading block to content `mb-12`.

**The grid is drawn, not implied.** 1px vertical rails in `grid-hairline` run down both container edges through the full height of every section, with page color continuing in the gutters, and horizontal hairlines close each section. Rails run *behind* cards and carousels and terminate at section boundaries, not content boundaries. They are hidden below `md`. Rails are subtler than card borders on purpose — using card-weight borders for structure makes the page look caged.

### Backgrounds and texture

No decorative imagery, no stock photography, no hand-drawn illustration, no full-bleed photographic backgrounds. Three textures only:

- **Hero backdrop** — a heavily blurred, darkened *real product screenshot* at low opacity (`blur(8px)`, brightness ~35%), with a vertical gradient to the page color at top and bottom. Real product surface behind the headline, not competing with it. (No screenshots were supplied, so the kits render live product surface into this slot instead.)
- **Hero glow** — radial `lime-500` at 8% opacity, `blur(120px)`, behind and below the headline. In light mode it becomes a flat `lime-100` wash; glows read as smudges on white.
- **Grid pattern** — 1px lines at 32px intervals in `grid-hairline`, radially masked, behind CTA blocks only.

Gradients appear in exactly two places: the hero glow and the (unsupplied) wordmark glyph. There are no purple-to-blue background gradients anywhere in this system.

### Corners, borders, elevation

`3px` on controls, inputs and chips (the default), `4px` on diagram nodes and code blocks, `6px` on cards and panels, `8px` max, `9999px` for the announcement pill and avatars only. The system reads as technical because it is not rounded.

**All borders are 1px.** There are no 2px borders — the single exception is the 2px lime indicator on an active vertical tab, which is an *indicator*, not a border. Weight is expressed by color, along the ladder `grid-hairline` → `grid-dimmed` → `grid-bright` → `border-bright` → `border-brighter` → `border-brightest`. Dashed borders mark empty states, drop targets and "add new" affordances, never decoration.

**No drop shadows on dark** — elevation is surface color alone. In light mode a white card on the tinted page gets `--shadow-card` (`0 1px 2px rgba(9,9,11,0.05)`) on top of its hairline: 1px of lift, enough to separate the card from the page without reading as a floating panel. The token is `none` on dark, so the same component is correct in both themes. `--shadow-panel` covers popovers and dropdowns, `--shadow-modal` dialogs. Text over imagery or colored bars gets `1px 1px 1px rgba(0,0,0,0.5)`.

### Cards

Marketing card: `background-dimmed` fill, `grid-hairline` border, `radius-md`, `overflow-hidden`, `p-6` body — which resolves to a tinted-grey card on dark and a **pure white** card on light. A pattern card is a fixed ~390px width with a 185px media well on top that drops to page color and is closed by a hairline. A media feature card crops its media against the card edge so the interface implies continuation. Extension cards are `p-5` with uniform row heights.

### Motion

150ms `ease` is the house default and it applies to `color`, `background-color`, `border-color` and `opacity` — never `all`. 200ms `ease-out` for disclosure, 300ms `ease-out` for carousel scroll, 200ms for layout shift, 400ms `cubic-bezier(0.16, 1, 0.3, 1)` for a page-level reveal. No bounces, no springs, no scale-on-hover, no entrance animations on scroll. Motion confirms; it doesn't perform.

Only two ambient animations exist: the logo marquee (two rows, opposite directions, ~40s linear, paused on hover) and the hero glow ring (conic gradient over 3s at 10% opacity). Both are disabled under `prefers-reduced-motion`, along with carousel smooth-scroll and any autoplaying video; functional transitions stay.

### Hover, press and focus states

- **Surfaces** step up one level (`background-dimmed` → `surface-hover-subtle`).
- **Cards** brighten their border one step (`border-hover`) and shift their trailing arrow 2px. Cards never scale and never lift on dark; in light mode they do gain `--shadow-card-hover` (`0 2px 6px`), because a hairline change alone is invisible on white. Light hover greys are one step stronger than the resting muted grey: `#ebebed` fills, `#c9c9ce` hover borders.
- **Text links** move `text-link` → `text-link-hover` with no underline by default; a `data-underline-links="true"` root preference underlines body links at a 2px offset.
- **Icon buttons** fill the icon's bounding box with `background-hover`.
- **Categorical icons** go to full saturation on row hover, resting at `opacity-90`.
- **Buttons**: primary `lime-500` → `lime-400`; secondary `surface-control` → `surface-control-hover`; tertiary/minimal gain `background-hover` and their text lifts to bright; danger darkens 8%. There is no separate press treatment — no shrink, no inset shadow. Controls step through the `surface-control` → `-hover` → `-active` ladder instead.
- **Disabled** is `opacity: .75` with `pointer-events: none`, never a different fill.
- **Focus** is `1px solid var(--color-text-link)` with `outline-offset: -1px` (inset, so it isn't clipped inside carousels and scroll containers) and a 5px radius. Never `outline: none` without a replacement.

### Transparency and blur

Used in exactly two places: the scrolled nav bar (`background-deep` at 80% with `backdrop-blur-md`) and the hero backdrop's blurred screenshot. Nothing else is translucent — no frosted cards, no glass panels. Protection for text over media is a **gradient** (the hero's vertical fade to page color, the testimonial masonry's fade-out clamp), never a capsule behind the text.

### Scrollbars

Two treatments, and the difference is functional. **Content scrollbars** are 8px with a thumb that fades in only on hover of the scroll area, inset via a 2px transparent border and `background-clip: padding-box`. **Carousel scrollbars are always visible** because they double as a progress indicator: 6px, page-color track, `background-raised` thumb, full container width.

### Imagery

Product screenshots only, shipped in a light variant as well as dark — never auto-inverted. No photography, no warm/cool grade, no grain, no b&w treatment; the only image processing in the system is the hero backdrop's blur and brightness reduction. Avatars are real photos at 32px round, falling back to initials on `background-raised` — never illustrated.

### Fixed and layered elements

The nav bar is the only sticky element on the marketing page (`z-index: 40`), transparent over the hero and translucent past ~40px of scroll. The product surface pins its sidebar, top bar and detail panel and scrolls only the table body. Mobile nav is a full-screen sheet on `background-deep` with links at `text-lg` and the CTA pinned to the bottom.

---

## Iconography

**Icons are outline-style, 16–20px, 1.5px stroke.** 16px in chips, buttons, metric rows, sidebars and footers; 20px in stat triplets and `extra-large` buttons. Fills are not used. Every icon that carries meaning is paired with a text label — categorical color is a memory aid layered on top of a readable label, never a substitute (§10 of the source).

Color comes from the categorical key and is passed in, never chosen by the component. A capability's icon stays fully saturated when its chip is inactive, because the color is identity, not state.

**No icon set was supplied with the source document.** The `Icon` component loads **Lucide** (outline, 24px grid, adjustable stroke, closest match to the specified 1.5px outline style) from the unpkg CDN on demand. This is a flagged substitution — see below. Names are passed kebab-case or PascalCase (`bot`, `git-pull-request`, `GitBranch`).

Beyond icons, three glyph conventions carry meaning and are not interchangeable:

- `›` chevron — navigation within the site (nav CTAs, section links, carousel advance)
- `↗` up-right — opens an example, doc or external resource
- `→` right — sequence or data flow, **in diagrams only**

No emoji anywhere. No PNG icons. No icon font. Status is a 7px round dot in a status token plus a text label. Diagram nodes are drawn from tokens (1px hue border, 950-step hue fill, mono uppercase label) rather than from icon assets.

---

## Index

**Root**

- `styles.css` — the entry point consumers link. `@import` lines only.
- `base.css` — resets, body defaults, focus ring, scrollbar treatments, rails, textures, keyframes.
- `thumbnail.html` — the system's homepage tile.
- `SKILL.md` — Agent Skills wrapper for use outside this project.
- `readme.md` — this file.

**`tokens/`** — `fonts.css`, `palette.css` (Layer 1), `semantic.css` (Layer 2 + light theme), `categorical.css`, `diagram.css`, `code-syntax.css`, `typography.css`, `spacing.css`, `radii.css`, `motion.css`.

**`components/`** — 19 families, each with `.jsx`, `.d.ts`, `.prompt.md`, and one `@dsCard` HTML per directory.

- `core/` — `Icon`, `Button`, `IconButton`, `CapabilityChip`, `Wordmark`, `ThemeToggle`
- `layout/` — `Section`, `SectionHeader`, `NavBar`, `Footer`
- `code/` — `CodeBlock`, `CodeShowcase`, `VerticalTabs`
- `diagram/` — `Diagram`, `DiagramNode`, `DiagramArrow`
- `marketing/` — `Hero`, `AnnouncementPill`, `ChipRow`, `PatternCard`, `Carousel`, `StatTriplet`, `MediaFeatureCard`, `AlternatingFeature`, `ExtensionCard`, `FeatureIndex`, `LogoMarquee`, `MetricRow`, `TestimonialCard`, `ClosingCTA`

**`guidelines/`** — 20 foundation specimen cards across four groups: Colors (ramps, both surface stacks, border ladder, categorical key, light-mode step-down, status, code syntax dark + light), Type (brightness hierarchy, display scale, body/UI scale, mono specimen, section header pattern), Spacing (scale, radii, rails in use), Brand (arrow conventions, motion timing, wordmark, focus and hover states).

**`ui_kits/`**

- `marketing_site/` — the full landing page in the canonical §8 section order. `index.html` + `content.jsx` (copy and data), `RunPeek.jsx` (live product surface for media slots), `Sections.jsx`, `Sections2.jsx`, `App.jsx`.
- `product_app/` — the runs dashboard. `index.html` + `data.jsx`, `Shell.jsx` (sidebar, top bar), `RunsView.jsx` (table + run detail with timeline and log wells), `OtherViews.jsx` (tasks, queues, empty state), `App.jsx`.

### Intentional additions

The source document's §7 defines 19 component families and this system builds all 19. Three additions were needed to make them usable:

- **`Icon`** — a wrapper for the glyph set. §7.6 and §7.9 specify icon sizes, stroke weight and categorical coloring but no set was shipped; every other component depends on this one.
- **`Diagram`** / **`DiagramNode`** / **`DiagramArrow`** — §2.5 specifies a complete node grammar (borders, fills, labels, solid vs dotted paths) but describes it as a color language rather than a component. It is componentised here because pattern cards, the how-it-works section and the product run timeline all consume it.
- **`Section`** — §4.3 and §4.4 specify the rails, the closing hairlines and the vertical rhythm as layout rules. Making them a component is what keeps the drawn grid consistent across the page.

---

## Substitutions and gaps

Flagged for your review:

1. **Fonts — resolved.** *Geist Variable* and *Geist Mono Variable* are self-hosted from `fonts/Geist-Variable.ttf` and `fonts/GeistMono-Variable.ttf`, wired as `@font-face` rules in `tokens/fonts.css`. Both are variable TTFs covering weights 100–900; the system only uses 400 / 500 / 600.
2. **Icons — substituted.** Lucide from CDN, as described above. Point `Icon`'s `LUCIDE_SRC` at your own sprite if one exists.
3. **Logo — absent by design.** No logo file was supplied. `Wordmark` renders the product name in plain type with the lavender suffix and leaves an empty slot for the lime gradient glyph described in §7.1; pass `glyphSrc` when the asset arrives. Nothing was drawn or approximated.
4. **Partner and customer logos — absent.** `LogoMarquee` and `ExtensionCard` accept real assets as nodes; the kits render company names in plain type as the honest stand-in.
5. **Product screenshots — absent.** §5.4's hero backdrop and §7.10's cropped media both call for real screenshots. The kits render live product surface (`RunPeek`, `CodeBlock`) into those slots instead. Swap in real screenshots — including a light variant of each — when you have them.
6. **No slide template.** The source contains no deck or slide specification, so no sample slides were created.
7. **Product screens are inferred from rules, not recreated.** The document specifies the product layer's surface stack, type scale, scrollbars, shadows and categorical key, but contains no product screen layouts. `ui_kits/product_app` demonstrates those documented rules on plausible operational views; it is not a recreation of a specific screen. If you have real product screens or code, send them and this kit should be rebuilt against them.
8. **Assets directory is empty.** `assets/` was not created because there were no assets to copy in.
