Skip to content

Design system

The tokens, in public.

Lanai's design system is the same across iOS, iPadOS, macOS, Vision Pro, and the marketing site. The tokens you see below are the ones the app uses. The source of truth is the SwiftUI DesignSystem package; the web mirrors it through CSS custom properties named --ds-*. The deep-dive accessibility as design and the synced DESIGN_SYSTEM.md hold the longer version.

Tokens

Every visual decision in the app routes through a named token. Colors, type, spacing, motion, radii, shadows. The Swift names and the CSS names match deliberately — DesignSystem.Colors.textPrimary in the app is --ds-color-textPrimary on the web. A change in one is a change in both.

Source of truth
src/styles/tokens.css (web) · Packages/DesignSystem/Sources/DesignSystem/Tokens/ (app)
Naming
--ds-{category}-{tokenName}. camelCase for compound tokens (textPrimary, bgCanvas).
Theme selection
data-theme on <html>. With no override, prefers-color-scheme drives the default — light to Day, dark to Late Night.

Typography

Three families. Three weights you'll see often. A ramp that holds up from the smallest Dynamic Type size to AX5. Body type is anchored at 17pt — the iOS minimum we treat as a floor rather than an aspiration.

Pull up a chair. The kettle is on.

.caption ds-font-caption · ds-text-caption · ds-weight-regular Timestamps and metadata

Pull up a chair. The kettle is on.

.label ds-font-label · ds-text-label · ds-weight-regular Tab labels, section headers

Pull up a chair. The kettle is on.

.body ds-font-body · ds-text-body · ds-weight-regular Timeline body — the 17pt floor

Pull up a chair. The kettle is on.

.bodyEmphasis ds-font-body · ds-text-bodyEmphasis · ds-weight-semibold Author names, button labels

Pull up a chair. The kettle is on.

.identity ds-font-identity · ds-text-identity · ds-weight-regular App name; accent moments

Pull up a chair. The kettle is on.

.title3 ds-font-display · ds-text-title3 · ds-weight-regular Card titles

Pull up a chair. The kettle is on.

.title2 ds-font-display · ds-text-title2 · ds-weight-regular Section heads

Pull up a chair. The kettle is on.

.title1 ds-font-display · ds-text-title1 · ds-weight-regular Page heads

Pull up a chair. The kettle is on.

.display ds-font-display · ds-text-display · ds-weight-regular Hero blocks

Pull up a chair. The kettle is on.

.displayLarge ds-font-display · ds-text-displayLarge · ds-weight-regular The thesis sentence

Editorial typography, on the page.

Six small things a good typesetter would notice — rendered live in the page you're reading, not described.

  1. A line of body type wants room to breathe. Sufficient leading and well-mannered hyphenation keep long words from sprinting into the margin while shorter ones still walk at the pace of the rest of the paragraph.

    Generous leading & hyphenation Reading Mode · Image Export

  2. "The opening quote belongs in the margin, so the first real letter sits flush with the body text underneath it. The eye prefers it that way, even when it can't say why."

    Hanging punctuation Reading Mode · Image Export

  3. Two adjacent characters that get confused all the time — the em dash and the hyphen-minus — behave very differently in running text. Lanai uses the right one in display, every time.

    Real em dashes Timeline cleanup · Tier 3

  4. “It’s the smallest thing,” she said, “and the first thing I notice.” The keyboard gives you straight quotes; the page gives them back curly, with the apostrophes you'd write yourself if you had time.

    Curly quotes & apostrophes Timeline cleanup · Tier 3

  5. A reading surface is just a room that respects the words inside it.

    Pull-quote treatment Reading Mode · Image Export

    • Posted11:14 AM
    • Reply11:47 AM
    • Quote2:08 PM

    Tabular numerals Everywhere a column of figures meets a column of figures

Color

Five themes, designed at equal polish. Every theme's primary text token on its primary canvas meets APCA Lc≥75 — the body-text floor. The High Contrast theme targets Lc≥105. Values below are computed at build time from the canonical hex values; the same numbers ship in the app.

Day

The default. A porch in late morning.

bgCanvas#FBFAF6Page background
bgSurface#FFFFFFPosts, cards
Pull up a chair.
textPrimary#1A1A1FBody text on canvas Lc 101 · Body text OK
@lanai.bsky.social
textSecondary#5C5C66Handles, timestamps Lc 80 · Body text OK
accent#4A7FB8Links, primary actions Lc 66 · Headings only

Dusk

Dark mode default. A porch at twilight.

bgCanvas#0F0E0CPage background
bgSurface#18171APosts, cards
Pull up a chair.
textPrimary#F2EFE7Body text on canvas Lc 97 · Body text OK
@lanai.bsky.social
textSecondary#A39E92Handles, timestamps Lc 50 · Secondary text
accent#7AA8D6Links, primary actions Lc 53 · Secondary text

Miami

Warm cream. Mid-century light.

bgCanvas#F5EDE0Page background
bgSurface#FBF6ECPosts, cards
Pull up a chair.
textPrimary#2B2A28Body text on canvas Lc 91 · Body text OK
@lanai.bsky.social
textSecondary#7A6E5EHandles, timestamps Lc 64 · Headings only
accent#4A8D8CLinks, primary actions Lc 55 · Secondary text

Late Night

OLED black. A single muted-gold accent.

bgCanvas#000000Page background
bgSurface#0A0A0APosts, cards
Pull up a chair.
textPrimary#F0EBE0Body text on canvas Lc 95 · Body text OK
@lanai.bsky.social
textSecondary#8A857DHandles, timestamps Lc 37 · Decorative only
accent#C9A961Links, primary actions Lc 58 · Secondary text

High Contrast

Maximum legibility. As crafted as the others.

bgCanvas#FFFFFFPage background
bgSurface#FFFFFFPosts, cards
Pull up a chair.
textPrimary#000000Body text on canvas Lc 106 · High contrast
@lanai.bsky.social
textSecondary#1A1A1AHandles, timestamps Lc 104 · Body text OK
accent#0050C8Links, primary actions Lc 83 · Body text OK

Motion

Long, gentle transitions. Crossfades over snaps. Easing curves that decelerate into rest. Three duration tokens cover almost every motion in the app. prefers-reduced-motion is honored at every site — springs become crossfades, durations collapse to near-zero, the visual rhythm survives.

The easing curve is cubic-bezier(0.25, 0.1, 0.25, 1) — Apple's iOS 17+ smooth curve, slightly easier than easeInOut. No bouncy springs in v1.0; the porch is calm.

Accessibility

Body type starts at 17 pt and only goes up. Touch targets are 44 pt at default, 56 pt at AX3 and above. Default contrast clears APCA Lc≥75 in every theme; High Contrast targets Lc≥105. Hyperlegible mode swaps the body and display typefaces for Atkinson Hyperlegible Next. Every animation has a reduced-motion path built in from the start.

The longer essay is at accessibility as design. The synced engineering spec is src/content/engineering-source/design-system.md; this page is its public mirror.