loworbit design system
source of truth. the hub follows this. the template's CLAUDE.md inlines the critical rules. every new prototype inherits them.
if you're an agent reading this to build a prototype: follow every section marked rule. treat the rest as context.
the short version
- lowercase everywhere in the ui. sentence case in prose. proper nouns keep case.
- monospace typeface only. JetBrains Mono with ligatures.
- one accent color.
#7dd3fc. used only to signal interactive, active, or noteworthy. never decorative. - 2px border radius on everything.
- no gradients. no stock photography. no emoji. no animations unless they communicate state.
- character-as-architecture for sections.
-- section name --. - the
▲glyph breathes when the system is active, stills when quiet. - every decision must be earned. if you can't articulate what a choice communicates, delete it.
principles
1. one accent, and it's earned.
#7dd3fc is the only color outside of the grayscale fg/bg/muted palette. use it to mark interactive elements, active states, live values, or anything the eye should go to first. applying it for decoration dilutes it.
rule: any element rendered in accent must be doing one of: linking, indicating active/live state, or highlighting a value that changed.
2. lowercase is structural.
not quirky. the ui is in lowercase because this is a place where sentences happen, not headlines. it signals that the site is a working document rather than a broadcast.
rule: all ui chrome is lowercase (nav, buttons, labels, section headers, badges, empty states). prose body copy uses sentence case. proper nouns (AWS, APMP, FedRAMP, Clerk, Vercel, Anthropic) keep their case. code/identifiers (slugs, hex codes, urls, commit hashes) preserve literal case.
3. monospace for honesty.
the site handles precise, structured text — prompts, api keys, slugs, commit hashes, log entries. monospace treats every character equally. it's not costume; it's accurate.
rule: JetBrains Mono, with ligatures enabled. no other typeface anywhere in the ui.
4. radius is 2px.
sharp corners feel mechanical. fully rounded feels friendly-corporate. 2px is the architectural compromise.
rule: every element with corners uses 2px border-radius. no exceptions.
5. motion must communicate.
decorative motion is banned. motion is acceptable when it represents a real state transition: a value changing, a status flipping, a deploy starting, an agent working.
rule: no ambient motion except the ▲ glyph, which breathes only while the system is actively doing something. all other motion must correspond to a state change the user should notice.
6. absence is a choice, not a gap.
no gradients. no stock photography. no decorative illustrations. no mouse-tracking spotlights. no microgrids. no scanning-line animations. no "uplink: active" sci-fi language. the blankness is the product.
rule: if a visual element cannot be justified by communication (text, data, live state), it does not exist.
7. refuse the category.
the "dark dev-site aesthetic" has a current fashion: mouse-tracking gradients, grid backgrounds, pulse animations, sci-fi status labels. loworbit refuses that category. we are in the lineage of sites where every gap is tuned and every interaction earned.
rule: before adding anything, ask: "does this exist on 5+ other dev sites in 2026?" if yes, it should probably not exist here unless it serves a specific loworbit-only purpose.
voice
banned phrases
leverage, unlock, empower, journey, seamless, intuitive, next-generation, cutting-edge, solution (as a noun meaning "our product"), let's, we're excited to, transformative, best-in-class, robust, synergy, innovative, uplink, mission-critical (outside literal mission contexts), any phrase that would feel at home in a B2B saas marketing email.
preferred patterns
- "use X" over "leverage X"
- "do things you couldn't before" over "unlock capabilities"
- "makes teams faster" over "empowers teams"
- show, don't claim: concrete numbers, specific verbs, real artifacts
- one em-dash per paragraph, max — and only when a comma or period won't do
tone
direct, quiet, honest. the voice of a working-document, not a pitch. the reader is assumed to be intelligent and in a hurry.
color
palette
| role | hex | use |
|---|---|---|
bg | #0a0a0a | page background everywhere. not #000. |
fg | #e5e5e5 | primary text. |
muted | #6b7280 | secondary text, timestamps, meta. |
border | ~#222222 | dividers, input borders, card edges. |
accent | #7dd3fc | links, primary buttons, active states, live values. |
ok | ~#86efac | success badges, "saved" indicators. muted green, not bright. |
error | ~#fca5a5 | error messages. muted red, not bright. |
rule: no new colors. no gradients. use opacity modifiers (fg/70, fg/50) instead of adding greys. no color outside this list.
accent rules
- links are accent on hover only (underline appears on hover too; default is underlineless accent text or unstyled depending on context)
- primary buttons are accent bg + inverted fg text
- live/active indicators use accent
- count values that tick or change use accent while changing, fade to fg after
type
family
JetBrains Mono, variable weight, ligatures on.
font-family: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo,
Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
font-feature-settings: "calt", "liga", "ss01", "ss20";
calt+liga: enable contextual ligatures (→,=>,>=,!=,->, etc.)ss01: cursive italic styless20: alternate zero style (dotted zero)
rule: no other typeface. no sans-serif fallback for headings. system mono only as a fallback if the font fails to load.
scale
| element | size | weight |
|---|---|---|
| page title (rare) | 30px | 400 |
| section heading | 24px | 400 |
| body | 14px or 16px | 400 |
section divider (-- section --) | 12px | 400 |
| meta / timestamp | 12px | 400 |
| code / ids | 12px | 400 |
| badge / tag | 11px | 400 |
rule: no bold anywhere. use color (text-fg vs text-muted) and size for hierarchy. monospace typefaces often render bold awkwardly at small sizes.
line length
body prose: max 65-75 characters per line. enforces narrow containers for reading sections.
spacing
strict 8px scale. use only these increments:
| tailwind class | px | use |
|---|---|---|
gap-1 / p-1 / m-1 | 4px | rare. tight inline spacing only. |
gap-2 / p-2 / m-2 | 8px | inline text gaps, tight inputs. |
gap-3 / p-3 / m-3 | 12px | standard inline. |
gap-4 / p-4 / m-4 | 16px | comfortable inline. |
gap-6 / p-6 / m-6 | 24px | between components within a section. |
gap-8 | 32px | minor breaks. |
gap-12 | 48px | major section breaks. |
gap-16 | 64px | page-level breaks. rare. |
rule: never use arbitrary tailwind values. no p-[17px]. no gap-5 (20px — off-grid). the scale is the scale.
one explicit micro-tier exception: status pills and badges may use gap-1.5 (6px), px-1.5 (6px), or py-0.5 (2px). these are the only contexts where sub-8px vertical spacing is allowed. every other element uses the main scale.
page width
- narrow (
max-w-3xl, ~768px): reading context. portfolio, /now, /log, writing, admin detail pages. protects 65-75 character line length. - wide (
max-w-6xl, ~1152px): scanning context. admin inventory, dashboards where density matters. - full: only for editors/canvases where chrome is the tool.
rule: if the eye moves down the page, narrow. if the eye compares across, wide.
components
primitives
all component primitives live in the template and are reused everywhere. do not invent new primitives without a justified need.
terminal-box
- bg slightly lighter than page (
#111) - 1px border-border
- 2px radius
- used for cards, list containers, callouts, input groups
terminal-button
- transparent bg, 1px border-border, text-fg
hover:bg-fg/5focus-visible:ring-1 focus-visible:ring-accent focus-visible:ring-offset-2 focus-visible:ring-offset-bgpx-3 py-1.5 text-xs- default button. secondary actions.
terminal-button-primary
- accent bg, bg-color text (inverted)
hover:opacity-90- same focus ring
- same padding
- rule: one per screen, max. only for the single most important action.
terminal-input
- transparent bg, 1px border-border, text-fg, font-mono
- labels always above inputs, never floating
terminal-label
- 12px, uppercase, tracking-wider, text-muted
- always above the input it describes
terminal-link
- inline links in accent color
- no underline by default
- underline on hover only
- no visited-state distinction
status pills
- rounded 2px, 1px border, text-xs
- green for
live,ok,succeeded - accent for
building,iterating,running - muted for
draft,queued - red for
error,failed - grey/strikethrough for
retired
states
hover
two patterns. no third.
hover-lift— containers, rows, cards.hover:bg-fg/5. subtle 5% wash on hover. signals "this is interactive."hover-underline— inline text links only. underline appears on hover.
buttons inherit hover-lift. everything else inherits one of the two by context.
rule: any hover treatment that isn't one of these two is a bug. no scale-up, no color shift, no border-color change.
focus
one pattern. everywhere.
focus-visible:ring-1 focus-visible:ring-accent
focus-visible:ring-offset-2 focus-visible:ring-offset-bg
1px accent ring with 2px bg offset. uses focus-visible so mouse users don't see it on click; keyboard users always do.
rule: every interactive element has this focus treatment. audit keyboard-navigation for any that's missing. a missing focus ring is an accessibility bug.
loading
no spinners. no skeletons.
- narrated step list when multiple things happen in sequence:
generating surface 4 of 15... - single active indicator when one thing is happening: the
▲glyph breathing - never a generic twirling circle
empty
nothing live yet.
one line, terminal-box, centered, text-muted. no illustrations. no CTAs unless there's an obvious next action and the page has real authority to push it.
error
terminal-box with border-error and text-error. inline, not modal. prose error text (no codes, no tracebacks). short. ends with what to do next if possible.
couldn't read this file. try pdf or docx under 20mb.
success
✓ saved
checkmark (text, not icon library) in ok color. "saved" in muted. fades after 2s if ephemeral. persists if it affects the page.
motion
the one ambient exception
the ▲ glyph at the top-left of every page breathes (opacity cycle, 4s period, 0.7 → 1.0 → 0.7) only when the system is actively doing something. active states:
- a spawn is in-flight
- an agent build is running on any prototype
- an iteration is queued or running
- the retire-expired cron is running
determined by a single GET /api/system-status call from the client on mount, polled every 30s. when active, the glyph breathes. when quiet, it's still.
rule: no other ambient motion exists in the system. not anywhere. not on any page.
reactive motion (hover/focus/state change)
hover and focus are handled by their two patterns (above). state-change motion happens when:
- a value ticks up (count, timestamp)
- a status badge flips (
queued→running→succeeded) — fade 200ms, ease-out - a progress step checkmarks in (step list loading indicator)
- a card expands/collapses on click (height, 200ms, ease-out)
rule: all reactive motion uses the same easing (ease-out) and similar durations (200ms for state changes, 4000ms for the triangle). no unique easing curves per component.
what's banned
- parallax
- fade-in-on-scroll
- stagger reveals on page load
- typewriter effects (one-time or ongoing)
- floating/drifting elements
- mouse-tracking gradients or spotlights
- scanning lines across cards
- pulse animations on "status" labels
- any decoration that moves for its own sake
content structure
section dividers (character-as-architecture)
sections on text-heavy pages are demarcated by monospace ascii-inspired strings:
-- bio --
-- log --
-- what's running --
-- now → /now --
- 12px, font-mono,
text-muted - not uppercase (too loud)
- the two
--wrappers are literal - inline arrows (
→) to indicate the divider itself is a link
rule: section dividers replace traditional <h2> tags on manuscript-style pages (landing, /now, /log, /tools). admin pages still use normal section headings.
the /now page
a microblog of dated entries. the landing shows the most recent. /now shows the archive. each entry gets its own url.
schema: now_entries { id, content (markdown), created_at, slug (YYYY-MM-DD) }
flow:
/admin/now→ textarea + submit → creates a new entry- landing renders
latest.contentwithlatest.dateas the divider link /nowrenders all entries, reverse-chron, full content/now/[slug]renders a single entry, shareable
rule: /now is updated at the author's discretion. no minimum frequency. staleness is acceptable — "last updated 2 months ago" is better than fabricating urgency.
the /log
a mixed auto+manual event log. system events auto-populate. narrative entries come from the author.
schema: log_entries { id, source ('auto'|'manual'), content, event_type (nullable), related_prototype (nullable), created_at }
auto triggers:
- new spawn →
spawned {slug} - iteration auto-merge →
{slug} v{n} shipped - retire →
retired {slug} - go-live →
went live: {slug}
manual entries: bug post-mortems, framing commentary, non-prototype shipping, thoughts. written via /admin/log.
landing shows the latest 7, sorted by created_at desc. /log or /archive shows the full history.
rule: system entries should be one short line. manual entries can be longer but still terse by default.
the /tools/[slug] pages
public prototype writeups. one per live prototype. exists to give context to strangers.
content: title, slug, one-line description, longer narrative about why it was built, link to try it (prototype subdomain), link to github repo, optional screenshot.
rule: each prototype linked from the landing opens its /tools/[slug] page, not the prototype directly. random visitors need context before they click into a live tool.
what loworbit deliberately doesn't do
- no logo beyond the
▲glyph - no logotype, no animated mark, no "brand kit"
- no dark/light toggle (dark is the aesthetic)
- no marketing pages ("why loworbit", "pricing", "customers")
- no footer "made with love" or "built with next.js" (the footer is live json metadata, see below)
- no cookie banner beyond legal minimum
- no parallax, no fade-in-on-scroll, no stagger-reveal animations
- no mouse-tracking gradients
- no microgrid backgrounds
- no pulse animations on arbitrary status labels
- no sci-fi status vocabulary ("uplink", "system: online", "transmitting")
- no decorative ASCII art
- no easter eggs that require konami codes or hidden keys — if something exists, it should be discoverable through normal use
the footer is live metadata, not a byline
the page footer is a single-line json object with real, current data pulled from the system:
{ deploy: "da8cff0", agents_run: 43, live: 4, last_spawn: "2h" }
deploy: current vercel commit hash (short)agents_run: total agent builds ever triggered (from iterations table)live: count of prototypes with status='live'last_spawn: time since most recent spawn
rule: no "made by", no "© 2026", no "built with". the json is the footer.
applying this in new prototypes
every new prototype (spawned via /admin/new) inherits the template's CLAUDE.md, which references this document and inlines the critical rules. when an agent builds a prototype, it reads:
- all principles (voice, motion, absence, earned-only)
- the type scale + JetBrains Mono config
- the 8px spacing scale
- the two hover patterns and one focus pattern
- the component primitives (terminal-box, terminal-button, etc.)
- the banned elements list
the agent may add ui patterns specific to its prototype's function (e.g., panel's conversation view, surface's editor canvas) but those patterns must follow the design system's color/type/spacing/motion rules.
the checker runs a shared-chrome-diff against every build. it does not yet lint for design-system compliance — that's a future enhancement. for now, compliance is the agent's responsibility, enforced by the template's CLAUDE.md.
current inconsistencies (cleanup list)
- some cards use
terminal-box, others useborder border-border. consolidate. - muted grey inconsistent: sometimes
text-muted, sometimestext-fg/70, sometimestext-fg/50. pick per-context rules. - button sizes ad-hoc. standardize on xs / sm / base.
- admin pages inconsistent: some use
max-w-6xl, some usemax-w-3xl. apply the narrow/wide rule consistently. - hover treatments vary (some rows have no hover, some have wash, some have border change). audit and reduce to
hover-lift+hover-underlineonly. - no global focus-visible rule applied yet. needs audit.
these land as a cleanup commit before or alongside the landing redesign.
changelog
- 2026-04-16 — initial version. written after extensive ideation. captures the "kinetic manuscript" debate and the final "earned-only, refuse-the-category" position.