← Desktop LabDESIGN SYSTEM / THE VISUAL LANGUAGE

Every detail.
A shared intention.

Color, type, space and movement, defined once. These are the tokens behind the Rust desktop—and the starting point for your own components.

590 source tokensSemantic palettesRust + CSS outputs
Explore native components and states ↗
01 / ONE SOURCE OF TRUTH

Primitive → semantic
→ component.

Primitives describe the available values. Semantic tokens give them a purpose. Component tokens define a specific piece of the interface, including the optical geometry that makes the launcher and widgets feel balanced.

PRIMITIVEpalette.cyan_300#73dcee
SEMANTIC · DARKtheme.dark.primarySelected action
COMPONENT RECIPEtheme.button(…)Primary button

design/tokens.json is the source. The generator resolves references into typed Rust constants and CSS custom properties. CI checks for stale outputs, broken references and cycles. Desktop components consume tokens; the framework stays independent of this visual identity.

Regenerate and verify
python3 scripts/generate-design-tokens.py
python3 scripts/generate-design-tokens.py --check
02 / COLOR WITH A PURPOSE

Same roles.
A different atmosphere.

Components ask for primary, on_primary and surface. Switching themes changes their values together, including icon tints. Text pairs in both themes have automated contrast checks.

◉   LucentDesign preview
A QUIETER WORKSPACE

Room to focus.

Semantic color keeps every component in the same conversation.

TerminalApplication · ready to launch
primary#73dcee
on_primary#153f5b
surface#2c5184
surface_container#1b6a7d
on_surface#bce8f4
error#ffcfbc
success#a7ead3
warning#f4d48f
info#bce8f4
focus#73dcee

This browser illustration explains two semantic themes. The component lab compares the actual Rust-rendered controls across all exported palettes and states. Native desktop actions run in the VM. The landing page uses a separate mint-and-ink brand theme from the same token source.

03 / RHYTHM & HIERARCHY

Space to breathe.
Type that belongs.

Dimensions are logical pixels. The compositor’s output scale determines physical pixels. Type roles range from compact captions to the stacked desktop clock.

font.captionAa11px
font.bodyAa14px
font.headingAa17px
font.temperatureAa32px
font.timerAa51px
font.clockAa110px

The spacing scale

space.hairline1px
space.xxs2px
space.xs4px
space.sm8px
space.md12px
space.lg16px
space.xl20px
space.xxl24px
space.section32px
space.wide40px
space.page64px

A grid with enforced relationships

The launcher, dock and top bar settle on the same 16-pixel shell grid as widget anchors. Control slots and gaps use 8 pixels; compact insets use a 4-pixel subdivision. Launcher rows and search fields are 48 pixels high. Panel heights and bar widths derive from their contents, and geometry tests enforce alignment, equal gutters and containment.

On screen sizes that do not divide evenly by 16, leftover pixels stay outside the shell. Text metrics and image aspect ratios keep their precision. Animation moves continuously between aligned endpoints; the renderer does not snap every frame.

Corners, with hierarchy

radius.small8px
radius.control18px
radius.panel20px
radius.widget30px
04 / MOTION & DENSITY

Continuity you can feel.

The Rust runtime samples Motion::value(now). The panel uses a 500 ms spatial curve that settles directly at its target without overshoot; launcher selection updates immediately; content enters in 320 ms and exits in 190 ms. Retargeting starts at the current value, even halfway through a transition.

SHARED MOTION TOKENSBrowser illustration
AppsWidgets
›_Terminal
◉Browser
motion.panel · 500ms

CSS uses the generated timing tokens. The native client samples the same curve in Rust. Reduced-motion preferences disable this website illustration.

Sharper at higher density

2×

Text coverage is rasterized at twice the output’s physical density, then resolved once into a cached texture.

1,024

Maximum preview texture edge in pixels. Icons prefer vector or larger originals; mip levels smooth reductions.

The framebuffer follows the output’s negotiated density. At 2×, one logical pixel covers four physical pixels. Supersampling text improves coverage; it does not add detail to a low-resolution original. Fractional scaling uses optional Wayland fractional-scale/viewport protocols with integer fallback. A scoped one-output live-video resize and density-retargeting run passes; physical, secure and broader multi-output acceptance remains separate. The merged text engine supports script shaping, bidirectional text, font fallback and opt-in ordinary IME. Owned native IME checks pass, while third-party, physical and cross-output text acceptance remains open. See the library guide for those source-specific boundaries.

05 / BUILD WITH INTENT

A recipe, not a bag
of magic numbers.

The desktop composes framework primitives using lucent-design. Choose a semantic role, then a type, spacing and corner token. Component geometry lives under component.launcher, component.calendar and the other component groups.

A themed component recipe
use lucent_design::{Theme, component, font, radius};

let theme = Theme::new(settings.light);
let launch = theme.button("Open application", Message::Launch)
    .font(font::BODY)
    .padding_xy(component::launcher::ROW_PADDING_INLINE, component::launcher::ROW_PADDING_BLOCK)
    .radius(radius::BUTTON)
    .background(theme.primary)
    .color(theme.on_primary);

// Selection and field recipes share the same semantic roles and geometry.
let choice = theme.choice_button("30 FPS", Message::Rate(30), selected);
let input = theme.text_input(value, selection, "Media path", Message::Edit);
let pause = theme.toggle_row("Pause all", Message::PauseAll, paused);
let status = theme.status_text("Saving preferences…", false);

// Shared hover and elevation recipes, including child elements.
theme.apply(launch)

Keep raw values in the token source. A new component-specific dimension should name its purpose—for example component.launcher.row_height. Arithmetic, timekeeping and protocol values remain ordinary code.

06 / CURVES NEED ROOM

Padding follows
the shape.

Capsules need more room at their ends than above and below their contents. The framework exposes padding_xy(horizontal, vertical); the design system chooses the insets. Backgrounds and hit targets keep their full size.

ComponentHorizontalVertical
Button20px8px
App, command and widget rows16px8px
Search field16px8px
Bar capsule12px4px

Command icons and labels share a vertical center. The dock derives its width from slots, gaps and equal end insets. Panel contents use the 20px inset token.

A shared spatial rhythm

Widgets move freely while dragging and snap on release to component.widget_layout.grid_step → space.lg: 16 logical pixels. The saved position uses that grid; screen edges take priority so a widget remains visible. This aligns anchors without forcing every widget to have the same size.

Application window corners use component.window.radius → radius.panel: 20px. The generated Hyprland configuration sets the compositor default for rounding and gaps, so terminals and browsers share it; explicit application rules and Omarchy gap toggles can take precedence. The compositor draws and clips application corners. Lucent draws its own panels. The integration can remove the generated configuration without replacing user settings.

07 / LUCENT APP ICONS

One family.
Each app distinct.

58 original pictograms share a shape, stroke weight and monochrome treatment. The launcher and dock select the same artwork by desktop-entry ID, so translated app names do not change their icon. These are Lucent interpretations, separate from the applications’ official logos.

Applicationapplication
Footterminal
Foot Clientterminal-client
Foot Serverterminal-server
Neovimeditor
Avahi Browsernetwork
Avahi SSHssh
Avahi VNCremote-screen
uuctlusb
System Restorerestore
btopmonitor
Chromiumbrowser
Camera Testcamera-test
Video Capturecapture
cliampmusic
Manage Printingprinting
Document Viewerdocuments
Input Methodinput-method
Hardware Localityhardware
Kdenlivevideo-editor
LibreOffice Basedatabase
LibreOffice Calcspreadsheet
LibreOffice Drawdrawing
LibreOffice Impressslides
LibreOffice Mathformula
LibreOfficeoffice
LibreOffice Writerwriter
LocalSendsend
Moonlightmoonlight
Media Playerplayer
Filesfiles
Disksdisks
OBS Studiostudio
Obsidiannotes-crystal
Omacalccalculator
Omacutcut
Omawritewrite
Pintapaint
Tensakugrammar
Print Settingsprinter-settings
Xournal++sketchbook
Aetheraether
Voxtypemicrophone
Basecampbasecamp
Discorddiscord
Disk Usagedisk-usage
Dockercontainers
Contactscontacts
Mapsmaps
Messagesmessages
Photosphotos
HEYmail
WhatsAppwhatsapp
Xsocial
YouTubeyoutube
Zoommeetings
Image Viewerimages
Lucent Desktoplucent

Edit design/app-icons.json to change the artwork or desktop-ID aliases. Glyphs use the theme’s semantic foreground on transparent backgrounds. Geometry lives in app_icon.* tokens. Unmapped apps retain their supplied icon; a Lucent application glyph is the final fallback.

Regenerate tokens, SVGs and the typed Rust catalog
python3 scripts/generate-design-tokens.py
python3 scripts/generate-app-icons.py
python3 scripts/generate-app-icons.py --check
NATIVE COMPONENT CAPTURES

From our renderer.

Open the component lab for selectable native control stories, palettes, focus/hover states and densities. The references below show production desktop compositions.

These are fixed Vulkan renders of the Rust components, taken from the visual-test baselines. The app names are fixtures; their icons come from the actual Lucent catalog. Keyboard selection, input alignment, switch geometry and seven visible rows are checked separately from the image comparison.

Dark native launcher with seven complete rows, keyboard-selected last row and focused search field
Scrolled launcher · keyboard selection · 2×
Light native widget selector with drawn switches and a keyboard-selected Focus widget
Widget selector · semantic focus · 2×
Native notification with wrapped content and an application action
Notification · shared component tokens · 2×
Native session screen with masked fixture input and a primary unlock action
Secure session UI · fixture data · 2×
Native keybinding picker with an aligned search field and selected shortcut
Keybinding menu · native selection · 2×
Native System submenu with Back, Close, search and disabled action
Submenu · shared picker component · 2×
Desktop widgets over a token-spaced grid visible during dragging
Drag grid · decorative Element composition · 2×

Shell icons use pinned, Apache-2.0 Material Symbols Rounded SVGs and an original Lucent notification bell. Images keep their aspect ratio. Focus, caret and toggle geometry use named component tokens. Run the native component checks ↗

08 / THE SOURCE, EXPLORABLE

Find your token.

This catalog is generated directly from design/tokens.json. Copy a Rust constant path or a CSS custom property.

50 tokens

theme.dark.surface↳ palette.navy_700
#2c5184
theme.dark.surface_container↳ palette.cyan_700
#1b6a7d
theme.dark.on_surface↳ palette.cyan_200
#bce8f4
theme.dark.primary↳ palette.cyan_300
#73dcee
theme.dark.on_primary↳ palette.aqua_950
#153f5b
theme.dark.error↳ palette.coral_200
#ffcfbc
theme.dark.success↳ palette.green_300
#a7ead3
theme.dark.warning↳ palette.amber_200
#f4d48f
theme.dark.info↳ palette.cyan_200
#bce8f4
theme.dark.focus↳ palette.cyan_300
#73dcee
theme.light.surface↳ palette.sky_200
#badbea
theme.light.surface_container↳ palette.aqua_100
#b1e9eb
theme.light.on_surface↳ palette.navy_950
#17334b
theme.light.primary↳ palette.cyan_800
#176075
theme.light.on_primary↳ palette.white
#ffffff
theme.light.error↳ palette.coral_800
#9c2e16
theme.light.success↳ palette.green_800
#236144
theme.light.warning↳ palette.amber_800
#754e08
theme.light.info↳ palette.cyan_800
#176075
theme.light.focus↳ palette.cyan_800
#176075
theme.ocean.surface↳ palette.ocean_surface
#15232d
theme.ocean.surface_container↳ palette.ocean_surface_container
#243845
theme.ocean.on_surface↳ palette.ocean_on_surface
#e5f2fb
theme.ocean.primary↳ palette.ocean
#83c7eb
theme.ocean.on_primary↳ palette.ocean_on_primary
#12252f
theme.ocean.error↳ palette.coral_200
#ffcfbc
theme.ocean.success↳ palette.green_300
#a7ead3
theme.ocean.warning↳ palette.amber_200
#f4d48f
theme.ocean.info↳ palette.cyan_200
#bce8f4
theme.ocean.focus↳ palette.ocean
#83c7eb
theme.forest.surface↳ palette.forest_surface
#18261f
theme.forest.surface_container↳ palette.forest_surface_container
#293b30
theme.forest.on_surface↳ palette.forest_on_surface
#e4f2e8
theme.forest.primary↳ palette.forest
#9ed8ae
theme.forest.on_primary↳ palette.forest_on_primary
#142a1c
theme.forest.error↳ palette.coral_200
#ffcfbc
theme.forest.success↳ palette.green_300
#a7ead3
theme.forest.warning↳ palette.amber_200
#f4d48f
theme.forest.info↳ palette.cyan_200
#bce8f4
theme.forest.focus↳ palette.forest
#9ed8ae
theme.ember.surface↳ palette.ember_surface
#2b201c
theme.ember.surface_container↳ palette.ember_surface_container
#423027
theme.ember.on_surface↳ palette.ember_on_surface
#fbece5
theme.ember.primary↳ palette.ember
#f6b18e
theme.ember.on_primary↳ palette.ember_on_primary
#312016
theme.ember.error↳ palette.coral_200
#ffcfbc
theme.ember.success↳ palette.green_300
#a7ead3
theme.ember.warning↳ palette.amber_200
#f4d48f
theme.ember.info↳ palette.cyan_200
#bce8f4
theme.ember.focus↳ palette.ember
#f6b18e