Skip to content

Ledger

Every edge is a hairline, every corner is square, every headline is a serif. Colour only means status.

  • Light by default
  • 35 colours · 14 type steps
  • Inspired by midday.ai
Use Ledger
localhost:3000

The same generated app in every design. Scroll inside the window to see the whole page.

What it is

Ledger draws the interface like a ruled book. One warm 1px hairline marks every edge, corners are square, and only things that float, like menus and dialogs, cast a shadow. Headlines are set large in Hedvig Letters Serif over a single-weight Hedvig Letters Sans, with small labels in tracked capitals and figures in IBM Plex Mono. Actions are flat ink blocks, and the only colour on screen is a status: a green dot, an amber chip, a red label. It opens in light, white with warm grey lines, and its dark palette is the same book on near-black.

Maintained by @agentic-studio in the open registry.

Best for

Money and operations products where tables and figures carry the screen: invoicing, bookkeeping, analytics, admin consoles and internal tools. Also calm, text-first marketing sites that want to look exact rather than loud.

Trade-offs

  • Flat and square on purpose. With no radius and no shadow on the page, hierarchy comes only from space, type and rules, so a crowded screen looks crowded at once.
  • One real weight. Hedvig Letters Sans ships only 400, so emphasis comes from size, colour and capitals. font-medium and font-semibold render as regular, and only strong gets a synthetic bold.
  • Greyscale charts. Series differ by lightness alone. The first three are spread wide, but past three series a chart needs direct labels, and a forecast line needs a dash.

Tokens

Generated from this design’s design.yaml, and shipped as CSS variables plus a Tailwind theme. Templates may use these names and nothing else.

Colours

Every colour token in design.yaml, grouped the way the system documents them. These are the light palette's values. The dark palette uses the same names with its own values, and tokens.css ships both.

Brand and accent

  • accent

    #121212

  • accent-1

    #23211e

  • accent-2

    #7d7c78

  • accent-3

    #474642

  • accent-4

    #959490

  • accent-5

    #65635f

  • accent-6

    #b1afab

  • accent-hover

    #2f2f32

  • accent-soft

    #ebeae7

  • primary

    #18181b

Surface

  • canvas

    #ffffff

  • hairline

    #dbdad7

  • surface-card

    #f3f2ef

  • surface-dark

    #121212

  • surface-dark-elevated

    #1c1c1c

  • surface-soft

    #f7f6f4

  • surface-solid

    #f7f6f4

  • surface-strong

    #e6e4e0

Text

  • body

    #4a4a4a

  • body-strong

    #242424

  • ink

    #121212

  • muted

    #616161

  • muted-soft

    #8a8a8a

  • on-accent

    #fafafa

  • on-dark

    #fafafa

  • on-primary

    #fafafa

Semantic

  • error

    #a43b36

  • success

    #006e30

  • warning

    #785a00

Other

  • danger

    #a43b36

  • danger-soft

    #f7eceb

  • error-soft

    #f7eceb

  • primary-hover

    #2f2f32

  • success-soft

    #eaf2eb

  • warning-soft

    #f3efe6

Type scale

Each token rendered at its real size, weight, line height and tracking. Specimens fall back to this site's typeface when the design's own face is not installed locally, so read the numbers, not the letterforms.

  • Ship faster

    Token
    display-xl
    Values
    4.5rem · 72px · 400 · lh 1 · ls -0.025em
  • Ship faster

    Token
    display-lg
    Values
    3.5rem · 56px · 400 · lh 1.04 · ls -0.025em
  • Ship faster

    Token
    display-md
    Values
    2.5rem · 40px · 400 · lh 1.1 · ls -0.02em
  • Ship faster

    Token
    display-sm
    Values
    2rem · 32px · 400 · lh 1.15 · ls -0.015em
  • Ship faster

    Token
    title-lg
    Values
    1.5rem · 24px · 400 · lh 1.33 · ls 0
  • Ship faster

    Token
    body-lg
    Values
    1.125rem · 18px · 400 · lh 1.56 · ls 0
  • Ship faster

    Token
    title-md
    Values
    1.125rem · 18px · 400 · lh 1.45 · ls 0
  • Ship faster

    Token
    title-sm
    Values
    1rem · 16px · 400 · lh 1.5 · ls 0
  • Ship faster

    Token
    body-md
    Values
    0.9375rem · 15px · 400 · lh 1.6 · ls 0
  • Ship faster

    Token
    body-sm
    Values
    0.875rem · 14px · 400 · lh 1.5 · ls 0
  • Ship faster

    Token
    button
    Values
    0.875rem · 14px · 400 · lh 1 · ls 0
  • Ship faster

    Token
    nav-link
    Values
    0.875rem · 14px · 400 · lh 1.4 · ls 0
  • Ship faster

    Token
    caption
    Values
    0.75rem · 12px · 400 · lh 1.5 · ls 0
  • SHIP FASTER

    Token
    caption-uppercase
    Values
    0.6875rem · 11px · 400 · lh 1.5 · ls 0.08em

Typefaces

The families the generated Tailwind theme points at. A licensed face is named here even when the substitute below is what actually renders.

  • DisplayHedvig Letters Serif, ui-serif, Georgia, "Times New Roman", serif
  • BodyHedvig Letters Sans, ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif
  • MonoIBM Plex Mono, ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace

Spacing scale

Drawn at true size. These are the only gaps and paddings a generated repo is allowed to use.

  • xxs0.25rem · 4px
  • xs0.5rem · 8px
  • sm0.75rem · 12px
  • md1rem · 16px
  • lg1.5rem · 24px
  • xl2rem · 32px
  • xxl3rem · 48px
  • section6rem · 96px

Radius scale

Sample shapes at true corner radius. Buttons and inputs sit at the small end of this scale, feature cards at the large end.

  • lg0 · 0px
  • md0 · 0px
  • pill0 · 0px
  • sm0 · 0px
  • xl0 · 0px
  • xs0 · 0px
  • full9999px

Elevation

Shadow tokens on a neutral card, at true blur and spread.

  • inset0 0 #0000
  • lift0 0 #0000
  • overlay0 12px 24px rgb(0 0 0 / 0.16), 0 4px 8px rgb(0 0 0 / 0.08)
  • ring0 0 0 1px #121212
  • soft0 0 #0000
  • xs0 0 #0000

DESIGN.md

Ships in your repo as DESIGN.md. The designer agent reads it before it may introduce a new visual pattern. This is the briefing, not a page about the briefing.

Ledger draws the interface like a ruled book. One warm 1px hairline marks every edge, every corner is square, and actions are flat ink. A large serif carries the headlines and a one-weight sans carries everything else. Colour only ever reports a status.

Read this before you add a screen, a component or a colour. The tokens live in src/app/globals.css, the faces in src/app/fonts.ts, the kit in src/components/ui/.

At a glance

ThemeLight first: white canvas, warm grey lines, near-black ink. Dark is the same book on near-black with grey lines.
TypeHedvig Letters Serif at 24px and up, Hedvig Letters Sans below that, IBM Plex Mono for ids, amounts and numbers. Weight 400 everywhere.
ColourInk and warm greys. Green, amber and red appear only as a status.
ShapeRadius 0 on everything. Only avatars, radio dots and status dots are round.
DepthFlat. Hairlines do the work of shadows. Only menus, popovers, dialogs, sheets and toasts cast one.
SignatureColumn rules and a warm tray in the hero, numbered section heads, ruled grids that share edges, and a double rule under every total.

Principles

  1. One 1px warm hairline marks every edge. Need more separation? Add space, not depth.
  2. Every corner is square. Round is only for people and status dots.
  3. The serif only gets big: 24px and up. Everything smaller is the sans.
  4. Colour only reports a status. Everything else is ink and grey.
  5. Hover darkens a line to ink. Nothing lifts or glows, and a borderless item gets only a quiet warm fill.

Colour

Palette

TokenLightDarkRole
canvas (--background)#ffffff#0d0d0dThe page floor
surface-soft#f7f6f4#121212Section bands, sidebars, table heads
surface-card (--muted)#f3f2ef#1c1c1cTinted fills: inactive tabs, hover rows, code
surface-strong#e6e4e0#232323Pressed fills, progress tracks
surface-solid (--popover)#f7f6f4#121212Menus, selects, toasts: always opaque
card#ffffff#0d0d0dThe card fill
surface-dark#121212#161616The inverted panel
hairline (--border)#dbdad7#262626Every border and divider
ink (--foreground)#121212#fafafaHeadings and primary text
body-strong#242424#d4d4d4Emphasised body text
body#4a4a4a#a1a1a1Running text
muted (--muted-foreground)#616161#878787Secondary text, captions
muted-soft#8a8a8a#6b6b6bPlaceholders, disabled text, icons at rest
primary#18181b#fafafaThe action colour
accent (--accent-fill)#121212#fafafaThe saturated accent: links, the accent badge
accent-soft#ebeae7#1c1c1cThe tint an accent label sits on
danger (--destructive)#a43b36#ff383cDestructive actions and errors

success, warning and error each come with a -soft tint for the back of a badge or an alert: #006e30, #785a00 and #a43b36 in light, #22c55e, #eab308 and #ff383c in dark.

Ledger's own variables, read by the landing hooks and the kit: --ground (#f3f2ef light, #161616 dark).

How colour is used

  • Ink (--foreground, near-black) is text, primary buttons, focus outlines and every hover edge. The accent is ink too, so a featured thing gets an ink edge, not a colour.
  • Warm greys are structure: --border for every hairline, --input one step firmer on fields, --surface-soft and --muted for quiet fills.
  • --ground is the flat warm tray a framed thing sits on: the hero's product tray and the showcase pictures.
  • Green, amber and red appear only as a status: a tinted back, a coloured label and a 6px dot. Never a full fill.

Charts

accent-1 to accent-6 (also chart-1 to chart-6) are six series colours ordered so neighbours stay distinguishable. The six chart colours are greys from ink to pale, told apart by lightness alone. Past three series, label lines directly and dash a forecast. Assign them in order; never let colour be the only way to tell two series apart.

Dark mode

The palette is declared twice in src/app/globals.css: light on :root, dark under @media (prefers-color-scheme: dark) and again on .dark, where the class wins in both directions. @theme inline points every utility at those variables, so bg-canvas is one class that follows the mode. src/lib/design.ts sets the theme a first visit opens in: light here, because Ledger is drawn in light first. The theme menu in the header offers light, dark and system and remembers the choice.

Type

Faces

  • Hedvig Letters Serif (font-display): headings and display type.
  • Hedvig Letters Sans (font-body): running text and interface copy (weights 400).
  • IBM Plex Mono (font-mono): code, ids and tabular labels (weights 400, 500).

Hedvig Letters Serif is a warm, sturdy text serif that holds at 72px, and its optical size axis gives the few 18px uses a sturdier cut. Hedvig Letters Sans is its one-weight companion, so emphasis is size, colour and case, and IBM Plex Mono gives ids, amounts and numbers plain, slightly typewritten figures.

Every face loads through next/font/google in src/app/fonts.ts: downloaded at build time and served from this app. Change a face there and here, together.

Scale

TokenSizeLineTrackingWeightUse
text-display-xl72px1-0.025em400The hero headline, desktop
text-display-lg56px1.04-0.025em400The hero headline, tablet
text-display-md40px1.1-0.02em400Section headlines, the hero on phones
text-display-sm32px1.15-0.015em400Section headlines on phones, big numbers
text-title-lg24px1.330400Page titles in the app
text-title-md18px1.450400Card titles
text-title-sm16px1.50400Small headings, table titles
text-body-lg18px1.560400Lead paragraphs
text-body-md15px1.60400Running text
text-body-sm14px1.50400Interface copy, table cells
text-caption12px1.50400Metadata, helper text
text-caption-uppercase11px1.50.08em400Eyebrows above headings
text-button14px10400Buttons
text-nav-link14px1.40400Header and footer links

Rules: one h1 per page. Display sizes are for marketing pages; the app tops out at text-title-lg. Headings at title-lg and up are the serif at 400; a heading set smaller falls back to the sans through the base layer. Small labels are 11px capitals at 0.08em. Stats, prices and table cells use tabular figures, and ids and amounts use the mono.

Space and layout

  • Spacing tokens: xxs 4px, xs 8px, sm 12px, md 16px, lg 24px, xl 32px, xxl 48px, section 96px (the gap between landing sections).
  • Content sits in max-w-content (1280px) with px-md on phones and px-lg from sm up. Reading text stops at max-w-measure (672px).
  • Generous space, small type: 96px between sections, 24 to 32px inside a cell, 14 to 15px text, 11px labels.
  • Neighbours share one edge (negative 1px margins), so two boxes never draw a 2px line.
  • Section heads sit on the left edge of the column. The hero stays centred.
  • The hero sits in an 80rem column ruled on both sides; below that width the side rules drop.

Shape and depth

TokenValueWhere
rounded-xs0Not used by the kit
rounded-sm0The landing page
rounded-md0Toaster, the landing page
rounded-lg0The landing page
rounded-xl0The landing page
rounded-pill0The landing page
rounded-full9999pxAvatar, Badge, RadioGroup

Every radius token is 0, so stack code that asks for a pill comes out square. rounded-full stays for avatars, radio dots and status dots. Shadows xs, soft, lift and inset are empty. The overlay shadow is real and only for things that float, and focus is a square 2px ink outline 2px off the edge.

Components

Every component lives in src/components/ui/ and is written against the tokens above. Import from there; never restyle a kit component inline to make a one-off.

ComponentIn Ledger
ButtonSquare and flat, 14px sans at 400, sentence case, with a 1px border on every variant so sizes match. Primary is an ink block; outline is a hairline that turns ink on hover; danger keeps its red in the label until hover.
BadgeA square 20px chip with a 1px border and 12px sans. Neutral is a hairline, accent is an ink edge, and status badges add a soft tint and a 6px round dot.
CardSquare and flat in every tone. Default is the canvas colour with a hairline, soft and strong are warm fills with no border, and dark is near-black. Titles are the 18px sans.
Input, Textarea, Select40px square fields with a firm --input line and the canvas fill. Focus turns the line ink and adds a 1px ink ring for a 2px edge, with no halo. Select content is a square hairline menu with the overlay shadow.
Checkbox, RadioGroup, SwitchA 16px square checkbox that fills ink when checked. The radio is the one round control, with an ink dot. The switch is a square track with a square thumb that slides 16px.
TabsDefault is a warm tray where the active tab is a canvas sheet with a hairline. Line tabs sit on a hairline that turns ink under the active tab.
AccordionItems sit on hairline rules that turn ink on hover and when open. The question is the 16px sans, and a muted chevron turns over when an item opens.
Dialog, AlertDialog, SheetA warm 6px tray holds a square hairline sheet over a flat scrim with no blur, and the dialog footer sits under a 4px tray rule. Titles are the 24px serif. Sheets are square canvas panels with a hairline on their inner edge and a ruled header and footer.
DropdownMenu, Popover, Select content, TooltipSquare hairline surfaces with the overlay shadow. Items highlight with a warm fill, labels are small capitals, shortcuts are mono. The tooltip is a tiny inverted ink tag with no arrow.
TableRuled both ways like a ledger page: caps heads on a soft band, a column rule between cells, tabular figures, and a double ink rule above the footer total.
AlertA square panel with a 16px icon. Info is soft grey; success, warning and danger use a tint, a light status edge and a coloured icon. No side bar, no shadow.
Avatar, Progress, Skeleton, Spinner, Separator, Kbd, Breadcrumb, Pagination, Field, Label, ToasterAvatars are round; everything else is square. Progress is a 4px ink bar, separators are hairlines, Kbd is a mono key on a soft fill, pagination is square ghost cells with the current page outlined, labels are the 14px sans, and toasts are hairline panels with a status icon.

The landing page

The landing page is the same in every design; the design decides how it looks. Its sections carry data-slot hooks (listed at the top of src/components/marketing/section-heading.tsx) that globals.css may style. Ledger styles these:

  • hero-backdrop: the wash is gone. Two column rules on the 80rem column drop from the header hairline to the tray; below that width they drop out.
  • hero-visual: a warm --ground tray with a hairline, its sides on the column rules.
  • product-preview: a white, square, flat window on the tray, with a soft title bar.
  • hero-eyebrow: a square hairline tag with a 6px green status dot, the only colour in the hero.
  • section: every section opens on one full-width hairline. The one right after the hero and the logo strip drop it.
  • section-heading and section-eyebrow: heads sit on the left, and their eyebrows count in mono, 01 to 06. Showcase rows count 02.1, 02.2, 02.3.
  • showcase-row and showcase-visual: entries split by one hairline, each picture on the warm ground.
  • feature-grid and testimonial: ruled tables with shared 1px edges and no gaps. A hovered feature cell raises its edge to ink, icons are outlined squares, and quotes are the 20px serif.
  • stat-grid: big figures between a hairline above and a 3px double ink rule below, with caps labels.
  • step-marker: outlined ink squares with a mono numeral.
  • faq: the rule under an open or hovered question turns ink.
  • logo-cloud and logo: one ruled band of greyscale logos, with text wordmarks in the 24px serif, muted.
  • cta-panel: a flat near-black slab with no wash and an inner grey frame 12px in.
  • site-header, site-nav, brand-mark, site-footer: an opaque header with no blur, an ink underline on the current page, a serif initial in the mark, and a footer that opens on a double hairline.

Everything else is type, space and the kit.

Signed-in screens

The dashboard is a white canvas with a warm off-white sidebar split by a hairline, a 24px serif page title, hairline cards and tables ruled both ways with mono amounts and a double rule above totals. Sign-in is one white square card with a hairline on the warm ground, a serif title, 40px fields and a full-width ink button.

Do and don't

Do

  • Separate regions with one 1px border-hairline. If they need more separation, add space, not depth.
  • Keep every corner square. Use rounded-full only for avatars, radio dots and status dots.
  • Set headlines at text-title-lg and up in font-display, and everything smaller in the sans.
  • Put small labels in tracked capitals: text-caption-uppercase uppercase text-muted.
  • Show a status with its colour and a 6px dot (<Badge variant="success">). Keep everything else ink and grey.
  • Set ids, amounts and keys in font-mono tabular-nums, and right-align money columns.
  • Answer hover on a bordered thing by darkening its line (hover:border-ink), not by filling or lifting it.
  • Close a column of figures with the total rule (border-t-[3px] border-double border-ink) above the total.

Don't

  • Don't put a shadow on anything that does not float. shadow-overlay is for menus, popovers, dialogs, sheets and toasts only.
  • Don't round a button, card, input, badge, image or tab. No rounded-md, no rounded-lg.
  • Don't add a brand colour, a gradient, a glow or a backdrop blur.
  • Don't reach for font-medium, font-semibold or font-bold. They render at 400 here. Use size, colour or capitals.
  • Don't fill a whole block with a status colour. Tint the back (bg-success-soft), colour the label and the dot.
  • Don't use dashed or dotted rules. The only dashed line is the empty state's placeholder.
  • Don't stack two lines where one will do. No bordered card inside a bordered panel; let neighbours share an edge (-ml-px, -mt-px).
  • Don't paste hover:bg-accent hover:text-accent-foreground as is. Here the accent is ink, so use hover:bg-surface-card hover:text-ink.

Working in this design

Pasting from shadcn/ui. The token layer carries shadcn's variable names, so most snippets render unmodified. Two utility names mean something else here: bg-muted becomes bg-surface-card, and bg-accent / text-accent-foreground (a hover surface upstream) become bg-surface-card / text-ink. Here muted is a text colour and accent is the saturated accent.

Adding a token. Add it to all three palette blocks in globals.css (light, the dark media query, .dark), map it in @theme inline, and document it in the palette table above. Check contrast in both modes.

Adding a component. Check the kit first: most needs are a variant of Button, Card or Badge, or a composition on the page. A new component gets its own file in src/components/ui/, token classes only, a data-slot, and a row in the Components table here. The designer subagent owns this.

Build a repo in Ledger

The builder opens with Ledger picked. Pick your batteries and the design is applied to every template before the zip is built.