LetzAI Design System

Internal reference for the LetzAI product shell — typography, materials, and components that match canvas chrome.

Design system v1.7 · June 2026

Preview theme

Principles

LetzAI uses an Apple-like neutral palette. Hierarchy comes from opacity and weight, not color accents in the app shell.

  • Accent = ink. --accentcolor resolves to --color. No product purple in app chrome.
  • Marketing exception. Landing pages (.lp2) may use --marketing-accent, scoped to marketing CSS only.
  • Canvas exception. User strokes and picks can be any color; default focus/selection chrome uses ink.
  • Marketing heroes use --text-display-hero (landing .lp2-hero h1).
  • Canvas typography. In-product headings use the compact canvas scale (--text-page-title through --text-panel-label), not global h1h6.
  • Materials. Frosted chrome uses blur + translucent surfaces — not tinted glass.
  • Design artifacts. Sandboxed iframe designs use public/design-starters/*/tokens.css — separate from the app shell.

Control decision tree

App-wide actions.stdbutton / .altbutton in app-buttons.css
Canvas chrome.cv-* in canvas.css — toolbars, inspector, pills
DropdownsAppListSelect / .app-dropdown — not native <select>
Legacystyles.css patterns — migrate, do not extend

Color

Semantic tokens for the active preview theme. Semantic greens/reds are for feedback only — not decorative chrome.

Text hierarchy

--colorInk / primary text
--text-secondarySecondary text
--text-tertiaryTertiary text

Surfaces

--bgPage background
--verylightbgSubtle surface
--popupbgPopup / panel
--headerbgHeader
--surface-chromeFrosted chrome
--scrim-defaultModal scrim
--scrim-denseDense scrim (no blur)
--modalbgModal tint
--skeleton-baseSkeleton base

Interactive / chrome

--hairlineHairline border
--bordercolorBorder
--field-bgField background
--field-borderField border
--field-border-hoverField border (hover)
--buttonbgButton fill
--lighthoverHover fill
--stdbuttonbgPrimary fill (= ink)
--stdbuttoncolorPrimary fill text
--accentcolorAccent (= ink)
--focus-ringFocus ring
--control-font-sizeCanvas control text
--control-height-mdCanvas control height
--form-control-font-sizeForm field text
--textarea-height-compactCompact textarea
--textarea-height-defaultDefault textarea
--textarea-height-longformLong-form textarea
--textarea-max-height-defaultTextarea max height

Semantic (feedback only)

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

Typography

Marketing display for logged-out landing heroes; canvas scale for in-product UI. Titles use --font-title (Space Grotesk Semibold); body and UI copy stay on --font-sans (Inter).

Inter 400 body · 500 UI · Space Grotesk 600 titles

Logged-out landing scale — larger than in-app canvas titles. Space Grotesk Semibold, tight tracking.

Create anything.

Generate, edit, and organize on an infinite canvas.

Create anything.

Marketing heroheading-display-hero--text-display-hero.lp2-hero h1 · .heading-display-hero

Built for creative teams

Marketing sectionheading-display-section--text-display-section.lp2-section-title · .heading-display-section

In-app canvas scale

Seven steps from 2rem page titles down to 11px panel labels. Pick by structural depth: page list → settings section → top bar name → inspector group → uppercase row label — not by how “important” the copy feels.

Largest to smallest above. Page title is a step above everything else; chrome and chat sit between section copy and inspector labels. Panel labels are 11px, uppercase, secondary ink.

SampleRenderedClassTokenWhere
My Boards2.2rem2.2remcv-heading-page--text-page-title.cv-list-title · .app-page-title
Board settings20px20pxapp-section-title--text-section-titleSettings / modal sections
Sharing & access16px16pxapp-subsection-title--text-subsection-titleNested settings blocks
Untitled board14px14pxcv-heading-chrome--text-chrome-title.cv-topbar-title
Next steps13px13pxcv-heading-chat--text-chat-heading.cv-cc-md h1–h6
Layout13px13pxcv-heading-section--text-panel-title.cv-inspector-section-title
Layers12px12pxcv-heading-panel-label--text-panel-label.cv-layers-heading-row · uppercase

Spacing

4px base grid. Prefer tokens over raw pixel values.

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

Border radius

Radius is contextual: --radius-md for inspector fields, --radius-lg for top-bar controls, --radius-full for toolbars and selection pills.

--radius-nonenone
--radius-smsm (4px)
--radius-mdmd (6px)
--radius-lglg (8px)
--radius-xlxl (12px)
--radius-2xl2xl (16px)
--radius-fullfull

Shadows

--shadow-sm
--shadow-md
--shadow-lg
--shadow-xl

Materials & blur

Frosted chrome, modal scrims, and floating bars use tokenized blur values. Always pair -webkit-backdrop-filter with backdrop-filter for Safari.

Frosted chrome bar
Board title
Developer spec.app-glass-chrome · --surface-chrome · --blur-md
Modal scrim

Invite collaborators

Share this board with your team. The scrim blurs content behind it.

Developer spec.app-backdrop-scrim · --scrim-default · --blur-sm
Floating glass bar
Unsaved changes
Developer spec.app-glass-float · --blur-xl
When to skip blur

Heavy dialogs that re-render on every interaction should use a denser opaque scrim instead of full-screen blur (see canvas .cv-create-overlay).

Create organization

Dense fill, no backdrop-filter.

Developer spec.app-backdrop-scrim--dense · backdrop-filter: none

Motion

Hover the box — uses --duration-normal and --ease-default.

Buttons

Use .stdbutton for primary actions and .altbutton for secondary — compact density, one filled button per row.

Primary + secondary
Developer spec.stdbutton · .altbutton
Sizes
Developer spec.stdbuttonsmall · .stdbuttonbig
Disabled
Developer spec.stdbutton[disabled] · .stdbuttondisabled · .altbuttondisabled
Action row
Developer specone primary per row
Model badges
LG
Developer spec.model-badge--letzai

Canvas-specific chrome

Toolbar, inspector, and drawer controls use .cv-* classes where layout-specific.

Icon ghost
Developer spec.cv-icon-btn
Share actions
Developer spec.cv-share-btn · .cv-share-primary
Aspect pills
Developer spec.cv-section-aspect-pill
Primary CTA
Developer spec.cv-section-generate-btn
Floating toolbar
Developer spec.cv-toolbar · .cv-tool

Full canvas chrome catalog lives in styles/components/canvas.css.

Form controls

Text inputs use .app-input. Textareas use .stdtextarea: default for descriptions, .stdtextarea--compact for comments and short notes, and .stdtextarea--longform only for prompts, markdown, or code. Fields are outlined — they sit on --field-bg with a --field-border stroke (no grey fill); hover darkens the border. Field text uses --form-control-font-size; do not use body-copy sizing inside controls. Dropdowns use AppListSelect — not native <select>.

List select
Developer specAppListSelect · .app-dropdown
Property field
Developer spec.cv-prop-field
Textarea density
Developer spec.stdtextarea · --compact · --longform

Deprecated legacy

Deprecated patterns — do not use in new UI. Prefer .app-input and AppListSelect.

Text input
Developer spec.app-input

Search & selection

Search fields, custom list selects, segmented controls, and toggle switches.

Search field
Developer spec.app-search-input
List select

Native <select> menus are OS-rendered. Use AppListSelect for on-brand dropdown panels.

Closed
Open
Developer specAppListSelect · .app-dropdown (not native <select>)
Segmented control
Developer spec.app-segmented
Toggle switch
Off
On
Developer spec.switch · .slider

Feedback & status

Alerts, status chips, and mentions from app-feedback.css.

Alert banners
Board published successfully.
Generation queue is busy — expect a short delay.
Upload failed. Check file size and try again.
Invite collaborators from the Share panel.
Developer spec.app-alert
Status chips
PublishedPendingFailedDraft
Developer spec.app-chip
Mention

Use @landscape-lora or @model in prompts.

Developer spec.app-mention

Loading & empty

Skeleton loaders and empty states from app-layout.css.

Skeleton
Developer spec.app-skeleton · --skeleton-base
Empty state

No boards yet

Create a board to start generating and organizing assets.

Developer specAppEmptyState · .app-empty-state

Modals & overlays

Modal content on --popupbg with scrim from .app-backdrop-scrim.

Modal shell

Delete board?

This removes the board for all collaborators. Generated assets stay in your library.

Developer spec.app-modal-shell · --popupbg

App screen

Composed hierarchy — how chrome, headings, and actions fit together on one screen.

Summer campaign

Generate assets

One primary action per row. Secondary actions stay ghost or bordered.

Aspect ratio

Body text

Paragraphs, captions, lists, links, and inline code across the app shell.

Primary body

LetzAI helps teams generate, edit, and organize creative assets on an infinite canvas. Default paragraph style at comfortable reading size.

Developer specp · --color · --text-base
Secondary

Supporting copy, metadata, and helper text use secondary ink via opacity.

Developer spec--text-secondary
Tertiary

Timestamps, placeholders, and de-emphasized labels.

Developer spec--text-tertiary
Disabled

Unavailable or inactive field hints.

Developer spec--disabledcolor
Caption

Caption text — notifications, footnotes, table meta.

Developer spec--text-xs
Links

Read the API documentation or use a ghost link button.

Developer spec.textlink · .ghost-link-btn
Inline code

Set data-theme="dark" on <html> to switch themes.

Developer speccode
Lists
  • Generate images from natural-language prompts
  • Edit and upscale existing assets
  • Organize work on shared boards
Blockquote
Hierarchy comes from opacity and weight, not product color accents in the app shell.
Developer spec--text-secondary

Layout & dividers

Hairline dividers, cards, and popup surfaces. Page widths use the three shell tiers documented under Page shells.

Horizontal divider

Content above


Content below

Developer spec.app-divider · --hairline
Vertical divider
Developer spec.app-divider-vertical
Content card

Summer campaign

Updated 2 hours ago · 12 assets

Developer spec.app-card
Popup surface

Dropdown or panel on popup surface.

Developer spec--popupbg · --shadow-md

Focus & accessibility

Keyboard focus uses --focus-ring — never hardcoded outline colors.

Focus ring

Tab to the control — uses box-shadow: var(--focus-ring).

Developer spec--focus-ring · .app-focus-target
Input focus
Developer spec.app-input:focus-visible

Elevation & z-index

Staggered stack — higher z-index layers sit above lower ones.

--z-base(1)
--z-dropdown(50)
--z-studio(150)
--z-header(190)
--z-modal(200)

Logos

LetzAI logo lockupLogo lockup — header
LetzAI L iconL icon — compact mark
LetzAI logo lockup on lightLockup — on light

Maintain clear space equal to the height of the “L” mark. Do not stretch, recolor, or place on low-contrast backgrounds.

Do / Don't

Do

  • Add shared UI to styles/components/app-*.css
  • Use semantic tokens from styles/tokens/
  • Use canvas heading classes for in-app titles
  • Use .stdbutton / .altbutton for app-wide actions
  • Use AppListSelect for dropdown menus
  • Keep one filled primary per action row
  • Use opacity-based secondary text
  • Use --page-* shell tokens for page max-widths
  • Scope marketing purple to .lp2 only

Don't

  • Stack multiple .stdbutton fills in one row
  • Use global h1h6 in app UI
  • Use native <select> for product UI
  • Add purple accents to app shell
  • Add new patterns to styles.css
  • Hardcode hex in component CSS
  • Invent one-off page max-widths
  • Use flat #bbb for secondary text

Page shells

Logged-in content pages pick one shell tier. Long-form copy inside a shell centers on the reading column. Prefer .app-page (with .app-page--narrow / .app-page--wide) or point existing wrappers at these tokens — do not invent a new pixel width.

  • --page-narrow (720px) — linear single-purpose pages (notifications, simple forms)
  • --page-default (960px) — entity pages (settings, profile, organizations, board detail, edit org / edit board, model settings, subscription)
  • --page-wide (1440px) — dense browsing (pricing, boards grid; subscription plan cards break out only)
  • --page-column (800px) — reading column inside a shell (heroes, settings shell, section copy)

Theme API

  • document.documentElement.setAttribute("data-theme", "light" | "dark")
  • Persisted in localStorage.theme (default: dark)
  • .loggedout / .auth-page use a dedicated dark chrome palette
  • The switch on this page is preview-only — it does not change your app-wide theme.

Token reference

Live values from the preview theme (Dark). Switch theme above to compare. Section links jump to where each token is documented.

TokenDark valueUsed by
--colorGlobal text
--text-secondarySecondary text
--text-tertiaryTertiary text
--bgPage shell
--verylightbgSubtle surface
--popupbgPanels / dropdowns
--surface-chrome.app-glass-chrome
--scrim-default.app-backdrop-scrim
--stdbuttonbg.stdbutton fill
--stdbuttoncolor.stdbutton text
--hairlineHairline border
--focus-ringFocus states
--color-successSuccess feedback
--color-errorError feedback
--text-display-heroLanding hero
--text-display-sectionLanding section titles
--text-page-titleCanvas page titles
--text-section-titleIn-app sections
--text-panel-titleInspector sections
--text-panel-labelLayers / panel labels
--text-subsection-titleNested settings blocks
--text-chrome-titleTop bar board name
--text-chat-headingCanvas chat headings
--control-font-sizeCanvas controls
--control-height-mdTop bar buttons
--control-height-smCompact controls
--form-control-font-sizeInputs / textareas
--textarea-height-compactComments / short notes
--textarea-height-defaultDescriptions / bios
--textarea-height-longformPrompts / markdown / code
--textarea-max-height-defaultDefault textarea growth cap
--blur-smModal scrims
--blur-mdSite header chrome
--blur-lgCanvas top bar
--blur-xlFloating bars
--backdrop-saturate-chromeHeader saturate
--backdrop-saturate-canvasCanvas top bar saturate
--scrim-denseDense overlay (no blur)
--radius-smSmall corners
--radius-mdInspector fields
--radius-lgTop-bar controls
--radius-fullToolbar, pills
--space-2Tight gap
--space-4Default gap
--space-8Section gap
--page-narrowLinear pages (notifications)
--page-defaultEntity pages + subscription shell
--page-widePricing / boards grid / subscription plan cards
--page-columnReading column inside a shell
--shadow-smSubtle elevation
--shadow-mdCards
--shadow-lgModals
--z-headerHeader
--z-modalModals
--duration-fastQuick transitions
--duration-normalTransitions
--ease-defaultEasing
--font-sansInter body stack
--font-titleSpace Grotesk titles