Visual Design

Visual rules and standards for the Airy design system.

Visual Design Rules

This guide documents the visual system behind Airy.

Theme Architecture

Airy supports two themes with light and dark variants:

  • App theme: :root and .dark
  • Marketing theme: .theme-marketing and .theme-marketing.dark

All tokens currently use identical defaults so you can customize later without changing component code.

Color System

Use semantic tokens, not raw hex values:

  • --background, --foreground
  • --primary, --secondary, --accent
  • --muted, --destructive, --border
  • --card, --popover, --ring

Charts and sidebar tokens exist for dashboards and admin surfaces.

Typography

Use the tokenized scale in airy.css:

  • --text-xs through --text-3xl
  • --line-height-tight, --line-height-normal, --line-height-relaxed
  • --font-weight-regular, --font-weight-medium, --font-weight-semibold, --font-weight-bold

Font families:

  • --font-sans: "IBM Plex Sans", ui-sans-serif, system-ui, sans-serif
  • --font-mono: "IBM Plex Mono", ui-monospace, monospace
  • --font-handwriting: "Caveat", cursive

Font roles:

  • Body and interface text: font-sans
  • Code and tabular text: font-mono
  • Accent/handwritten treatments: font-handwriting

Spacing

Use the spacing tokens:

  • --space-0 through --space-24

Prefer layout utilities for spacing. Use tokens when defining new CSS rules.

Radius

Use --radius and derived tokens (--radius-md, --radius-lg, etc).

Surface Treatments

Glass

.surface-glass is a translucent, blurred surface treatment for floating panels, popovers, command palettes, and similar elevated UI. Inspired by Apple's "Liquid Glass" material.

<div className="surface-glass rounded-2xl p-6">…</div>

Behavior:

  • Owns: background, backdrop-filter, border, box-shadow.
  • Does NOT own: border-radius, padding, sizing, layout, typography.
  • Lives in the airy-glass cascade layer (declared after utilities), so it auto-overrides Tailwind utilities like bg-popover, border, shadow-*. No bg-transparent border-0 shadow-none boilerplate needed.
  • Falls back to a solid background in browsers without backdrop-filter.
  • Supports both light and dark mode via the .dark parent selector.

Per-instance overrides via CSS variable hooks (preferred over ! overrides):

| Variable | Default (light) | Purpose | | -------------------- | ------------------------------------------- | ------------------------- | | --glass-tint | oklch(var(--primitive-neutral-50) / 0.82) | Background color | | --glass-blur | 32px | Backdrop blur radius | | --glass-saturation | 190% | Backdrop saturation boost |

<div className="surface-glass [--glass-tint:oklch(0.85_0.12_140/0.55)]">
  Brand-tinted glass surface
</div>

<div className="surface-glass [--glass-blur:8px]">
  Subtler frost (e.g. for nested glass surfaces)
</div>

For one-off property overrides outside the hook set, use Tailwind's ! prefix (e.g. !bg-red-500/20). Inline style is the right tool for dynamic values driven from JavaScript.

Composing onto a primitive that already sets a background — no need to neutralize it with bg-transparent:

<NavigationMenuViewport className="surface-glass" />
<DialogContent className="surface-glass rounded-2xl" />

When NOT to use:

  • Inside another glass surface — glass-on-glass produces visual noise. Use a plain bg-card panel inside a glass dialog, or tune the inner surface to a much lighter blur ([--glass-blur:6px]).
  • On large flat-color backgrounds — glass needs varied content behind it (images, gradients, busy UI) to read as glass.
  • On dozens of elements simultaneously — backdrop-filter is GPU-expensive.

Brand

  • Use the lev-logo registry component for all logo placements
  • Three variants: icon, logo (wordmark), lockup (wordmark + icon)
  • The logo uses currentColor — set text color on the parent to control it
  • Never recreate the logo as raw SVG or an image tag

Iconography

Airy uses the Lucide icon set. Browse the full library at lucide.dev.

Sizing and alignment

  • Icons are drawn on a 24x24 grid but can be scaled up or down as needed
  • Icon size should be roughly 2px larger than the accompanying text size
  • Maintain 16-24px clear padding from surrounding UI elements
  • Align icons optically to the text's x-height for visual balance
  • Single-line text: center-align the icon vertically
  • Multiline text: align the icon to the top edge of the text
  • Never use emojis as icon substitutes

If you need an icon not in the Lucide standard set, contact Product Design before introducing a new one.

Code implementation

In React, import from lucide-react:

import { Mail, Search, Settings } from "lucide-react"

Size pairings (icon should be ~2px larger than the text):

  • text-xs with size-3.5
  • text-sm with size-4
  • text-base with size-5
  • text-lg or text-xl with size-6

Use text-muted-foreground for secondary/decorative icons and text-foreground for primary action icons.

Example:

<Button><Mail className="size-4" /> Send email</Button>

When lucide-react cannot be imported (static HTML, emails, sandboxed environments): use inline SVGs from lucide.dev. Never use emojis or image tags as substitutes.

Reserved icons

These icons are reserved to ensure consistency across Lev products. They represent core actions and concepts that should not be replaced, restyled, or repurposed. Always reference the reserved set first and only introduce new icons when a concept is not already represented.

Status and feedback:

  • Info — info
  • CircleAlert — warning
  • TriangleAlert — error
  • CircleCheck — success

Universal system actions:

  • CircleHelp — help
  • Search — search
  • Share2 — share
  • Settings — settings
  • MoreHorizontal — more
  • RefreshCw — refresh

File manipulations:

  • Save — save
  • Trash2 — delete
  • Upload — upload
  • Download — download
  • Copy — copy / duplicate

Editing:

  • Pencil — edit
  • Link — link
  • Undo2 — undo
  • Redo2 — redo

Dark Mode

  • Maintain contrast ratios for all text and controls.
  • Avoid hard-coded colors in components.
  • Verify hover, focus, and disabled states in both themes.

Marketing Assets

Marketing materials — brochures, one-pagers, landing pages, pitch decks — use the marketing dark theme (.theme-marketing.dark). All colors must come from the semantic tokens defined in that theme context, not from primitives or hardcoded values.

The marketing dark theme provides a dark background with green accent palette. Use the same semantic token names as the app theme (--background, --foreground, --card, --primary, --border, etc.) — they resolve to the correct marketing values automatically.

When generating output that cannot reference CSS variables (PDFs, static HTML, emails, sandboxed artifacts), use the resolved theme values returned in Airy route or part payloads. Those values are generated from registry/airy-theme/*.css and preserve semantic role names. Never extract colors from Figma mockups or approximate values.

Resolved Theme Decision Rule

Use this rule before requesting resolved theme values:

  1. Will CSS custom properties be available at render time?
  2. If yes, use semantic tokens/classes directly (do not resolve).
  3. If no or unknown, request route or part context that includes resolved values.

Common examples:

  • Use resolved values: PDFs, emails, static exports, sandboxed outputs, native presentations, and image specs.
  • Do not use resolved values: normal web app components with airy.css loaded, runtime light/dark switching, or token discovery tasks.

For non-web deliverables, pair theme resolution with Airy context:

  • request context for the target surface and deliverable type
  • search covered parts, brand assets, guidelines, and theme roles as needed
  • fetch exact part or token payloads when precision matters
  • use resolved theme token payloads when CSS variables are unavailable
  • preserve semantic token role names in specs so implementation can trace every resolved color back to Airy

Anti-pattern: claiming exact Airy component fidelity for non-web output without using the relevant Airy source files, assets, and resolved theme tokens.