Design and style UI components for ryOS following the four OS themes (System 7, macOS Aqua, Windows XP, Windows 98). Use this when creating UI components, styling elements, working with themes, adding visual effects, or implementing retro OS aesthetics.
docs/3.3.1-theme-architecture.mddocs/3.3-theme-system.mdsrc/styles/themes/tokens.csssrc/styles/themes.csstailwind.config.jssrc/themes/, src/stores/useThemeStore.ts, src/hooks/useThemeFlags.tsVisual values belong in CSS --os-* tokens. TypeScript theme files hold metadata, platform behavior, dark-mode support flags, and wallpaper defaults; do not duplicate palettes in TS or component code.
| Theme | ID | Platform | Key traits |
|-------|----|----------|------------|
| macOS Aqua | macosx | mac / aqua | Glossy controls, traffic lights, dock, pinstripe or Aqua Glass, light/dark, accents |
| System 7 | system7 | mac / system7 | Black/white, square corners, Chicago-style type, dotted titlebars, accents |
| Windows XP | xp | windows | Luna blue chrome, rounded window borders, taskbar, legacy XP CSS |
| Windows 98 | win98 | windows | Gray bevels, square corners, classic taskbar, legacy 98 CSS |
Default theme is macosx; default Aqua material is glass.
useThemeStore applies attributes to <html>. Prefer targeting these through tokens and Tailwind variants.
| Attribute / class | Meaning |
|-------------------|---------|
| data-os-theme | Exact theme id: system7, macosx, xp, win98 |
| data-os-platform | Shared platform bucket: mac or windows |
| data-os-mac-chrome | Mac chrome variant: aqua or system7; absent for Windows |
| data-os-color-scheme="dark" | Present only when the active theme supports dark mode and dark is enabled |
| data-os-aqua-material="glass" | Present only for macOS Aqua Glass |
| data-os-accent | Present for non-default Mac chrome accents |
| data-os-system-font | Present for debug font overrides |
| .dark | Mirrors Aqua dark mode for Tailwind dark:* compatibility |
src/styles/themes.css imports theme CSS in this order:
tokens.css - defaults, per-theme token blocks, [data-selected="true"], z-index scale.platform.css - rules shared by mac or windows platform buckets.containment.css - reduced motion and third-party/app isolation.aqua.css - Aqua structural chrome, .aqua-button, brushed metal, typography.windows.css - Windows structural landing rules.dark-aqua.css - Aqua dark tokens and structural overrides.aqua-glass.css - Aqua Glass overrides, imported after dark Aqua.control-panels-mac.css / control-panels-themed.css - Control Panels skins.Windows themes also load /css/xp-custom.css or /css/98-custom.css dynamically. When a rule applies to both XP and Win98, use data-os-platform="windows" instead of duplicated exact-theme selectors.
Use token-backed Tailwind utilities first:
className="bg-os-window-bg border-os-window rounded-os shadow-os-window"
className="font-os-ui text-os-text-primary"
className="bg-os-panel-bg border-[length:var(--os-metrics-border-width)]"
className="bg-os-input-bg border-os-input-border focus:border-os-input-focusBorder"
className="text-os-link bg-os-selection-bg text-os-selection-text"
className="h-os-titlebar h-os-menubar z-menubar"
Core token groups:
--os-font-ui, --os-font-mono--os-color-window-bg, --os-color-panel-bg, --os-color-input-bg--os-color-window-border, --os-color-separator, --os-color-input-border--os-color-text-primary, --os-color-text-secondary, --os-color-text-disabled, --os-color-link--os-color-menubar-*, --os-color-titlebar-*, --os-color-button-*--os-color-selection-*, --os-color-selection-glow, --os-color-selection-ring-gap--os-metrics-*, --os-window-shadow--os-color-traffic-light-*, --os-pinstripe-*, --os-texture-*, --os-typography-*--z-base, --z-dialog, --z-menubar, --z-dropdown, --z-spotlightShadcn HSL variables (--background, --primary, etc.) still exist for generic UI primitives. For OS chrome and app surfaces, prefer --os-* tokens and bg-os-* / text-os-* utilities.
Use root-attribute variants for small static visual differences:
className={cn(
"bg-os-window-bg text-os-text-primary",
"os-windows:border-os os-mac-aqua:rounded-os",
"os-mac-system7:rounded-none os-theme-win98:shadow-none",
"os-dark:bg-os-window-bg os-mac-aqua-dark:text-os-text-primary"
)}
Available variants:
os-mac:, os-windows:os-mac-aqua:, os-mac-system7:os-theme-system7:, os-theme-macosx:, os-theme-xp:, os-theme-win98:os-dark:, os-mac-aqua-dark:, os-theme-<id>-dark:Prefer CSS variants and tokens over React theme branches when the DOM and behavior do not change.
Use useThemeFlags() for component decisions:
const {
currentTheme,
osPlatform,
macChrome,
metadata,
isWindowsTheme,
isMacTheme,
isMacOSTheme,
isSystem7Theme,
isWinXp,
isWin98,
isClassicTheme,
isAquaMenuChrome,
isMacAquaChrome,
supportsDarkMode,
isDarkMode,
darkModePreference,
supportsAccent,
accent,
aquaMaterial,
isAquaGlass,
} = useThemeFlags();
Use React branches only when structure, behavior, assets, layout math, or app logic differs. For non-React code, use useThemeStore.getState() or helpers from @/themes such as getOsPlatform, getOsMacChrome, isWindowsTheme, isMacTheme, isThemeWinXp, and isThemeWin98.
Prefer shared primitives before adding new four-way class branches:
import {
osCardClassName,
osDrawerSurfaceClassName,
osToolbarSurfaceClassName,
osAppSidebarSurfaceClassName,
osSeparatorBorderClassName,
osSubtleIconButtonClassName,
windowsBevelClassName,
} from "@/components/shared/osThemePrimitives";
Pass isAquaGlass when a primitive supports it. Use windowsBevelClassName("raised" | "sunken") instead of hand-writing Win98 bevel borders.
When choosing app icons, toolbar glyphs, file-type art, dialog icons, devices, folders, or other OS-flavored imagery, search the repo icon libraries before drawing new assets or using external sources.
Active theme icons live under public/icons/<theme>/... and are resolved through public/icons/manifest.json by helpers such as pickIconPath, resolveIconLegacyAware, and useIconPath. Historical source libraries live outside active theme resolution:
public/resources/macos-icon-catalogs/{panther,tiger}/catalog.mdpublic/resources/windows-icon-catalogs/{win98,xp}/catalog.mdUse this order:
public/icons/default, then matching theme variants in public/icons/macosx, public/icons/win98, or public/icons/xp.applications, system-preferences, control-panels, dialog-ui-assets, folders, devices, and file-types.public/icons/<theme>/... only when it should participate in active UI rendering; keep public/resources/... as the source catalog, not as runtime app metadata.bun run generate:icons after changing active public/icons files.default assets first, then theme-specific variants where they materially improve the UI.Useful searches:
rg -i "calculator|paint|printer|warning|folder" public/resources/*-icon-catalogs
rg -i "\"themes\"|\"macosx\"|\"win98\"|\"xp\"" public/icons/manifest.json
import { Button } from "@/components/ui/button";
<Button variant="default">Standard</Button>
<Button variant="secondary">Secondary</Button>
<Button variant="retro">Retro</Button>
<Button variant="aqua">Aqua</Button>
Button maps variants to .aqua-button on macOS Aqua and legacy .button on Windows.
<button className="aqua-button">Default</button>
<button className="aqua-button primary">Primary</button>
<button className="aqua-button secondary">Secondary</button>
<button className="aqua-button orange">Orange</button>
<div
className={cn(
"rounded-os bg-os-window-bg text-os-text-primary shadow-os-window",
"border-[length:var(--os-metrics-border-width)] border-os-window",
"os-theme-win98:shadow-none"
)}
>
Use the built-in selection utility when possible:
<div data-selected={isSelected ? "true" : undefined}>Song</div>
macosx; it is driven by data-os-aqua-material="glass" and src/styles/themes/aqua-glass.css.bg-white/80 backdrop-blur-* unless the surface is intentionally outside the OS material system.macosx currently supports dark mode. Use --os-* tokens, os-dark:, or os-mac-aqua-dark:; branch on isDarkMode only for behavioral or structural differences.macosx, system7) support accents. The default accent is wallpaper; default means "System" and clears inline overrides so stylesheet tokens win.--os-color-selection-*, --os-color-link, focus-ring tokens, or Tailwind bg-os-selection-bg / text-os-link.WindowFrame supports per-window materials:
| Material | Use case |
|----------|----------|
| default | Standard opaque windows |
| transparent | Semi-transparent app windows such as media surfaces |
| notitlebar | Immersive windows with floating or hover chrome |
| brushedmetal | Classic Mac brushed-metal apps |
Global Aqua Glass is separate from WindowFrame material. Regular Aqua windows receive glass classes when the global material is glass; brushed-metal windows keep brushed-metal semantics and are adjusted by CSS.
font-os-ui and font-os-mono; avoid theme-specific font utility names.WindowFrame content has .window-body, which consumes --os-typography-window.WindowFrame, use OS_SHELL_TEXT_SCALE_CLASS from @/lib/themeChrome.OS_NATIVE_CHROME_SKIP_CLASS on an ancestor.prose-textedit typography variant for TextEdit-like rich content so Aqua dark mode stays readable.--os-* tokens.currentTheme === "xp" || currentTheme === "win98" branches for shared Windows styling; use platform attributes, variants, or helpers.src/components/shared/osThemePrimitives.ts and existing app patterns.cn() for conditional class merging.--os-* tokens, token-backed Tailwind utilities, and os-*: variants.useThemeFlags() or @/themes helpers only when structure or behavior changes.macosx surfaces.Search for places (restaurants, cafes, etc.) via Google Places API proxy on localhost.
Interact with GitHub using the `gh` CLI. Use `gh issue`, `gh pr`, `gh run`, and `gh api` for issues, PRs, CI runs, and advanced queries.
Create or update AgentSkills. Use when designing, structuring, or packaging skills with scripts, references, and assets.
Start voice calls via the OpenClaw voice-call plugin.
Notion API for creating and managing pages, databases, and blocks.
Gemini CLI for one-shot Q&A, summaries, and generation.
Category:developer