Skip to content

Theming ​

The macOS UI theming system is built on CSS custom properties, a Tailwind CSS preset, and a programmatic API for runtime theme changes.

CSS Custom Properties ​

All visual aspects of the components — colors, border radii, blur effects, and shadows — are controlled by --macos-* CSS variables defined on :root. This makes it trivial to customize the look of every component by overriding a few variables.

Full Variable Reference ​

Colors ​

VariableLight Mode DefaultDark Mode Default
--macos-backgroundhsl(0 0% 100%)hsl(222.2 84% 4.9%)
--macos-foregroundhsl(222.2 84% 4.9%)hsl(210 40% 98%)
--macos-primaryhsl(221.2 83.2% 53.3%)hsl(217.2 91.2% 59.8%)
--macos-secondaryhsl(210 40% 96.1%)hsl(217.2 32.6% 17.5%)
--macos-accenthsl(210 40% 96.1%)hsl(217.2 32.6% 17.5%)
--macos-mutedhsl(210 40% 96.1%)hsl(217.2 32.6% 17.5%)
--macos-borderhsl(214.3 31.8% 91.4%)hsl(217.2 32.6% 17.5%)
--macos-windowBackgroundhsla(0 0% 100% / 0.8)hsla(222.2 84% 4.9% / 0.8)
--macos-windowBorderhsl(214.3 31.8% 91.4%)hsl(217.2 32.6% 17.5%)
--macos-dockBackgroundhsla(0 0% 100% / 0.7)hsla(222.2 84% 4.9% / 0.7)
--macos-menuBarBackgroundhsla(0 0% 100% / 0.8)hsla(222.2 84% 4.9% / 0.8)

Border Radius ​

VariableDefault
--macos-radius-sm0.25rem
--macos-radius-md0.5rem
--macos-radius-lg0.75rem
--macos-radius-window0.75rem
--macos-radius-dock1rem

Blur ​

VariableDefault
--macos-blur-window20px
--macos-blur-dock40px
--macos-blur-menuBar20px

Shadows ​

VariableLight Mode DefaultDark Mode Default
--macos-shadow-window0 10px 40px rgba(0, 0, 0, 0.1)0 10px 40px rgba(0, 0, 0, 0.5)
--macos-shadow-windowActive0 20px 60px rgba(0, 0, 0, 0.2)0 20px 60px rgba(0, 0, 0, 0.7)
--macos-shadow-dock0 10px 30px rgba(0, 0, 0, 0.15)0 10px 30px rgba(0, 0, 0, 0.5)

Overriding Variables in CSS ​

To customize a theme, override the variables in your CSS:

css
/* Custom light theme */
:root {
  --macos-primary: hsl(262 83% 58%);        /* Purple accent */
  --macos-windowBackground: hsla(262 30% 97% / 0.9);
  --macos-dockBackground: hsla(262 30% 97% / 0.8);
  --macos-radius-window: 1rem;               /* Rounder windows */
}

/* Custom dark theme */
@media (prefers-color-scheme: dark) {
  :root {
    --macos-primary: hsl(262 83% 68%);
    --macos-windowBackground: hsla(262 30% 10% / 0.9);
    --macos-dockBackground: hsla(262 30% 10% / 0.8);
  }
}

Using the Tailwind Preset ​

The Tailwind preset maps CSS variables to utility classes. Add it to your config:

ts
// tailwind.config.ts
export default {
  presets: [require('@sylonik/macos-ui/tailwind.preset')],
}

This gives you utilities like:

html
<!-- Colors -->
<div class="bg-macos-background text-macos-foreground" />
<div class="bg-macos-primary" />
<div class="bg-macos-window-background border-macos-window-border" />

<!-- Border radius -->
<div class="rounded-macos-window" />
<div class="rounded-macos-dock" />

<!-- Backdrop blur -->
<div class="backdrop-blur-macos-window" />
<div class="backdrop-blur-macos-dock" />

<!-- Shadows -->
<div class="shadow-macos-window" />
<div class="shadow-macos-window-active" />

<!-- Animations -->
<div class="animate-window-appear" />
<div class="animate-dock-bounce" />

Programmatic Theming ​

The library exports functions for runtime theme manipulation:

applyTheme(config, root?) ​

Applies a ThemeConfig object to CSS custom properties:

ts
import { applyTheme } from '@/lib/theme'
import { defaultTheme } from '@/lib/theme.types'

// Apply a custom theme
applyTheme({
  ...defaultTheme,
  colors: {
    ...defaultTheme.colors,
    primary: 'hsl(262 83% 58%)',
  },
})

The optional root parameter defaults to document.documentElement. Pass a different element to scope the theme.

getTheme(root?) ​

Reads the current theme from computed CSS variables:

ts
import { getTheme } from '@/lib/theme'

const currentTheme = getTheme()
console.log(currentTheme.colors.primary) // "hsl(221.2 83.2% 53.3%)"

mergeWithDefaults(partial) ​

Merges a partial theme configuration with the default theme:

ts
import { mergeWithDefaults } from '@/lib/theme'

const myTheme = mergeWithDefaults({
  colors: { primary: 'hsl(262 83% 58%)' },
  radius: { window: '1rem' },
})
// All other values filled from defaultTheme

serializeTheme(config) / deserializeTheme(json) ​

Serialize a theme to JSON for storage, and deserialize it back:

ts
import { serializeTheme, deserializeTheme } from '@/lib/theme'

// Save to localStorage
const json = serializeTheme(myTheme)
localStorage.setItem('macos-theme', json)

// Restore
const saved = localStorage.getItem('macos-theme')
if (saved) {
  const theme = deserializeTheme(saved)
  applyTheme(theme)
}

ThemeConfig Interface ​

ts
interface ThemeConfig {
  colors: ThemeColors
  radius: ThemeRadius
  blur: ThemeBlur
  shadows: ThemeShadows
}

interface ThemeColors {
  background: string
  foreground: string
  primary: string
  secondary: string
  accent: string
  muted: string
  border: string
  windowBackground: string
  windowBorder: string
  dockBackground: string
  menuBarBackground: string
}

interface ThemeRadius {
  sm: string
  md: string
  lg: string
  window: string
  dock: string
}

interface ThemeBlur {
  window: string
  dock: string
  menuBar: string
}

interface ThemeShadows {
  window: string
  windowActive: string
  dock: string
}

Creating a Custom Theme ​

Here's a complete example of creating and applying a custom purple theme:

ts
import type { ThemeConfig } from '@/lib/theme.types'
import { applyTheme, mergeWithDefaults } from '@/lib/theme'

const purpleTheme: ThemeConfig = mergeWithDefaults({
  colors: {
    primary: 'hsl(262 83% 58%)',
    accent: 'hsl(262 60% 90%)',
    windowBackground: 'hsla(262 30% 97% / 0.85)',
    windowBorder: 'hsl(262 30% 85%)',
    dockBackground: 'hsla(262 30% 97% / 0.75)',
    menuBarBackground: 'hsla(262 30% 97% / 0.85)',
  },
  radius: {
    window: '1rem',
    dock: '1.25rem',
  },
  blur: {
    window: '24px',
    dock: '48px',
  },
})

// Apply on load
applyTheme(purpleTheme)

Dark Mode Support ​

By default, the theme CSS uses @media (prefers-color-scheme: dark) to switch variables automatically based on the user's OS preference.

For manual dark mode control (e.g., a toggle), see the Configuration guide.

Animation Keyframes ​

The Tailwind preset includes animation keyframes for all components:

AnimationKeyframeDurationEasing
animate-window-appearScale 0.95 + fade in0.2sease-out
animate-window-disappearScale 0.95 + fade out0.2sease-in
animate-window-minimizeScale 0.1 + slide to bottom0.3sease-in
animate-window-restoreScale 0.1 + slide from bottom0.3sease-out
animate-dock-bounceTranslate Y -10px0.5sease-in-out
animate-dock-item-appearScale 0.5 + fade in0.2sease-out
animate-dock-magnifyScale to 1.50.2sease-out (forwards)
animate-menu-slide-downTranslate Y -10px + fade in0.15sease-out
animate-menu-slide-upTranslate Y 0 + fade out0.15sease-in
animate-icon-bounceTranslate Y -5px0.3sease-in-out
animate-icon-wiggleRotate +/-2deg0.3sease-in-out

See Also ​

  • Configuration — Tailwind preset details and CSS variable reference.
  • Theme API — Complete type definitions for theme interfaces.

Released under the MIT License.