Design system

How LetzAI should look and behave in the product.

v2 · September 2026 · Public brand guidelines

Preview theme

Principles

  1. Monochrome. Ink is the accent (--accentcolor = --color). Colour comes from the work — images and video — never from chrome. Green, red and amber mean feedback and nothing else.
  2. One primary per surface. Only the single action the surface exists for is filled with ink: Send, Share. Everything else is a ghost.
  3. Ghost, tint, stronger tint. Controls are transparent at rest, take --control-hover-bg on hover and --control-active-bg when chosen. No borders, no underlines, no grey blocks.
  4. Solid surfaces. A surface is a hairline and one shadow (--elevation-*), not glass.
  5. Two typefaces. Space Grotesk for marketing headlines, never in all caps. Inter for everything in the product.
  6. One icon set, tokens only. Icons come from components/icons; colours, sizes and radii come from styles/tokens/. No hex in component CSS.

Where things live

Tokensstyles/tokens/
Controlsstyles/components/app-controls.css
Shared UIstyles/components/app-*.css
Iconscomponents/icons
Popup header, ⋯ menucomponents/shared/PopupHeader, MenuButton
Brand filespublic/brand/

Legacy — keeps working, don't extend

.stdbutton / .altbutton.app-btn / .app-btn--primary
FontAwesomecomponents/icons
Frosted glass (.app-glass-*, --blur-*)Solid surface + --elevation-*
Classic canvas chrome (.cv-*)?ui=next (canvas-next.css)
Marketing purple (--marketing-accent)Ink on black, as on the landing
New rules in styles.cssA file in styles/components/

Color

Live values for the preview theme. The media stage stays near-black in both themes so letterboxing never tints the picture.

Ink

--colorPrimary text, the accent
--text-secondarySecondary text
--text-tertiaryHints, meta, labels

Surfaces

--bgPage
--surface-raisedComposer, cards
--popupbgMenus, popups
--hairlineDividers, surface edges
--bordercolorOutlined controls

States

--control-hover-bgHover
--control-active-bgChosen
--stdbuttonbgPrimary fill
--stdbuttoncolorPrimary text

Media stage (both themes)

--media-stage-bgViewer stage
--media-stage-inkControls on the stage

Feedback only

--color-successSuccess
--color-errorError
--color-warningWarning

Type

Marketing headlines are Space Grotesk 600 with tight tracking, as on the landing page, and never in all caps. The product is Inter throughout: headings are steps in size and weight of the same family.

Marketing

Create what you love.

HeroOne per page, the landing heroSpace Grotesk 600 · --text-display-hero · --tracking-display-hero

Built for professionals.

SectionLanding section titlesSpace Grotesk 600 · --text-display-section · --tracking-display-section

Generate the shots, cut them, export the film.

BodyEverything under a headlineInter 400 · 1rem–1.25rem · muted ink

Product

Create what you love.

Page greetingThe one title of a page (Create)Inter 500 · 24–30px · -0.02em

Enter your image prompt

ComposerThe prompt fieldInter 400 · 17px · 1.55

I generated three looks and put them in a slideshow beside the chat.

ReadingChat replies, long copyInter 400 · 15.5px · 1.7 (--chat-text)

Generations Upscales Assets

ControlButtons, tabs, menu and nav rowsInter 500 · 13px

2 min ago · 4 images

MetaTimestamps, counts, hintsInter 400 · 12px · --text-tertiary

Recents

Section labelGroups in sidebars and menusInter 600 · 11px · uppercase · 0.04em · --text-tertiary

Space & shape

Spacing — 4px grid

--space-14px
--space-28px
--space-312px
--space-416px
--space-624px
--space-832px
--space-1248px
--space-1664px

Radius

--control-radius8px · Every control
--radius-xl12px · Menus, floating chrome
--radius-2xl16px · Composer card
--radius-fullfull · Avatars, status dots

Elevation — a hairline and one shadow

--elevation-raisedComposer, cards
--elevation-floatPopups, docked bars
--elevation-menuMenus

Page widths

Pick one: --page-narrow 720px for linear pages, --page-default 960px for entity pages, --page-nav 1200px for settings and hubs, --page-wide 1440px for dense grids. Long copy inside any of them sits on --page-column 800px. Never invent a width.

Icons

One line set on a 24-unit grid with a 1.5 stroke, drawn at 12, 14, 16 or 24px. Icons take currentColor, so they follow the control they sit in. Click an icon to copy its component.

12px14px16px24px

Buttons & tabs

One height (--control-height-md 34px, --control-height-sm 28px for chips and card overlays), one radius (--control-radius 8px), 13px Inter 500. Keyboard focus is --focus-ring.

Buttons
.app-btn · .app-btn--primary · .app-btn--sm · .app-icon-btn · .app-icon-btn--sm
Tabs
.app-tabs · .app-tab[aria-selected] — the chosen tab gets a tint, never an underline

Fields

Text
.app-input · textarea.stdtextarea (--compact · --longform)
Choose one
AppListSelect — never a native <select> · .app-segmented

Menus & popups

Every popup closes in the same place: the X, last on the right, labelled “Close (Esc)”. Escape closes the top layer only — an open menu first, then the popup.

Popup header
Editorial slideshow
PopupHeader — what you're looking at on the left, actions on the right, the close X always last · MenuButton for the one ⋯ menu

Feedback

Toast
showToast() from lib/toastStore — the link is a word inside the sentence, not a button beside it
Inline
Your first job is queued.
The upload failed. Try again.
DraftReadyQueuedFailed
.app-alert--* · .app-chip--* — colour is for feedback, nothing else
Empty

No bookmarks yet

Saved images and videos show up here.

.app-empty · .app-empty-title · .app-empty-body — one line, no illustration

Tokens

TokenDark valueUsed by
--colorText, primary fill, focus ring
--text-secondaryControls at rest
--text-tertiaryMeta, section labels
--bgPage
--surface-raisedComposer, cards
--popupbgMenus, popups
--hairlineDividers, surface edges
--control-hover-bgHover
--control-active-bgChosen tab or option
--stdbuttonbg.app-btn--primary
--focus-ringKeyboard focus
--media-stage-bgMedia Modal, canvas rooms
--font-sansInter — all product UI
--font-titleSpace Grotesk — marketing headlines
--text-display-heroLanding hero
--text-display-sectionLanding sections
--control-height-mdButtons, tabs, fields
--control-height-smChips, card overlays
--control-radiusEvery control
--radius-xlMenus, floating chrome
--radius-2xlComposer card
--space-2Gaps inside a control row
--space-4Default gap
--space-8Between sections
--elevation-raisedComposer, cards
--elevation-floatPopups, docked bars
--elevation-menuMenus
--duration-fastHover and press
--duration-normalMost transitions
--ease-defaultEvery transition
--page-narrowLinear pages
--page-defaultEntity pages
--page-navSettings, hubs
--page-wideDense grids
--page-columnReading column

The theme is data-theme="light" | "dark" on <html>, stored in localStorage.theme for signed-in visitors. Pages for visitors without a session are always dark. The switch above only changes this page.