# Lumen — the Desklight design system > Lumen is Desklight’s design system. Say what you want in plain words, and your coding agent builds it from the same parts we ship. Version 2.0.0-rc.3. 14 foundations, 38 components, 4 composed reference patterns. Inventory generated from the built Studio. Simulations are local; this file is not a deployment receipt or accessibility certification. Machine-readable inventory: https://lumen.desklight.ai/lumen-manifest.json ## Rules for building with Lumen - Compose from the components below before writing new UI; extend a component's props rather than forking it. - Colors are CSS variables. Prefer the semantic roles: var(--surfaceCanvas|surfacePanel|surfaceInset|surfaceRaised|surfaceOverlay), var(--textPrimary|textSecondary|textMuted), var(--actionPrimary|actionOnPrimary|actionLink), var(--focusRing), var(--success|warning|error|info). Never a raw hex. - Spacing, type size, line height, weight, radius, height and motion are keys of SPACE, TYPE, LH, W, RADIUS, HEIGHT and MOTION in src/design-tokens.js. A missing key is undefined and the browser drops the style silently. - Use only the props and values listed for a component. Legacy props are listed so you recognise them; do not write them. - Check your work: node scripts/contract-tests/run.mjs --only 14 fails on any token key or CSS variable that does not exist. ## Component contracts Generated from the component source; props not listed here do not exist. - **Status** — Content-type badges and the four presence status dots. `import { Status } from "src/components/ui";` Props: s: "active" | "paused" | "off"; style Live page: https://lumen.desklight.ai/components/badge#badge - **Toggle** — An immediate, persistent on/off — the setting takes effect the moment it flips. Pill track with a sliding knob. `import { Toggle } from "src/components/ui";` Props: on; color; onClick: function; disabled; ariaLabel; style; size = "md": "md" | "sm" Live page: https://lumen.desklight.ai/components/toggle#toggle - **Avatar** — Circular avatars in three sizes and keyboard-key chips. `import { Avatar } from "src/components/ui";` Props: name; initials; color; size = "md": "sm" | "md" | "lg"; status: "off"; src Live page: https://lumen.desklight.ai/components/misc#misc - **Badge** — Content-type badges and the four presence status dots. `import { Badge } from "src/components/ui";` Props: kind: "task" | "vault" | "system" | "brand"; children; style Live page: https://lumen.desklight.ai/components/badge#badge - **Kbd** — Circular avatars in three sizes and keyboard-key chips. `import { Kbd } from "src/components/ui";` Props: children; style Live page: https://lumen.desklight.ai/components/misc#misc - **Input** — Single-line (Input) and multi-line (Textarea) free text — names, slugs, captions, descriptions. Both share the height ladder and the border-only focus pattern. `import { Input } from "src/components/ui";` Props: label: node; value; onChange: function; placeholder; mono; type = "text"; hint: node; size = "md": "sm" | "md" | "lg"; disabled = false; autoFocus; onKeyDown; onBlur: function; name; id; flush = false; style Live page: https://lumen.desklight.ai/components/input#input - **Textarea** — Single-line (Input) and multi-line (Textarea) free text — names, slugs, captions, descriptions. Both share the height ladder and the border-only focus pattern. `import { Textarea } from "src/components/ui";` Props: label: node; value; onChange: function; placeholder; rows = 4; hint: node; disabled = false; style Live page: https://lumen.desklight.ai/components/input#input - **Select** — Pick one value from a flat list. Portals its menu to
to escape overflow; inline sizes the trigger to its content for toolbars. `import { Select } from "src/components/ui";` Props: label: node; value; onChange: function; options: array of { value, disabled, label, description, tier, chip }; inline = false; prefixIcon; disabled = false; placeholder = "Select…"; size = "md": "xs" | "sm" | "md" | "lg" | "xl"; style; className = ""; ariaLabel; description: node Live page: https://lumen.desklight.ai/components/select#select - **Btn** — Every button in the app is this one. It comes in eleven variants and four sizes. Each variant is shown below. `import { Btn } from "src/components/ui";` Props: children; onClick; variant: "primary" | "success" | "danger" | "warning" | "subtle" | "link" | "quiet" | "imageOverlay" | "glass" | "menuItem" | "ghost"; outline = false; size = "md": "sm" | "md" | "lg" | "xl"; disabled = false; loading = false; icon; iconRight; iconOnly = false; block = false; title; type = "button"; style; ariaLabel; className = ""; press = false; other props pass through to the element Legacy, do not use: primary, danger, success, small Live page: https://lumen.desklight.ai/components/btn#btn - **SectionLabel** — See src/components/ui/SectionLabel.jsx. `import { SectionLabel } from "src/components/ui";` Props: children - **Card** — The neutral container and its labeled value rows. `import { Card } from "src/components/ui";` Props: children; style; other props pass through to the element Live page: https://lumen.desklight.ai/components/card#card - **CardRow** — The neutral container and its labeled value rows. `import { CardRow } from "src/components/ui";` Props: label: node; children; last Live page: https://lumen.desklight.ai/components/card#card - **Stack** — Layout without hand-rolled flexbox: a column with one gap, and a row that wraps when space runs out. `import { Stack } from "src/components/ui";` Props: gap = 12: "2" | "4" | "6" | "8" | "10" | "12" | "16" | "20" | "24" | "32" | "40" | "48"; align = "stretch"; as = "div"; style; children; other props pass through to the element Live page: https://lumen.desklight.ai/components/layout#layout - **Cluster** — Layout without hand-rolled flexbox: a column with one gap, and a row that wraps when space runs out. `import { Cluster } from "src/components/ui";` Props: gap = 8: "2" | "4" | "6" | "8" | "10" | "12" | "16" | "20" | "24" | "32" | "40" | "48"; align = "center"; justify = "flex-start"; wrap = true; as = "div"; style; children; other props pass through to the element Live page: https://lumen.desklight.ai/components/layout#layout - **PageHeader** — PageHeader — the strip every tool-page (Calendar, Connectors, Team, Knowledge, BrandsPage, Assets) renders at the top. `import { PageHeader } from "src/components/ui";` Props: title: node; subtitle: node; action: node - **Toast** — See src/components/ui/Toast.jsx. `import { Toast } from "src/components/ui";` Props: msg; type = "success"; duration = 2500; onClose: function - **Empty** — The zero-state — icon, title, one line, and a single primary action. `import { Empty } from "src/components/ui";` Props: icon: node; title: node; desc: node; action: node Live page: https://lumen.desklight.ai/components/empty#empty - **ErrorBoundary** — See src/components/ui/ErrorBoundary.jsx. `undefined` Props: none - **TableSkeleton** — See src/components/ui/Skeleton.jsx. `import { TableSkeleton } from "src/components/ui";` Props: cols = 4; rows = 6 - **FeedSkeleton** — tile: draw a rounded icon tile of that size instead of the dot, and sit flush (no gutters or hairlines), for feeds whose real rows are tile + text lines laid out edge to edge. `import { FeedSkeleton } from "src/components/ui";` Props: rows = 6; tile - **CardSkeleton** — See src/components/ui/Skeleton.jsx. `import { CardSkeleton } from "src/components/ui";` Props: none - **MediaLightbox** — Public API. `import { MediaLightbox } from "src/components/ui";` Props: open; onClose; items = []; index = 0; onIndexChange: function; onDelete: function; onGoToPost: function; onEdit: function; onLab: function; onReedit: function - **InfoTip** — The hover info tooltip for field labels and dense metadata. `import { InfoTip } from "src/components/ui";` Props: text: node; size = 14; maxWidth = 260; side = "bottom": "top" Live page: https://lumen.desklight.ai/components/infotip#infotip - **AllieThinking** — The live “Allie is at it” indicator, with two modes so she reads alive. mode="thinking" bounces three accent dots while she composes a reply; mode="working" sweeps an accent bar in a pill while a tool runs. One primitive, shared by ChatWidget + OnboardingChat — never hand-mirrored per surface. `import { AllieThinking } from "src/components/ui";` Props: mode = "thinking": "working"; size = 7 Live page: https://lumen.desklight.ai/components/alliethinking#alliethinking - **SparkGlyph** — Allie is the four-point sparkle cut out of the Desklight D. Her orb, a flat accent disc with the sparkle in onAccent, is her face and appears once on screen: the chat launcher, the empty-chat hero, the chat header, or her picker row. The bare sparkle opens each of her turns: accent on the newest, muted on every older one. While she works, a comet orbits that sparkle; when her reply lands it spirals into the star, which springs into the bright mark in the same place. `undefined` Props: none Live page: https://lumen.desklight.ai/components/allie#allie - **AllieOrb** — Allie is the four-point sparkle cut out of the Desklight D. Her orb, a flat accent disc with the sparkle in onAccent, is her face and appears once on screen: the chat launcher, the empty-chat hero, the chat header, or her picker row. The bare sparkle opens each of her turns: accent on the newest, muted on every older one. While she works, a comet orbits that sparkle; when her reply lands it spirals into the star, which springs into the bright mark in the same place. `undefined` Props: none Live page: https://lumen.desklight.ai/components/allie#allie - **AllieTurnMark** — Grouped user bubbles and a shared assistant turn mark. Keep the newest assistant mark bright, past turns quiet, and motion tied to a real in-flight request. `import { AllieTurnMark } from "src/components/ui";` Props: phase = "latest": "working" | "resolving"; progress = false; onContact; className = ""; style Live page: https://lumen.desklight.ai/components/chat-message#chat-message - **AllieStatusLabel** — Grouped user bubbles and a shared assistant turn mark. Keep the newest assistant mark bright, past turns quiet, and motion tied to a real in-flight request. `import { AllieStatusLabel } from "src/components/ui";` Props: text; leaving = false; className = ""; style Live page: https://lumen.desklight.ai/components/chat-message#chat-message - **Tooltip** — Portal-based hover tooltip — wraps any trigger element. `import { Tooltip } from "src/components/ui";` Props: text: node; children; side = "bottom": "top"; maxWidth = 240 - **ConfirmModal** — The calm, branded replacement for window.confirm() — title, body, and actions separated by whitespace, never banded. `import { ConfirmModal } from "src/components/ui";` Props: open; onClose; onConfirm: function; title = "Are you sure?": node; body: node; confirmLabel = "Confirm": node; cancelLabel = "Cancel": node; tone = "danger": "danger" | "caution" | "info"; confirmLoading = false; confirmDisabled = false; width = 440; children Live page: https://lumen.desklight.ai/components/confirm-modal#confirm-modal - **AssetPickerModal** — The shared file / image / video / doc picker — opens over any flow that attaches an existing asset. `import { AssetPickerModal } from "src/components/ui";` Props: open; onClose: function; onPick: function; tenantId; accept = ["photo", "graphic"]: array; title = "Select from library": node; emptyText = "No assets to choose from yet."; multi = false; existingUrls = []; pickLabel = "Add": node; allowUpload = false Live page: https://lumen.desklight.ai/components/asset-picker-modal#asset-picker-modal - **ModalShell** — ══════════════════════════════════════════════════════════════════ ModalShell — the one responsive + accessible dialog container. `import { ModalShell } from "src/components/ui";` Props: open; onClose: function; children; width = "min(560px, 100%)"; height; maxWidth = "calc(100vw - 32px)"; maxHeight = "calc(100dvh - 48px)"; mobileFullscreen = false; zIndex = Z.modal; closeOnBackdrop = true; closeOnEsc = true; padding; ariaLabel; ariaLabelledby; background = "var(--bg1)"; column = true; boxStyle Live page: https://lumen.desklight.ai/components/modal-chrome#modal-chrome - **ModalHeader** — ModalHeader — the standard responsive header band for a ModalShell. `import { ModalHeader } from "src/components/ui";` Props: leading = null: node; title = null: node; tools = null: node; actions = null: node; isMobile; id; background = "var(--bg2)"; borderless = false; style Live page: https://lumen.desklight.ai/components/modal-chrome#modal-chrome - **ActionBar** — ActionBar — the standard responsive footer band for a ModalShell. `import { ActionBar } from "src/components/ui";` Props: left = null; right = null; isMobile; stackOnMobile = true; background = "var(--bg2)"; borderless = false; style Live page: https://lumen.desklight.ai/components/modal-chrome#modal-chrome - **ChipRail** — ChipRail — a wrapping row of chips / pills (PickerPill, FilterChip, …). `import { ChipRail } from "src/components/ui";` Props: children; gap = SPACE[8]; align = "center"; style - **DatePicker** — DatePicker — design-system replacement for . `import { DatePicker } from "src/components/ui";` Props: value; onChange: function; placeholder = "Pick a date"; disabled = false; today; clearable = true - **TimePicker** — TimePicker — design-system replacement for . `import { TimePicker } from "src/components/ui";` Props: value; onChange: function; placeholder = "Pick a time"; disabled = false - **ColorPickerPopover** — Modern color picker popover — react-colorful saturation/value box + hue slider, plus a hex input that accepts "FFBA49", "#FFBA49", or "#FBA" (short hex auto-expanded), plus R/G/B numeric inputs that stay synced. `import { ColorPickerPopover } from "src/components/ui";` Props: value: { start }; onChange: function; onClose: function; anchorRef: { current }; allowGradient = false - **ProgressMeter** — Minimal progress bar (no badge — label + ETA now live in the centered overlay status text; see the calendar preview). `import { ProgressMeter } from "src/components/ui";` Props: percent = 0; variant = 'overlay': "overlay"; indeterminate = false; color; height = 2.5 - **ProgressRing** — A compact radial meter for 0–100 values — the premium alternative to a bar when the value IS the story. Sweeps in from 12 o'clock on mount (reduced-motion safe), hairline track, round caps, optional centre content. Powers Allie's brand-understanding read on the Desk brand page and the Home journey card. `import { ProgressRing } from "src/components/ui";` Props: value = 0; size = 44; stroke = 4; color = "var(--accent)"; trackColor = "var(--border)"; animate = true; title; style; children Live page: https://lumen.desklight.ai/components/progressring#progressring - **CadenceMeter** — The on-pace / behind-pace posting cadence meter for a brand's week. `import { CadenceMeter } from "src/components/ui";` Props: count = 0: node; target = 0: node; label = "": node; note = ""; size = "sm": "sm" | "md" Live page: https://lumen.desklight.ai/components/cadence-meter#cadence-meter - **CadenceBanner** — The behind-pace catch-up banner Allie surfaces on the dashboard. `import { CadenceBanner } from "src/components/ui";` Props: behindCount: node; brandName: node; onDraft; onDismiss; loading = false Live page: https://lumen.desklight.ai/components/cadence-banner#cadence-banner - **UpgradeNudge** — See src/components/ui/UpgradeNudge.jsx. `import { UpgradeNudge } from "src/components/ui";` Props: feature = "graphic_generation": "graphic_generation" | "video_generation"; used: node; cap: node; requiredTier = "solo": "team"; onDismiss; compact = false - **Spotlight** — See src/components/ui/Spotlight.jsx. `import { Spotlight } from "src/components/ui";` Props: items = []: array of { id, title }; interval = 6500; onDismiss; height = DEFAULT_H; style - **SpotlightMinimized** — SpotlightMinimized — what the Spotlight collapses to when minimized. `import { SpotlightMinimized } from "src/components/ui";` Props: onExpand; count = 0: node; urgent = false; style - **TabStrip** — Underlined tab bar for switching sections inside one surface. `import { TabStrip } from "src/components/ui";` Props: tabs: array of { id, label }; value; onChange: function; padding = "0 24px"; size = "md": "sm"; style Live page: https://lumen.desklight.ai/components/tabstrip#tabstrip - **NavRow** — The sidebar nav row — icon + label, with active, collapsed (icon-only), and danger states. `import { NavRow } from "src/components/ui";` Props: icon: node; label: node; active; onClick; collapsed = false; danger = false; title; trailing = null; style Live page: https://lumen.desklight.ai/components/navrow#navrow - **NavBadge** — See src/components/ui/NavBadge.jsx. `import { NavBadge } from "src/components/ui";` Props: count = 0: node; label; mode = "inline": "corner"; showCount = false Live page: https://lumen.desklight.ai/components/navbadge#navbadge - **SegmentedToggle** — Flat segmented control for switching a view or mode between 2–4 mutually exclusive options. `import { SegmentedToggle } from "src/components/ui";` Props: options: array of { value, title, buttonProps, label }; value; onChange: function; ariaLabel; size = "md": "sm"; fill = false; style Live page: https://lumen.desklight.ai/components/segmented#segmented - **PillToggle** — Sliding-pill segmented toggle — the billing-cycle style, with an optional per-option badge. `import { PillToggle } from "src/components/ui";` Props: options: array of { value, label, badge }; value; onChange: function; size = "md": "sm"; style Live page: https://lumen.desklight.ai/components/pilltoggle#pilltoggle - **TileCard** — Action tiles for dashboards — icon, title, sub, optional badge, with active + disabled states. `import { TileCard } from "src/components/ui";` Props: icon: node; title: node; sub: node; badge: node; badgeColor; onClick; disabled = false; active = false; horizontal = true; style Live page: https://lumen.desklight.ai/components/tilecard#tilecard - **MediaTile** — MediaTile — one square asset thumbnail used by every "pick an asset" surface: the AssetPickerModal grid and the Video editor's media bin. `import { MediaTile } from "src/components/ui";` Props: thumbUrl; fallbackIcon: node; type; chip: node; label: node; selected = false; draggable = false; onDragStart; onClick; title; ariaLabel; aspect = "1 / 1"; lazyPaint = false Live page: https://lumen.desklight.ai/components/mediatile#mediatile - **TypeChip** — The ONE content-type chip for asset tiles. `import { TypeChip } from "src/components/ui";` Props: type; label; icon; expanded = false; positioned = true; style Live page: https://lumen.desklight.ai/components/typechip#typechip - **PlatformPill** — Per-platform social pills with brand marks — inactive, active, sizes, and icon-only. `import { PlatformPill } from "src/components/ui";` Props: platform: "facebook" | "instagram" | "linkedin" | "twitter" | "x" | "tiktok" | "youtube" | "youtube_short" | "threads" | "pinterest"; active; onClick; showLabel = true; size = "md": "sm"; style Live page: https://lumen.desklight.ai/components/platformpill#platformpill - **EditorialTitle** — See src/components/ui/EditorialTitle.jsx. `import { EditorialTitle } from "src/components/ui";` Props: children; size = "lg": "lg" | "md"; accent: node; period = true; style - **FilterChip** — Filter pill — toolbar chip for slicing a list (kind, status, etc). `import { FilterChip } from "src/components/ui";` Props: label; children; icon: node; on; active; onClick; disabled; minHeight - **SuggestionChips** — See src/components/ui/SuggestionChips.jsx. `import { SuggestionChips } from "src/components/ui";` Props: items; onAction; disabled = false; size = "sm": "lg" | "xl"; variant = "default": "followup"; enterClass = null Live page: https://lumen.desklight.ai/components/suggestion-chips#suggestion-chips - **IconToggle** — See src/components/ui/IconToggle.jsx. `import { IconToggle } from "src/components/ui";` Props: icon: function; active = false; onClick; title; size = "md": "sm" | "md" | "lg"; disabled = false; style; other props pass through to the element - **ToggleRow** — Labeled on/off row for edit-control surfaces (inspector panels). `import { ToggleRow } from "src/components/ui";` Props: icon; label: node; on; onChange; disabled = false; ariaLabel Live page: https://lumen.desklight.ai/components/canvas-primitives#canvas-primitives - **Section** — See src/components/ui/Section.jsx. `import { Section } from "src/components/ui";` Props: id; title: node; hint: node; defaultOpen = false; children - **RailSection** — See src/components/ui/RailSection.jsx. `import { RailSection } from "src/components/ui";` Props: id; icon; title: node; count: node; defaultOpen = false; children Live page: https://lumen.desklight.ai/components/canvas-primitives#canvas-primitives - **MiniSlider** — See src/components/ui/MiniSlider.jsx. `import { MiniSlider } from "src/components/ui";` Props: icon; value; onChange: function; min = -50; max = 50; step = 1; format = defaultFormat: function; ariaLabel = "Adjust" Live page: https://lumen.desklight.ai/components/canvas-primitives#canvas-primitives - **ScrubTrack** — See src/components/ui/ScrubTrack.jsx. `import { ScrubTrack } from "src/components/ui";` Props: label: node; value; min; max; step = 1; unit = ""; format: function; renderedAt; bipolar = false; disabled = false; onInput; onCommit - **Swatch** — Small color-swatch button. `import { Swatch } from "src/components/ui";` Props: color; selected = false; title; variant = "color": "eyedropper"; onClick; size = 22 Live page: https://lumen.desklight.ai/components/canvas-primitives#canvas-primitives - **SwatchDivider** — Thin divider between groups of swatches — e.g. brand palette | photo palette | eyedropper. `import { SwatchDivider } from "src/components/ui";` Props: none - **AnimatedCaret** — Shared rotating caret indicator used by collapsible sections and Select triggers — single source of truth for the "click to open/close" affordance. `import { AnimatedCaret } from "src/components/ui";` Props: open; size = 10; closedRotation = -90; openRotation = 0; openColor = "var(--accent)"; closedColor = "var(--textMut)"; pill = false; style - **PickerPill** — Pill-shaped dropdown trigger with a custom popover body. `import { PickerPill } from "src/components/ui";` Props: leading = null: node; label: node; title; popoverWidth = 280; popoverAlign = "left": "right"; popoverSide = "bottom": "top"; glass = false; popoverHeader = null: node; disabled = false; size: "lg" | "xl"; chrome: "ghost"; labelMaxWidth; radius; popoverFill; popoverTopReserve; popoverBounds; children Live page: https://lumen.desklight.ai/components/pickerpill#pickerpill - **PickerPillContext** — See src/components/ui/picker-pill-context.js. `undefined` Props: none - **AspectPicker** — See src/components/ui/AspectPicker.jsx. `import { AspectPicker } from "src/components/ui";` Props: value: "auto"; onChange: function; allowAuto = false; publishTargets = []; isMobile; disabled - **Spinner** — See src/components/ui/Spinner.jsx. `import { Spinner } from "src/components/ui";` Props: size = 16; speed = "900ms"; color = "var(--accent)"; trackColor = "var(--accentMut)"; stroke; style - **Scrubber** — Scrubber — a media seek bar. `import { Scrubber } from "src/components/ui";` Props: value = 0; duration = 0; onSeek: function; marks = []: array; height = 6; ariaLabel = "Seek" Live page: https://lumen.desklight.ai/components/video-studio#video-studio - **TransportBar** — TransportBar — playback controls for any media surface: skip-to-start, play/pause (primary), skip-to-end, and a tabular m:ss / m:ss readout. `import { TransportBar } from "src/components/ui";` Props: playing = false; onPlayPause; onSkipStart; onSkipEnd; current = 0; total = 0; disabled = false Live page: https://lumen.desklight.ai/components/video-studio#video-studio - **TimelineTrack** — See src/components/ui/TimelineTrack.jsx. `undefined` Props: none Live page: https://lumen.desklight.ai/components/video-studio#video-studio - **RangeTrim** — See src/components/ui/RangeTrim.jsx. `import { RangeTrim } from "src/components/ui";` Props: duration = 0; start = 0; end = 0; onChange: function; poster; height = 44 Live page: https://lumen.desklight.ai/components/video-studio#video-studio - **VideoGenControls** — ─── VideoGenControls — the inline pill row ─── Props: value {model,durationSec,audio,aspect}, onChange(patch), compact. compact (editor inspector) hides the aspect picker — the clip inherits the timeline aspect — and … `import { VideoGenControls } from "src/components/ui";` Props: value; onChange; compact = false - **VideoFramesRow** — eslint-disable react-refresh/only-export-components -- intentional: this file re-exports VIDEO_MODEL_CAPS, VIDEO_MODEL_ORDER, and modelDisplayName alongside the components so the editor slice and ChatWidget share one … `import { VideoFramesRow } from "src/components/ui";` Props: value: { startFrameUrl, endFrameUrl }; onChange: function; onAttachStartFrame; onAttachEndFrame - **VoiceGenControls** — ─── VoiceGenControls — the inline pill row ─── Props: value {model, voiceId, emotion, speed, brief}, onChange(patch), compact. `import { VoiceGenControls } from "src/components/ui";` Props: value; onChange; compact = false - **MusicGenControls** — ─── MusicGenControls — the inline pill row ─── Props: value {model, durationSec}, onChange(patch), compact. compact (editor inspector) allows the two pills to wrap. `import { MusicGenControls } from "src/components/ui";` Props: value; onChange; compact = false - **AudioPlayer** — See src/components/ui/AudioPlayer.jsx. `import { AudioPlayer } from "src/components/ui";` Props: src; filename = ""; variant = "inline": "full"; active = true - **InlineNotice** — A message and its next step share one surface. Semantic colour marks the condition; a neutral, visible action keeps recovery distinct from danger. `import { InlineNotice } from "src/components/ui";` Props: tone = "info": "error" | "warning" | "info" | "success"; title: node; children; action; secondaryAction; announce = true; className = "" Live page: https://lumen.desklight.ai/components/inline-notice#inline-notice - **ChatAttachment** — A controlled, editable brand read in the conversation. Palette, actual type, voice and audience remain visible while secondary details stay behind a reveal. `import { ChatAttachment } from "src/components/ui";` Props: children; className = ""; label Live page: https://lumen.desklight.ai/components/chat-brand#chat-brand - **ChatBrandCard** — A controlled, editable brand read in the conversation. Palette, actual type, voice and audience remain visible while secondary details stay behind a reveal. `import { ChatBrandCard } from "src/components/ui";` Props: data: { headline_font, body_font, typography, headline_weight, body_weight, logo_preview, name, use_extracted_logo, tagline, colors, voice, audience, product_story }; onChange: function; disabled; source = "Allie’s read": node; onConfirm; confirmLabel = "Create my first draft"; onLogoFile: function; children Live page: https://lumen.desklight.ai/components/chat-brand#chat-brand - **ChatPalette** — A controlled, editable brand read in the conversation. Palette, actual type, voice and audience remain visible while secondary details stay behind a reveal. `import { ChatPalette } from "src/components/ui";` Props: colors = []: array; onChange: function; disabled = false; label = "Brand colors" Live page: https://lumen.desklight.ai/components/chat-brand#chat-brand ## Studio tools - https://lumen.desklight.ai/ - https://lumen.desklight.ai/start - https://lumen.desklight.ai/spacing - https://lumen.desklight.ai/surfaces - https://lumen.desklight.ai/identity - https://lumen.desklight.ai/principles - https://lumen.desklight.ai/foundations - https://lumen.desklight.ai/components - https://lumen.desklight.ai/patterns - https://lumen.desklight.ai/motion - https://lumen.desklight.ai/response - https://lumen.desklight.ai/agent-studio - https://lumen.desklight.ai/chat - https://lumen.desklight.ai/composition - https://lumen.desklight.ai/tokens - https://lumen.desklight.ai/quality - https://lumen.desklight.ai/agents - https://lumen.desklight.ai/roadmap - https://lumen.desklight.ai/changelog ## Principles - **One clear next step** — What can someone finish here, and which action moves them forward? - **Space expresses relationships** — Are related controls closer than separate groups? Are nested radii derived from the inset? - **Depth explains ownership** — Can someone tell what is editable, what is selected, and what temporarily sits above the page? - **Hierarchy survives stress** — Does the longest label still work on a narrow screen, in both themes? - **Acknowledge, then do the work** — Is input acknowledged immediately? Does motion explain a state change? - **Preserve progress** — Can someone correct a failure or reverse a destructive local action without starting over? - **Reuse the smallest useful part** — Does an existing component already solve this? Is a new abstraction supported by a second use? - **Authority stays legible** — Are scope, permission, and the boundary between a proposal and an external action explicit? ## Foundations Colour, type, space, and motion — the raw tokens every primitive reads from. Pull from these; never hard-code a value. - [Colour](https://lumen.desklight.ai/foundations#color) : Surfaces, text tiers, the active brand accent, and borders — the whole palette, recoloured live by the theme toggle above. Each swatch shows the active hex plus the other theme's value; click to copy the CSS var. - [Type scale](https://lumen.desklight.ai/foundations#type) : Eight sizes, 10–24px. Every fontSize in the app pulls a TYPE key — half-pixel sizes were dropped. - [Font weight](https://lumen.desklight.ai/foundations#weight) : The weight ladder — 600 headings down to 300 labels — that carries every hierarchy. Geist only. - [Spacing](https://lumen.desklight.ai/foundations#spacing) : The pixel-keyed spacing scale — 2 through 48. There is no SPACE[14] or [18]; they render as 0 and break layouts. - [Border radius](https://lumen.desklight.ai/foundations#radius) : Five radii — chip to pill — that collapse 17 ad-hoc values into one ladder. - [Interactive heights](https://lumen.desklight.ai/foundations#height) : The click-target height ladder so buttons, inputs, and selects line up. - [Line height](https://lumen.desklight.ai/foundations#lineheight) : Four line-heights covering headings through long-form copy. - [Motion](https://lumen.desklight.ai/foundations#motion) : Ease-out timings plus one spring (press) for tactile controls. Colour fades always ease out; nothing loops at rest. - [Border states](https://lumen.desklight.ai/foundations#border) - [Focus state](https://lumen.desklight.ai/foundations#focus) - [Shadow](https://lumen.desklight.ai/foundations#shadow) - [Stacking layers](https://lumen.desklight.ai/foundations#zindex) - [Icons](https://lumen.desklight.ai/foundations#icons) @phosphor-icons/react: One icon set, one weight: Phosphor duotone. No other weights, no hand-drawn SVG, and never an emoji in chrome — that single discipline is most of what makes the UI read as a product, not an LLM draft. - [Token naming](https://lumen.desklight.ai/foundations#naming) : The names are built so a value tells you how to use it — bracket scales are literal pixels, semantic keys are named for the job. ## Patterns & surfaces Primitives composed into real Desklight surfaces. Composition is the point — the atoms above only matter once they stack into a product. - [Spotlight](https://lumen.desklight.ai/patterns#pattern-spotlight) : The sidebar's rotating announcement card. A crossfade stage dissolves messages into each other (opacity only — no reflow, no positional jump); the dot→pill pagination's active pill carries an accent progress-fill that IS the auto-advance timer — onAnimationEnd advances, so hover-pause stays perfectly in sync with zero timers. The lead card plays a muted, looping clip behind the message under a legibility veil — how a real generated video becomes the pitch to try the model (the media over-covers the rounded corners by 1px so there's never a corner seam). Hover to pause; reduced-motion drops the autoplay + crossfade and the dots stay as manual controls. The minus MINIMIZES it — never a permanent dismiss — to SpotlightMinimized, a twin of the sidebar Credits row (muted glyph · label · pill count chip) that restores the card on click. Try the round-trip in the demo. - [Configuration panel](https://lumen.desklight.ai/patterns#pattern-config) : A settings cluster — the Card + CardRow + Toggle pattern used across Settings and brand config, with a commit row. - [Agent action row](https://lumen.desklight.ai/patterns#pattern-composer) : How Allie closes a turn — a message, suggestion chips, a model picker, and a send button. The flagship chat pattern, distilled. - [Publish status rows](https://lumen.desklight.ai/patterns#pattern-publish) : The per-platform publish list from the calendar — a recognizable surface built from three primitives. ## Controls & inputs The things people click and type into. Buttons, fields, pickers, and segmented switches. - [Button](https://lumen.desklight.ai/components/btn#btn) Btn: Every button in the app is this one. It comes in eleven variants and four sizes. Each variant is shown below. - [Text input](https://lumen.desklight.ai/components/input#input) Input · Textarea: Single-line (Input) and multi-line (Textarea) free text — names, slugs, captions, descriptions. Both share the height ladder and the border-only focus pattern. - [Select](https://lumen.desklight.ai/components/select#select) Select: Pick one value from a flat list. Portals its menu to to escape overflow; inline sizes the trigger to its content for toolbars. - [Toggle](https://lumen.desklight.ai/components/toggle#toggle) Toggle: An immediate, persistent on/off — the setting takes effect the moment it flips. Pill track with a sliding knob. - [Picker pill](https://lumen.desklight.ai/components/pickerpill#pickerpill) PickerPill - [Segmented toggle](https://lumen.desklight.ai/components/segmented#segmented) SegmentedToggle: Flat segmented control for switching a view or mode between 2–4 mutually exclusive options. - [Pill toggle](https://lumen.desklight.ai/components/pilltoggle#pilltoggle) PillToggle: Sliding-pill segmented toggle — the billing-cycle style, with an optional per-option badge. - [Suggestion chips](https://lumen.desklight.ai/components/suggestion-chips#suggestion-chips) SuggestionChips ## Navigation & status Moving between surfaces and signalling state — tabs, nav rows, badges, presence, and pace. - [Tab strip](https://lumen.desklight.ai/components/tabstrip#tabstrip) TabStrip: Underlined tab bar for switching sections inside one surface. - [Nav row](https://lumen.desklight.ai/components/navrow#navrow) NavRow: The sidebar nav row — icon + label, with active, collapsed (icon-only), and danger states. - [Nav badge](https://lumen.desklight.ai/components/navbadge#navbadge) NavBadge - [Badge & status](https://lumen.desklight.ai/components/badge#badge) Badge · Status: Content-type badges and the four presence status dots. - [Cadence meter](https://lumen.desklight.ai/components/cadence-meter#cadence-meter) CadenceMeter: The on-pace / behind-pace posting cadence meter for a brand's week. - [Cadence banner](https://lumen.desklight.ai/components/cadence-banner#cadence-banner) CadenceBanner: The behind-pace catch-up banner Allie surfaces on the dashboard. - [Avatar & keyboard key](https://lumen.desklight.ai/components/misc#misc) Avatar · Kbd: Circular avatars in three sizes and keyboard-key chips. - [Info tip](https://lumen.desklight.ai/components/infotip#infotip) InfoTip: The hover info tooltip for field labels and dense metadata. - [Allie thinking](https://lumen.desklight.ai/components/alliethinking#alliethinking) AllieThinking: The live “Allie is at it” indicator, with two modes so she reads alive. mode="thinking" bounces three accent dots while she composes a reply; mode="working" sweeps an accent bar in a pill while a tool runs. One primitive, shared by ChatWidget + OnboardingChat — never hand-mirrored per surface. - [Allie](https://lumen.desklight.ai/components/allie#allie) AllieOrb · AllieTurnMark · AllieStatusLabel · SparkGlyph: Allie is the four-point sparkle cut out of the Desklight D. Her orb, a flat accent disc with the sparkle in onAccent, is her face and appears once on screen: the chat launcher, the empty-chat hero, the chat header, or her picker row. The bare sparkle opens each of her turns: accent on the newest, muted on every older one. While she works, a comet orbits that sparkle; when her reply lands it spirals into the star, which springs into the bright mark in the same place. - [Progress ring](https://lumen.desklight.ai/components/progressring#progressring) ProgressRing: A compact radial meter for 0–100 values — the premium alternative to a bar when the value IS the story. Sweeps in from 12 o'clock on mount (reduced-motion safe), hairline track, round caps, optional centre content. Powers Allie's brand-understanding read on the Desk brand page and the Home journey card. ## Layout & containers The neutral surfaces that hold everything else — cards, tiles, zero-states, platform pills. - [Stack & cluster](https://lumen.desklight.ai/components/layout#layout) Stack · Cluster: Layout without hand-rolled flexbox: a column with one gap, and a row that wraps when space runs out. - [Card & row](https://lumen.desklight.ai/components/card#card) Card · CardRow: The neutral container and its labeled value rows. - [Empty state](https://lumen.desklight.ai/components/empty#empty) Empty: The zero-state — icon, title, one line, and a single primary action. - [Tile card](https://lumen.desklight.ai/components/tilecard#tilecard) TileCard: Action tiles for dashboards — icon, title, sub, optional badge, with active + disabled states. - [Platform pill](https://lumen.desklight.ai/components/platformpill#platformpill) PlatformPill: Per-platform social pills with brand marks — inactive, active, sizes, and icon-only. ## Assets & media Thumbnails, type chips, and the shared picker — the chrome around generated photos, graphics, and video. - [Media tile](https://lumen.desklight.ai/components/mediatile#mediatile) MediaTile - [Type chip](https://lumen.desklight.ai/components/typechip#typechip) TypeChip - [Asset picker](https://lumen.desklight.ai/components/asset-picker-modal#asset-picker-modal) AssetPickerModal: The shared file / image / video / doc picker — opens over any flow that attaches an existing asset. ## Overlays & modals The one dialog stack. Compose every overlay from these — never hand-roll a scrim or focus trap. - [Modal & dialog](https://lumen.desklight.ai/components/modal-chrome#modal-chrome) ModalShell · ModalHeader · ActionBar - [Confirm dialog](https://lumen.desklight.ai/components/confirm-modal#confirm-modal) ConfirmModal: The calm, branded replacement for window.confirm() — title, body, and actions separated by whitespace, never banded. - [First-post moment](https://lumen.desklight.ai/components/first-post-moment#first-post-moment) FirstPostMoment: The one designed celebration in the product — fires ONCE when a workspace's first post reaches scheduled/posted (the third journey milestone). The artifact is the reward, centered over the house radar ping in the accent. Deliberately un-gamified: no confetti, no badges, no points. ## Editor primitives Figma-grade inspector + timeline chrome that powers the canvas editor and Video Studio. - [Canvas editor](https://lumen.desklight.ai/components/canvas-primitives#canvas-primitives) RailSection · Swatch · MiniSlider · ToggleRow - [Video studio](https://lumen.desklight.ai/components/video-studio#video-studio) Scrubber · TransportBar · TimelineTrack · RangeTrim - [Editor chrome](https://lumen.desklight.ai/components/editor-chrome#editor-chrome) : The scene editor's shell chrome — the split save button, the aspect status chip, menu popovers, the scrubby number field, and the deviation dot + reset affordance. Static specimens on the editor's real class names; scoped styles keep the page self-contained (no editor-package import). ## Conversation The shared pieces behind Allie's chat. Transport-free surfaces with real working, reply, stop and recovery states. - [Chat surface](https://lumen.desklight.ai/components/chat-surface#chat-surface) ChatSurface · ConversationDemo: A complete conversation surface with a bounded transcript, identity, composer and host-owned actions. The interactive specimen uses local scripted replies. - [Brand attachment](https://lumen.desklight.ai/components/chat-brand#chat-brand) ChatBrandCard · ChatPalette · ChatAttachment: A controlled, editable brand read in the conversation. Palette, actual type, voice and audience remain visible while secondary details stay behind a reveal. - [Chat composer & actions](https://lumen.desklight.ai/components/chat-composer#chat-composer) ChatComposerSurface · ChatAction: The actual composer surface and action ladder extracted from ChatWidget: warm focus, busy edge, stable send/stop geometry and touch-aware targets. - [Notice & action](https://lumen.desklight.ai/components/inline-notice#inline-notice) InlineNotice: A message and its next step share one surface. Semantic colour marks the condition; a neutral, visible action keeps recovery distinct from danger. - [Message & turn](https://lumen.desklight.ai/components/chat-message#chat-message) ChatUserBubble · AllieTurnMark · AllieStatusLabel: Grouped user bubbles and a shared assistant turn mark. Keep the newest assistant mark bright, past turns quiet, and motion tied to a real in-flight request. ## Changelog - 2.0.0-rc.3 (2026-09-29) — Conversation polish: one composer focus ring and shared notices with responsive recovery actions. Orbit and Signal geometry now respects native Scene precision, with a 720-combination regression check. Local release candidate; this version is not a deployment receipt. - 2.0.0-rc.2 (2026-09-29) — Native composition studies, protected portraits, shared chat components, and an Allie SOP with real Sonnet trials and regression guards. - 2.0.0-rc.1 (2026-09-26) — Studio becomes the main Lumen site, with spatial recipes, semantic colour roles, practical tools and agent configuration. - 1.1 (2026-09-03) — Routed documentation site — overview with an inspectable live specimen, one page per group, a motion page, a token browser, quality gates, agent surface, roadmap · accent moves from amber to Sulphur (#E8E04A dark / #D9CF1C light) with --accentInk and --onAccent · --successInk / --warningInk added: the light success and warning fills failed AA as text · SHADOW.glow re-derived from the accent. - 1.0 (2026-07-03) — Principles and versioning published · live hero specimen · contrast pass — navigation and data raised to the AA floor · llms.txt + machine manifest for agents. - 0.9 (2026-07-01) — Spotlight pattern · standalone public site at lumen.desklight.ai with CI deploy. - 0.8 (2026-06-21) — Grouped doc shell — scroll-spy nav, hero, on-page dark/light theming. Benchmarked against Geist, Polaris, Carbon, Primer. ## Composition and Allie Reflow lab (approved posts re-flowed live into every format): https://lumen.desklight.ai/composition Contract: https://lumen.desklight.ai/composition-allie.json Working method: https://lumen.desklight.ai/composition-sop.md 1. Read the Desk and lock the brief 2. Choose a reasoned direction 3. Render the native Scene 4. Check every target format 5. Inspect the actual previews 6. Repair one visible defect 7. Compare with the previous candidate 8. Offer the work with an honest receipt The host owns approved copy, assets and brand facts. Allie returns design intent. Native HarfBuzz layout owns geometry. A visual review is not approval. Human preferences remain local.