oll.am · The Design Language · A Capture & Reuse Artifact · 2026-06-26

Specview's System, Captured

A working style library, not an essay. Every token and component below is quoted verbatim from the specview Design Playground and the live app stylesheet — and this page itself runs on them. The closing Reuse Kit is what oll.am copies in, no content changed.
Hub: Landing Build Log Sources: specview/landing/playground.html · web-ng/src/styles.css

What's inside

  1. Why this exists
  2. Tokens — Color
  3. Tokens — Typography
  4. Tokens — Spacing & Radii
  5. Tokens — The Border System
  6. Tokens — Shadow & Z-index
  7. Tokens — Motion
  8. Component Gallery
  9. The Language, Named (→ Rams)
  10. The Reuse Kit
  11. Reuse, not rebuild

Why this exists capture once, reuse everywhere

specview already solved its visual layer — a complete newspaper design system with a 926-line playground that renders every component, every state, every token. oll.am has six products to consolidate and a stack of HTML artifacts that should look like one publication. The cheapest way to get there is not to design again; it is to lift specview's system verbatim. This page is the bridge: the tokens and component CSS pulled out as a portable library, with one line each on where it slots into oll.am.

Borders do the work that cards and shadows do elsewhere. The whole system is a newspaper: rules, columns, weights, and restraint — not boxes. — the one idea the whole language hangs on

Tokens · Color from styles.css :root — light + dark

The shared palette is eight values. Everything is built from ink-on-cream with a single slate-blue accent and one editorial red. Swatches render live — toggle dark mode in the masthead to see the paired values.

Shared palette
--bg#FFFEF9
--ink#121212
--ink-light#5A5A5A
--ink-muted#999999
--border#DFDFDF
--border-dark#121212
--accent#567B95
--red#C41E3A
Surfaces, status & section-identity (app-only)
--surface#FAFAF7
--surface-raised#F5F5F0
--code-bg#F5F5F5
--status-running#22A66A
--status-success-bg#2E7D32
--color-ready#7B6BAE
--color-braindump#A08060

The raw custom properties, verbatim from web-ng/src/styles.css (light) — the dark overrides live under [data-theme="dark"] in the same file and at the top of this page:

:root {
  --bg: #FFFEF9;
  --ink: #121212;
  --ink-light: #5A5A5A;
  --ink-muted: #999999;
  --border: #DFDFDF;
  --border-dark: #121212;
  --accent: #567B95;
  --red: #C41E3A;
  --code-bg: #F5F5F5;
  /* status (four-state gen bar) */
  --status-idle:    #1a6b30;
  --status-active:  #7a5800;
  --status-success: #1a6b30;
  --status-failure: #C41E3A;
  --status-running:    #22A66A;
  --status-success-bg: #2E7D32;
  /* surfaces */
  --surface: #FAFAF7;
  --surface-raised: #F5F5F0;
  /* section identity (header titles only — never on cards) */
  --color-active:    #22A66A;
  --color-specced:   #567B95;
  --color-ready:     #7B6BAE;
  --color-braindump: #A08060;
}
Honesty note: dark-mode --red shifts to #E05A72 (and the playground's app-only block lists --status-failure: #8B0000 dark). Those are the values in the source; the swatches above show light-mode hexes with the live boxes re-tinting on toggle.

Tokens · Typography three families, three jobs

Each typeface has exactly one role and a hard rule. Playfair never appears at weight 400 in a headline; Source Sans never carries multi-sentence reading text. JetBrains Mono is this artifact's code face (specview uses 'SF Mono', Consolas).

FamilyVarRole & ruleWeights
Playfair Display--serifHeadlines, titles, pull-quotes, large numbers. Always 700 for headlines — never 400.400 / 700 / 700i
Source Serif 4--bodyBody copy, paragraphs, reading text. Multi-sentence content only.400 / 600
Source Sans 3--sansLabels, metadata, UI chrome, uppercase tags. Never for reading text.400 / 600
Type scale — the recurring sizes, observed across the playground & app
pxFamilyWhere it appears (verbatim)
64 / 56Playfair 700.masthead-title (app 64, demo 56), letter-spacing:-0.02em
36Playfair 700.expanded-title, line-height:1.15
26Playfair 700.markdown-content h1
22Playfair 700.modal-title
20Playfair 700.markdown-content h2
18Playfair 700.context-card__label, .file-item.featured title
15Playfair 600 / Serif.file-item-title, .col-title (700); markdown body
13–14Source Serif 4.file-item-teaser, modal textarea body
11Source Sans 600nav links, .op-chip, meta — uppercase, letter-spacing:0.06–0.1em
9–10Source Sans 600.overline (9), .badge (9), .col-badge (10)
Rendered specimen
Masthead 64/56
Heading 26 — Playfair 700
Body 15 in Source Serif 4. The editorial reading voice — multi-sentence content lives only here.
Label 11 · Source Sans · Uppercase
Verbatim declarations
--serif: 'Playfair Display', Georgia, serif;
--body:  'Source Serif 4', Georgia, serif;
--sans:  'Source Sans 3', system-ui, sans-serif;

.masthead-title { font: 700 64px var(--serif);
  letter-spacing: -0.02em; }
.file-item-title { font: 600 15px var(--serif);
  line-height: 1.3; }
.overline { font: 600 9px var(--sans);
  letter-spacing: 0.12em;
  text-transform: uppercase; }

Tokens · Spacing & Radii conventions, not named variables

Honesty note: specview has no named spacing or radius custom properties. The rhythm is a convention applied by hand across the CSS. These are the recurring values, quoted from the source — adopt them as the de-facto scale.

ScaleRecurring values (px)Where
Spacing / rhythm4 · 6 · 8 · 12 · 16 · 20 · 24 · 32 · 40 · 48 · 52gaps, padding, margins — e.g. .page{padding:20px 40px}, .file-column{padding:0 24px}, .markdown-content{column-gap:52px}
Border radius2px standard · 3px occasional · 20px pill · 50% dots.col-badge/.op-chip/.badge = 2; .style-chip = 20 (the one pill); status dots = 50%
Containermax-width 1400 (app) · 920–1080 (artifacts).page{max-width:1400px;margin:0 auto}
Reuse rule: radius is binary — 2px for every chip, badge and input; 20px only for the style-preset pill; 50% only for status dots. There is no large-radius "card" rounding anywhere, because there are no cards.

Tokens · The Border System the load-bearing token group

The entire visual hierarchy is borders — eight distinct, semantic uses. Weight encodes importance: 3px ink = major structural break, 2px ink = section-label underline, 1px border = content divider. This is the heart of the language.

WeightSemantic roleRendered
1px solid --borderMasthead bottom, column dividers, between sections
2px solid --inkColumn/widget title underline, editor toolbar bottom
3px solid --inkMajor structural break (section-bar top, expanded panel top, footer top)
3px solid --border-dark<hr> thick rule
border-left 3px --border-darkCode-block accent (markdown / pre)
code
border-left 2px --inkSidebar active file
active
border 1px dashed --accentGenerate button (the "AI action" affordance)
3px double --border-darkMasthead / footer rule (the artifact convention)

Tokens · Shadow & Z-index used three times on purpose

Shadow is almost banned. It appears in exactly three places, each deliberate — anything that floats above the page earns a shadow; nothing on the page does. Z-index is a short, flat ladder.

The only three shadows (verbatim)
/* 1. Modal — floats over a scrim */
.modal { box-shadow: 0 8px 48px rgba(0,0,0,0.18); }

/* 2. Floating editor toolbar — docked above */
.editor-toolbar--floating {
  box-shadow: 0 -4px 20px rgba(0,0,0,0.08); }

/* 3. Context-card hover — the one on-page lift */
.context-card:hover {
  box-shadow: 0 2px 8px rgba(0,0,0,0.06); }
Z-index ladder (observed)
9     editor toolbar / gen-status-bar (sticky)
10    .section-nav (sticky)
100   .editor-toolbar--floating (docked)
1000  .modal-backdrop
3000  .modal-backdrop (playground variant)

Honesty note: these are inline values, not a named token scale. The rule is the takeaway — sticky chrome ≈ 9–10, overlays ≈ 1000+.

Tokens · Motion subtle, never layout-shifting

One standard transition (0.15s) for all hovers; a handful of named keyframes for state. Motion communicates state change, never decoration. Three render live below.

NameTimingJob
rise0.2s ease · one-shotPanel / row entering — opacity + 6px translateY
gen-shimmer1.6s linear · infiniteGeneration in-progress track
poll-pulse0.7s ease-out · infinite altLive polling indicator (expanding ring)
dot-pulse1s ease-in-out · infinitePer-file running dot
thinking-pulse1.2s ease-in-out · infiniteAI "thinking" three-dot row
count-pulse200ms ease-out · one-shotA count badge ticking up
status-success-flash2s ease-out · one-shotStatus bar flashing on success
(transition)0.15sThe universal hover/focus duration
Live
gen-shimmer
poll-pulse
thinking-pulse
Verbatim keyframes
@keyframes rise {
  from { opacity:0; transform:translateY(6px); }
  to   { opacity:1; transform:translateY(0); }
}
@keyframes gen-shimmer {
  0%   { background-position: 200% 0; }
  100% { background-position: -200% 0; }
}
@keyframes poll-pulse {
  0%   { box-shadow:0 0 0 0   rgba(34,166,106,.55); }
  100% { box-shadow:0 0 0 5px rgba(34,166,106,0); }
}

Component Gallery rendered live + verbatim CSS

Each component: what it is and when to use it, the real thing rendered (it inherits the captured CSS at the top of this file), and the source to copy. Nothing here is redrawn.

Masthead

shared · styles.css
What: the newspaper nameplate — a 3-column grid (edition · title · actions). When: the top of every page; the masthead IS the brand.
Vol. 1, No. 1
Saturday, June 2026

oll.am

All the Specs Fit to Build
.masthead-top { display:grid;
  grid-template-columns: 150px 1fr 150px;
  align-items: flex-end; gap: 20px; }    /* center=editorial; app uses align-items:center */
.masthead-title { font: 700 64px var(--serif); letter-spacing: -0.02em; }
.masthead-tagline { font: italic 13px var(--body); color: var(--ink-light); }

Buttons

app + landing
What: the button vocabulary — filled-ink primary, accent-outline, ink-outline, filled-accent money CTA. When: editor-apply/modal-generate for the dominant action, upgrade-btn for checkout (filled accent so money reads as primary), outline variants for secondary.
.editor-apply-btn { background: var(--ink); color: var(--bg);
  border: 1px solid var(--ink); font: 700 12px var(--sans);
  text-transform: uppercase; letter-spacing: .06em; padding: 6px 22px; }
.upgrade-btn { background: var(--accent); color: #fff;     /* money = filled accent */
  border: 1px solid var(--accent); padding: 5px 12px; }
.new-project-btn { background: transparent; color: var(--accent);
  border: 1px solid var(--accent); }                       /* hover → fills accent */
.logout-btn { background: transparent; color: var(--ink);
  border: 1px solid var(--border-dark); }                  /* hover → fills ink */

Honesty note: the landing's .btn-primary / .btn-secondary live in landing/style.css (not read here); per the playground states table their hovers are opacity:0.85 (primary) and border-color:--ink (secondary).

Op Chips & Style Chips

app · styles.css
What: uppercase toggle chips (square, 2px radius) for operations; a label divider; pill chips (20px) for style presets. When: a small set of mutually-exclusive actions or filters. Active op-chip inverts to ink fill.
Rewrite
.op-chip { font: 11px var(--sans); text-transform: uppercase;
  letter-spacing: .06em; border: 1px solid var(--border);
  color: var(--ink-muted); padding: 4px 14px; border-radius: 2px; }
.op-chip.active { background: var(--ink); color: var(--bg); border-color: var(--ink); }
.op-chip--accent { border-color: var(--accent); color: var(--accent); }
.style-chip { border-radius: 20px; }   /* the one pill in the system */

File Grid & File Item

app (origin: ClawBoi)
What: the newspaper front page — columns split by 1px borders (gap:0; borders do the spacing), each with a 2px-ink underlined header and stacked items. When: any list of grouped documents/products. .featured bumps the first item bigger.
Analysis3
ClawBoi Ecosystem
2026-05-07 · analysis.md
Epic2
Architecture2
.file-grid { display: grid; grid-template-columns: repeat(3,1fr);
  gap: 0; }                                  /* borders do the work */
.file-column { padding: 0 24px; border-right: 1px solid var(--border); }
.file-header { border-bottom: 2px solid var(--ink);
  margin-bottom: 16px; padding-bottom: 8px; }
.file-item { border-bottom: 1px solid var(--border); transition: background .15s; }
.file-item.featured .file-item-title { font-size: 18px; font-weight: 700; }

Context Cards

app · styles.css
What: the one true "card" — 1px border, Playfair label, hover lifts border to ink + the system's only on-page shadow. When: a small grid of pickable options (the three spec stages, a product chooser).
Analysis
Problem framing, constraints, scope.
Epic
Tasks, effort, dependencies. (hover)
Architecture
System design and data flow.
.context-card { border: 1px solid var(--border); padding: 20px;
  transition: border-color .15s, box-shadow .15s; }
.context-card:hover { border-color: var(--ink);
  box-shadow: 0 2px 8px rgba(0,0,0,.06); }   /* the one on-page shadow */
.context-card__label { font: 700 18px var(--serif); }

Diff Blocks

app · styles.css
What: add/remove pair — red tint + strikethrough + 0.65 opacity for removed, green tint for added, both with a 3px left accent. When: showing a before/after rewrite or any change set.
The chain adapter requires explicit error handling at every call site.
The chain adapter catches ProviderError internally — callers receive clean results.
.diff-block-remove { background: rgba(196,30,58,.05);
  border-left: 3px solid var(--red);
  text-decoration: line-through; opacity: .65; }
.diff-block-add { background: rgba(46,125,50,.06);
  border-left: 3px solid var(--status-success-bg); }

Badges & Overline

app + landing
What: tiny uppercase state markers (grey default; red/green/accent for new/complete/ready) and the hero overline kicker. When: badges encode state, never category; overline sits above a headline as a label.
Introducing oll.am
Your braindump becomes a spec.
3 New Complete Ready
.badge { font: 600 9px var(--sans); text-transform: uppercase;
  letter-spacing: .05em; padding: 2px 6px; border-radius: 2px;
  background: var(--border); color: var(--ink-light); }
.badge--new { background: var(--red); color: #fff; }
.badge--complete { background: var(--status-success-bg); color: #fff; }
.overline { font: 600 9px var(--sans); letter-spacing: .12em;
  text-transform: uppercase; color: var(--ink-muted); }

Generation Status Bar

app · styles.css 5.7
What: a full-width four-state bar (idle / active+shimmer / success / failure+retry) on a saturated state colour. When: a global async process the user should track (generation, deploy, upload).
oll-core·idle — ready
oll-core·generating architecture…
oll-core·done — 5 files generated
oll-core·error — chain timed out
.gen-status-bar--idle    { background: var(--status-idle); }
.gen-status-bar--active  { background: var(--status-active); }
.gen-status-bar--success { background: var(--status-success); }
.gen-status-bar--failure { background: var(--status-failure); }
.gen-status-track { height: 2px; background-size: 200% 100%;
  background: linear-gradient(90deg, transparent, var(--accent), #fff, var(--accent), transparent);
  animation: gen-shimmer 1.6s linear infinite; }

Modal

app · styles.css
What: centred dialog over a scrim — 1px-dark border, 2px-dark header rule, ghost cancel + filled-ink generate. When: a focused entry task (new project, confirm). One of three places shadow is allowed.
.modal { background: var(--bg); border: 1px solid var(--border-dark);
  max-width: 680px; box-shadow: 0 8px 48px rgba(0,0,0,.18); }
.modal-header { border-bottom: 2px solid var(--border-dark); }
.modal-input:focus { border-color: var(--ink); }   /* focus = ink border */

Markdown Content

app · styles.css
What: two-column newspaper body with a column rule; headings span all columns. When: rendering long generated prose (a spec, an article). Collapses to one column under 900px.

Chain Adapter Pattern

The chain adapter is the only AI call boundary. Feature modules import from chain/adapter.py — never providers directly.

Core Functions

Three functions cover all uses: generate(), stream(), rewrite().

The chain layer is a boundary, not an abstraction.
.markdown-content { column-count: 2; column-gap: 52px;
  column-rule: 1px solid var(--border); line-height: 1.85; }
.markdown-content h1 { font: 700 26px var(--serif); column-span: all;
  border-bottom: 1px solid var(--border); }
@media (max-width: 900px) { .markdown-content { column-count: 1; } }

Search Bar & Update Banner

app · styles.css
What: a borderless underline search input with a count, and an ink-filled full-width banner with an outline reload button. When: filtering a grid; announcing fresh data ("3 files updated").
New spec files are ready — 3 files updated.
.search-bar input { border: none; border-bottom: 1px solid var(--border);
  background: none; } .search-bar input:focus { border-bottom-color: var(--ink); }
.update-banner { background: var(--ink); color: var(--bg); text-align: center; }
.update-banner button { border: 1px solid var(--bg); background: none; color: var(--bg); }

The Language, Named five principles, each mapped to Dieter Rams

The system isn't arbitrary taste — it's a small set of principles, each tracing to one of Rams' ten rules of good design, and each provable in the real specview CSS above.

1 · The newspaper metaphor

Rams 4 — Understandable

A masthead, columns, section headers, pull-quotes, a body in a serif reading face. The interface borrows a 400-year-old form everyone already knows how to read, so structure needs no explanation — your eye walks it like a front page.

Why it maps: Rams' "good design makes a product understandable" — the form clarifies structure and is, ideally, self-explanatory. The newspaper is a pre-loaded mental model; the UI inherits its legibility for free.

2 · Borders, not boxes

Rams 10 — As little design as possible

Hierarchy is carried entirely by border weight (3px / 2px / 1px) and whitespace. Almost no fills, almost no shadows (three, all for things that float), almost no radius (2px). gap:0 with borders doing the spacing.

Why it maps: "less, but better — back to purity, back to simplicity." Every removed box is one less thing competing for attention. The restraint is the design.

3 · One accent, earned

Rams 5 — Unobtrusive

The palette is ink-on-cream plus a single slate-blue --accent and one editorial --red. Colour is rationed: accent marks the AI/primary action and money CTA; red marks destructive/error; saturated greens/ambers appear only inside the status bar.

Why it maps: "products are tools, not decoration — neutral and restrained, to leave room for the user." When colour is scarce, the little there is becomes meaningful and directs the eye.

4 · States tell the truth

Rams 6 — Honest

The four-state generation bar, the shimmer-while-working track, the polling pulse, the diff add/remove blocks — the UI shows exactly what the system is doing, including failure (red bar + Retry). Nothing is faked or hidden behind a spinner.

Why it maps: "good design is honest — it does not make a product more innovative, powerful or valuable than it really is." Real progress, real latency badges, real errors. The interface doesn't oversell.

5 · One token source, dark-mode paired

Rams 8 — Thorough to the last detail

Every value is a custom property defined once in :root and paired under [data-theme="dark"] — components never hardcode a colour. The playground exists at all because the system is documented down to each chip's radius and each animation's easing.

Why it maps: "nothing must be arbitrary or left to chance — care and accuracy in the design process show respect for the user." A single source of truth is thoroughness made mechanical.

The sixth, implied: Rams 7 — long-lasting. A border-and-serif newspaper avoids trend-driven styling (no gradients-of-the-year, no glassmorphism), so it dates slowly. That is exactly why it's worth freezing and reusing rather than redesigning per product.

The Reuse Kit copy these into oll.am — change no content

Portable plain CSS, drops into any static <style> (no Angular, no SCSS). Adopt in this order; each enriches the existing oll.am HTMLs without touching a word of copy.

#StyleWhere it slots into oll.am
1The :root token block (light + dark)The foundation — paste both blocks once; every artifact already uses these var names, so this aligns colour/type/dark-mode instantly.
2The 3 font familiesSwap the Google Fonts link to Playfair + Source Serif 4 + Source Sans 3; gives every page the editorial voice.
3Border hierarchy (3/2/1px ink/border)Section breaks → 3px ink; heading underlines → 2px ink; dividers → 1px border. Replaces ad-hoc rules across the build-log pages.
4.masthead + .masthead-titleStandard nameplate at the top of every oll.am artifact — unifies the publication.
5.file-grid / .file-column / .file-itemThe artifact index / timeline page — list products or docs as a newspaper front page.
6.context-card (+ hover)The product chooser / "pick a service" grids; the only sanctioned card + shadow.
7op-chip / style-chip / badge / overlineFilters, tags, kickers and state markers throughout — the small-label vocabulary.
8Button set (apply / upgrade / outline)CTAs — use the filled-accent upgrade-btn for every Stripe checkout so money reads as primary.
9.diff-block-add / -removeBefore/after sections in decision write-ups and migration docs.
10.gen-status-bar (4 states) + keyframesAny live-process page (deploy status, generation) — honest progress + failure.
11.markdown-content (2-col)Long-form generated prose blocks inside artifacts.
12The 7 @keyframes + 0.15s transitionAll motion — paste once, reference by name.
Already done here: this very file is the proof — its <style> head contains items 1, 2 and 4–12 pasted verbatim, and every rendered example above is the real component. To reuse, lift that block into oll.am/site as a shared tokens.css + components.css and link it from each artifact.

Reuse, not rebuild — applied to the visual layer

specview's design system is a finished, frozen-worthy asset, exactly like oll-core is on the backend. The move is the same: prove it where it lives, then lift it verbatim. These tokens and component styles get pulled into the oll.am HTMLs as a shared library — the publication gets specview's polish without anyone redrawing a button. The cheapest good design is the good design you already shipped.