Slab
Warm paper, black ink and a light slab serif. Hairlines instead of shadows, rust only as text.
- Light by default
- 35 colours · 14 type steps
- Inspired by town.com
The same generated app in every design. Scroll inside the window to see the whole page.
What it is
Slab sets every heading in a light slab serif over warm olive paper, with body copy in a quiet grotesque tracked slightly open. White cards sit on the paper, held by ink hairlines, never by shadows, and product pictures are white plates in a 1px ink frame. Buttons, checkboxes, switches and the selected tab fill with ink, and the one colour, a rust red, appears only as text, underlines and the focus ring. A felt-tip yellow marks the hero label and selected text. Dark mode is a warm near-black page that keeps the same rules.
Maintained by @agentic-studio in the open registry.
Best for
Calm, trust-first products where reading matters more than chrome: assistants, writing and knowledge tools, careful B2B software. Teams that want the product to feel printed and considered, not glossy.
Trade-offs
- The light slab headline has a strong literary voice. It can feel slow or old-fashioned for a playful consumer app.
- No shadows and one rare colour means hierarchy rests on type, space and lines. A crowded screen needs better layout, not more decoration.
- The paper is tinted warm olive, so pure white screenshots and logos show their edges. Roboto Slab also stands in for a paid display face with more character.
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
#a5380b
accent-1
#d35d34
accent-2
#497032
accent-3
#2b6f9c
accent-4
#b88b35
accent-5
#5f5dbc
accent-6
#6f7069
accent-hover
#8a2d07
accent-soft
#f6ede5
primary
#1c1b1b
Surface
canvas
#fdfdfc
hairline
#dcddd7
surface-card
#f2f3ed
surface-dark
#1c1b1b
surface-dark-elevated
#272825
surface-soft
#f7f8f4
surface-solid
#ffffff
surface-strong
#e9eae4
Text
body
#3a3b37
body-strong
#272825
ink
#1c1b1b
muted
#6b6c68
muted-soft
#90918d
on-accent
#ffffff
on-dark
#fafaf9
on-primary
#ffffff
Semantic
error
#9f3e3b
success
#006e3d
warning
#7a5a00
Other
danger
#9f3e3b
danger-soft
#fbeceb
error-soft
#fbeceb
primary-hover
#31322e
success-soft
#e8f4eb
warning-soft
#f5f0e4
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
- 5rem · 80px · 300 · lh 1.02 · ls -0.025em
Ship faster
- Token
- display-lg
- Values
- 4rem · 64px · 300 · lh 1.04 · ls -0.025em
Ship faster
- Token
- display-md
- Values
- 3rem · 48px · 300 · lh 1.08 · ls -0.022em
Ship faster
- Token
- display-sm
- Values
- 2.25rem · 36px · 300 · lh 1.12 · ls -0.02em
Ship faster
- Token
- title-lg
- Values
- 1.5rem · 24px · 400 · lh 1.3 · ls -0.012em
Ship faster
- Token
- body-lg
- Values
- 1.125rem · 18px · 400 · lh 1.6 · ls 0.015em
Ship faster
- Token
- title-md
- Values
- 1.125rem · 18px · 400 · lh 1.4 · ls -0.005em
Ship faster
- Token
- body-md
- Values
- 1rem · 16px · 450 · lh 1.6 · ls 0.018em
Ship faster
- Token
- title-sm
- Values
- 1rem · 16px · 500 · lh 1.45 · ls 0.005em
Ship faster
- Token
- body-sm
- Values
- 0.875rem · 14px · 450 · lh 1.55 · ls 0.02em
Ship faster
- Token
- button
- Values
- 0.875rem · 14px · 500 · lh 1 · ls 0.02em
Ship faster
- Token
- nav-link
- Values
- 0.875rem · 14px · 450 · lh 1.4 · ls 0.02em
Ship faster
- Token
- caption
- Values
- 0.8125rem · 13px · 500 · lh 1.45 · ls 0.02em
SHIP FASTER
- Token
- caption-uppercase
- Values
- 0.75rem · 12px · 500 · lh 1.4 · ls 0.06em
Typefaces
The families the generated Tailwind theme points at. A licensed face is named here even when the substitute below is what actually renders.
- DisplayRoboto Slab, Rockwell, Georgia, ui-serif, serif
- BodyGeist, ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif
- MonoGeist 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
- section7rem · 112px
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.
- xs0.25rem · 4px
- sm0.375rem · 6px
- md0.5rem · 8px
- lg0.75rem · 12px
- xl0.875rem · 14px
- full9999px
- pill9999px
Elevation
Shadow tokens on a neutral card, at true blur and spread.
- inset0 0 #0000
- lift0 0 #0000
- overlay0 20px 44px -10px rgb(28 27 27 / 0.16), 0 2px 6px 0 rgb(28 27 27 / 0.05)
- ring0 0 0 1px rgb(28 27 27 / 0.9)
- soft0 0 #0000
- xs0 1px 2px 0 rgb(28 27 27 / 0.07)
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.
Slab is a well-printed book about software. Headings are a light slab serif in black ink on warm olive paper, and body copy is a quiet grotesque tracked slightly open. White cards and plates sit on the paper, held by hairlines and one line of full ink, never by shadows. Rust appears only as ink, and a felt-tip yellow marks the hero label.
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
| Theme | Light native on near-white paper (#fdfdfc) with a greener endpaper (#f2f3ed). Dark is a warm near-black page (#131410) that keeps the same rules. |
| Type | Roboto Slab at 300 for display, 400 for titles, 500 for small titles. Geist at 450 with +0.02em tracking for body. Geist Mono only for code, keys and ids. |
| Colour | Warm olive greys and black ink (#1c1b1b). One rust (#a5380b) used only as text, underlines and the focus ring. A felt-tip yellow (#f5d188) for the hero label and selected text. |
| Shape | Printed corners: 6px on controls, badges and tab segments, 12px on cards and menus, 14px on the big panels. Pills only for avatars, the switch, radios and progress. |
| Depth | Nothing on the page casts a shadow. Hairlines inside, one line of full ink outside. Only floating layers (menus, dialogs, toasts) get an overlay shadow. |
| Signature | A tilted felt-tip label over an 80px light slab headline, and the product window drawn as a white plate in a 1px ink frame on greener paper. |
Principles
- Selected means ink: the primary button, the checked box, the on switch, the active tab segment, the current page.
- Rust is ink, never paint: text, underlines, check marks and the focus ring. No rust fills.
- White on paper, lines not shadows. Only floating layers cast a shadow.
- Ink outside, hairlines inside: a picture or a table gets one frame of full ink, and everything in it is split by hairlines.
- Small boxes, not pills: badges and tabs are 6px boxes.
Colour
Palette
| Token | Light | Dark | Role |
|---|---|---|---|
canvas (--background) | #fdfdfc | #131410 | The page floor |
surface-soft | #f7f8f4 | #171814 | Section bands, sidebars, table heads |
surface-card (--muted) | #f2f3ed | #232420 | Tinted fills: inactive tabs, hover rows, code |
surface-strong | #e9eae4 | #2c2d29 | Pressed fills, progress tracks |
surface-solid (--popover) | #ffffff | #1d1e1a | Menus, selects, toasts: always opaque |
card | #ffffff | #181915 | The card fill |
surface-dark | #1c1b1b | #0c0d0a | The inverted panel |
hairline (--border) | #dcddd7 | #2f302c | Every border and divider |
ink (--foreground) | #1c1b1b | #fafaf9 | Headings and primary text |
body-strong | #272825 | #e4e5e0 | Emphasised body text |
body | #3a3b37 | #c6c7c2 | Running text |
muted (--muted-foreground) | #6b6c68 | #8d8e8a | Secondary text, captions |
muted-soft | #90918d | #6e6f6a | Placeholders, disabled text, icons at rest |
primary | #1c1b1b | #fafaf9 | The action colour |
accent (--accent-fill) | #a5380b | #e2764a | The saturated accent: links, the accent badge |
accent-soft | #f6ede5 | #2b1f18 | The tint an accent label sits on |
danger (--destructive) | #9f3e3b | #e07871 | Destructive actions and errors |
success, warning and error each come with a -soft tint for the back of a
badge or an alert: #006e3d, #7a5a00 and #9f3e3b in light, #44aa6f, #bd9023 and
#e07871 in dark.
Slab's own variables, read by the landing hooks and the kit: --border-strong
(#c3c4be light, #41423f dark), --border-ink (#1c1b1b light, #7c7d78 dark),
--marker (#f5d188 light, #57461a dark), --page (#f2f3ed light, #1b1c18
dark).
How colour is used
- Ink (
bg-primary,text-ink) fills every selected state and the primary button. White (bg-card) is every card, field and plate on the paper. - Rust (
text-accent) marks eyebrows,linkbuttons, check marks, link underlines and the current nav underline, and the focus ring is rust. It is never a fill. - The greener paper (
--page) sits under the features chapter, the showcase mats, the hero shelf, the product title bar and the footer. Text on it staysmutedor darker. - The felt-tip yellow (
--marker) appears once per screen: the hero label, plus selected text. - Green, ochre and red mark status only, as soft tints with a coloured label or icon.
Charts
accent-1 to accent-6 (also chart-1 to chart-6) are six series colours
ordered so neighbours stay distinguishable. Six series run terracotta, olive,
blue, ochre, violet and warm grey, in that order, in both themes. The terracotta
is lighter than the rust, so data is the one place a rust-family fill appears.
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
Slab is drawn in light first. The theme menu in the header offers light, dark
and system and remembers the choice.
Type
Faces
- Roboto Slab (
font-display): headings and display type. - Geist (
font-body): running text and interface copy (with italics). - Geist Mono (
font-mono): code, ids and tabular labels.
Roboto Slab is a geometric slab with even strokes and a true light weight, so at 300 with tight tracking it gives a calm, printed shout. Geist has Swiss proportions that stay clean with open tracking at 13 to 16px, and Geist Mono shares its widths, so an id sits calmly inside a sentence.
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
| Token | Size | Line | Tracking | Weight | Use |
|---|---|---|---|---|---|
text-display-xl | 80px | 1.02 | -0.025em | 300 | The hero headline, desktop |
text-display-lg | 64px | 1.04 | -0.025em | 300 | The hero headline, tablet |
text-display-md | 48px | 1.08 | -0.022em | 300 | Section headlines, the hero on phones |
text-display-sm | 36px | 1.12 | -0.02em | 300 | Section headlines on phones, big numbers |
text-title-lg | 24px | 1.3 | -0.012em | 400 | Page titles in the app |
text-title-md | 18px | 1.4 | -0.005em | 400 | Card titles |
text-title-sm | 16px | 1.45 | 0.005em | 500 | Small headings, table titles |
text-body-lg | 18px | 1.6 | 0.015em | 400 | Lead paragraphs |
text-body-md | 16px | 1.6 | 0.018em | 450 | Running text |
text-body-sm | 14px | 1.55 | 0.02em | 450 | Interface copy, table cells |
text-caption | 13px | 1.45 | 0.02em | 500 | Metadata, helper text |
text-caption-uppercase | 12px | 1.4 | 0.06em | 500 | Eyebrows above headings |
text-button | 14px | 1 | 0.02em | 500 | Buttons |
text-nav-link | 14px | 1.4 | 0.02em | 450 | Header and footer links |
Rules: one h1 per page. Display sizes are for marketing pages; the app tops
out at text-title-lg. Headings are Roboto Slab at 300 from 36 to 80px, tracked
tight (-0.025em at the top). Titles are 400, small titles and the brand mark
500, never heavier and never italic. Body is Geist at 450 with +0.02em tracking;
that opposite pull is most of the look.
Space and layout
- Spacing tokens:
xxs4px,xs8px,sm12px,md16px,lg24px,xl32px,xxl48px,section112px (the gap between landing sections). - Content sits in
max-w-content(1280px) withpx-mdon phones andpx-lgfromsmup. Reading text stops atmax-w-measure(672px). - Spacing steps are 4, 8, 12, 16, 24, 32 and 48px, with 112px between sections.
- Density is generous: 40px controls against 14 to 16px text, and 24px card padding (32px on the sign-in card).
- Leave about half the first screen as empty paper. Space separates the showcase rows, not lines.
Shape and depth
| Token | Value | Where |
|---|---|---|
rounded-xs | 4px | Breadcrumb, Checkbox, Kbd |
rounded-sm | 6px | Accordion, Badge, Button, Dialog, Input, Sheet, Skeleton, Tabs, Toaster, the landing page |
rounded-md | 8px | DropdownMenu, Select, Tooltip, the landing page |
rounded-lg | 12px | AlertDialog, Alert, Card, Dialog, DropdownMenu, Popover, Select, Toaster, the landing page |
rounded-xl | 14px | The landing page |
rounded-pill | 9999px | Progress, Switch, the landing page |
rounded-full | 9999px | Avatar, RadioGroup, Switch |
Radii are 4px on checkboxes and keys, 6px on buttons, inputs, badges and tab segments, 8px on menu items and tooltips, 12px on cards, dialogs, menus and alerts, and 14px on the product window, the showcase mats and the CTA panel. Borders are always 1px solid, in four strengths: hairline, input line, strong line and full ink, plus pencil grey on checkbox and radio rings so they clear 3:1. Nothing on the page casts a shadow. Menus, popovers, dialogs, sheets and toasts float on an overlay shadow, and the switch thumb has a whisper.
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.
| Component | In Slab |
|---|---|
Button | Geist 500 at 14px with +0.02em tracking, sentence case, 6px corners, flat, and every press sinks 1px. Primary is an ink fill with a white label; secondary is a flat paper step; outline carries the input line and an ink label, and turns solid ink when it marks the current page; ghost washes ink at 5% on hover; danger is a soft red tint, never solid; link is rust text with a 1px underline on hover. Sizes are 32, 40 and 48px tall, and focus is a rust ring set off the paper. |
Badge | A 24px box with 6px corners, never a pill: a soft fill and a matching 13px Geist label, no line. Accent is rust on apricot paper, neutral is paper with body text, and status badges take their soft tint. The uppercase mode (12px, +0.06em) is the only place caps appear. |
Card | Flat, 12px corners, never a shadow. Default is white above the paper with a hairline; soft and strong are flat paper steps with no line; outline lets the paper show inside the input line; dark is the ink panel. Titles are the slab at 18px 400. |
Input, Textarea, Select | White 40px fields on the paper with the input line and 6px corners, no shadow. Hover darkens the line, focus turns it rust with a faint rust halo, and invalid turns it red. Select content is a white menu with 12px corners, an overlay shadow and an ink check on the chosen item. |
Checkbox, RadioGroup, Switch | A white 16px box with 4px corners and a pencil-grey line that fills with ink and takes a 12px tick when checked. A radio is a white circle with the same line; checked, the ring turns ink around an 8px ink dot. The switch track is pencil grey when off and ink when on, with a paper thumb and a whisper shadow. All three keep a 32px hit area and a rust focus ring set off the paper. |
Tabs | The default is a segmented control: a white shell with the input line and 10px corners, and the active tab a solid ink segment with no line or shadow. Idle tabs wash ink at 5% on hover. Line tabs mark the active tab with a 2px ink underline over a hairline. |
Accordion | Rows split by hairlines, no boxes. Triggers are the slab at 16px 500 in ink with a muted chevron that turns over, and hover draws a 1px rust underline; content is 14px body copy. |
Dialog, AlertDialog, Sheet | A white sheet in a 6px paper tray, with 12px corners, a hairline and an overlay shadow over a plain 45% dark scrim with no blur. Dialog titles are the slab at 24px 400, and the close button is a 32px ghost. Alert dialogs are narrower and pair an outline cancel with a primary or soft danger action. Sheets are square with one hairline on the inner edge, 24px padding and an 18px slab title. |
DropdownMenu, Popover, Select content, Tooltip | White surfaces with a hairline, 12px corners and an overlay shadow. Items are 32px with 8px corners and highlight to paper; checks are ink and shortcuts are muted Geist Mono. Tooltips are not inverted: a small white chip with a hairline, an overlay shadow, 13px ink text and an arrow that keeps the hairline. |
Table | No frame of its own. A paper header band with 36px cells and 13px muted labels in sentence case, then 48px rows split by hairlines that hover to paper. Numbers are tabular; set them right, and set ids in Geist Mono. |
Alert | A borderless tint with 12px corners. Info is neutral paper with an ink title and a muted icon; success, warning and danger take their soft tint with the title and icon in the status colour. |
Avatar, Progress, Skeleton, Spinner, Separator, Kbd, Breadcrumb, Pagination, Field, Label, Toaster | Avatars are circles with a hairline ring inside and initials on paper. Progress is a 6px pill with an ink bar, never rust. Skeletons are flat paper blocks that pulse, and spinners turn in the colour of the text around them. Separators are 1px hairlines. Kbd is Geist Mono on white with the input line and no bevel. Breadcrumbs split with a thin chevron and end on the current page in ink at 500, and the current page in pagination is an ink box. Labels are 14px 500 in ink, sentence case, and field legends are the slab. Toasts are white with a hairline and 12px corners and show status only in the 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.
Slab styles these:
site-header: drops the blur and the full-width strip. Its bar is white with one hairline and 10px corners, floating 8px below the top with paper showing around it.site-nav: the current page loses its wash and gets a thin rust underline.brand-mark: an ink square with a slab letter at 500.hero-backdrop: no wash, grid or glow. It becomes a shelf of greener paper behind the lower half of the product window, opened by a hairline.hero-eyebrow: a felt-tip yellow swipe with uneven ends, tilted one degree, with ink text and a rust arrow. A linked label gets a rust underline on hover.hero-titleandsection-title: the light slab from the type scale with balanced lines, and no added weight or colour.product-preview: a white plate in one line of full ink, 14px corners, no shadow. A real hero screenshot gets the same frame.product-preview-bar: sits on the greener paper, with its dots and address field drawn as white controls with the input line.section: no section draws a bottom rule. Each one after the first, and the first one after the hero, opens on a full-width hairline, so the logo strip sits between two rules.section-eyebrow: rust sentence case at 15px, weight 500, no caps and no mono.sectionlogos: the title is a small sentence-case Geist caption, and eachlogois a muted slab wordmark.sectionfeatures: the one chapter on the greener paper.feature-cardcards take the strong line, andfeature-iconis a white tile with the strong line and a 1px ink icon.showcase-visual: a mat of greener paper with no line. The picture on it is a white plate in an ink frame.step-marker: white boxes framed in ink with slab numerals, joined by hairlines.stat-gridandstat-value: the one square panel, an ink frame with lighter inner rules and white cells, holding 48px light slab figures with tabular digits.testimonial: white cards with the strong line. The quote is a 20px light slab pull quote in ink.faq: ruled like a printed table, with an ink rule on top and at the bottom and hairlines between. Questions are 18px slab.- Featured plan: the highlighted plan card is outlined in 2px ink (its border plus an ink ring), never in rust.
cta-panel: the one ink panel, like a back cover. The darkest ink, one hairline, no wash and no shadow, with the dark palette inside.site-footer: the greener endpaper, with 16px slab column headings in sentence case.
Everything else is type, space and the kit.
Signed-in screens
The dashboard sits on the paper with a soft sidebar split by a hairline. The active row is paper with ink text, cards are white with hairlines, page titles are 24px slab and tabs are segmented with an ink active segment.
Sign-in is one white card on the paper with 12px corners, a hairline and 32px padding: a 24px slab title, white fields with the input line, a full-width ink button and rust text links.
Do and don't
Do
- Set every heading in Roboto Slab: 300 for display sizes, 400 for titles, 500 only for small titles and the brand mark.
- Keep body copy in Geist at +0.02em tracking and weight 450. The open tracking is part of the look.
- Put white cards on paper:
bg-cardonbg-canvasor on the greener--page, held by a line. - Mark every selected state with ink: primary buttons, checked boxes, the on switch, the active tab segment, the current page, progress bars.
- Keep rust for text and lines: eyebrows, link underlines, the eyebrow arrow, check marks, the current nav underline, the focus ring.
- Use the marker once per screen: the hero label, and text selection.
- Frame pictures and tables in one line of full ink (
--border-ink), with hairlines inside. - Draw every icon as a 1px line (stroke = 24 / size), in ink or
muted.
Don't
- Don't fill a button, badge or band with the rust. The first chart series is a lighter terracotta, and data is the only place a rust-family fill appears.
- Don't use a pill shape on buttons, badges, inputs or tabs. Pills are for avatars, the switch, radios and progress.
- Don't add a drop shadow to a card, button, input, tab or the product window.
Only floating layers get
shadow-overlay. - Don't set labels in capitals or tracked caps. The Badge
uppercasemode is the one exception. - Don't put a gradient, glow or grid on any surface, the hero included.
- Don't set the slab above 500 or in italic. It shouts by size, not by weight.
- Don't introduce a cool grey. Every neutral is warm olive.
- Don't use mono for eyebrows, buttons, nav links or badges.
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 Slab
The builder opens with Slab picked. Pick your batteries and the design is applied to every template before the zip is built.
