GitHub
Core
Glass
Brutal
Humanist

Theming

Versa UI ships with a powerful multi-theme architecture built on CSS custom properties (design tokens). Each theme defines a complete set of tokens that control colours, typography, spacing, corner radii, shadows, and effects — and every component adapts automatically.

Use the theme switcher in the navigation bar to preview all four themes in real time.


Available Themes

Versa UI includes 4 visual themes, each with light and dark modes:

ThemeDescription
coreA versatile, balanced style that feels modern and familiar, suitable for most products and use cases.
glassmorphismA visually rich style that emphasizes depth, layering and translucency for glass interfaces.
brutalismA bold style that favors raw simplicity, high contrast, clarity and functional directness.
humanistA subtle and understated style that prioritizes restraint and a natural visual tone.

Importing Themes

Import theme CSS files in your main stylesheet, after primitives.css:

Core Theme (Free)

/* Design tokens */
@import "@versaui/ui/styles/primitives.css";

/* Core theme — included free */
@import "@versaui/ui/styles/themes/core.css";

Glassmorphism Theme (Pro)

@import "@versaui/ui/styles/themes/glassmorphism.css";

Brutalism Theme (Pro)

@import "@versaui/ui/styles/themes/brutalism.css";

Humanist Theme (Pro)

@import "@versaui/ui/styles/themes/humanist.css";

Import only the themes you plan to use. Each theme CSS file is self-contained — importing multiple themes adds support for runtime switching between them.


Activating a Theme

Set the active theme and colour mode using data-theme and data-mode attributes on your root element:

<html data-theme="core" data-mode="light">
  <!-- or -->
<html data-theme="brutalism" data-mode="dark">

All Versa UI components will automatically adapt to the active theme and mode.


Dynamic Theme Switching

Toggle themes and modes programmatically at runtime:

function ThemeSwitcher() {
  const setTheme = (theme: 'core' | 'glassmorphism' | 'brutalism' | 'humanist') => {
    document.documentElement.dataset.theme = theme;
  };

  const setMode = (mode: 'light' | 'dark') => {
    document.documentElement.dataset.mode = mode;
  };

  return (
    <div>
      <button onClick={() => setTheme('core')}>Core Theme</button>
      <button onClick={() => setTheme('glassmorphism')}>Glassmorphism Theme</button>
      <button onClick={() => setTheme('brutalism')}>Brutalism Theme</button>
      <button onClick={() => setTheme('humanist')}>Humanist Theme</button>
      <button onClick={() => setMode('light')}>Light Mode</button>
      <button onClick={() => setMode('dark')}>Dark Mode</button>
    </div>
  );
}

System Preference Detection

Respect the user's system colour scheme preference:

import { useEffect } from 'react';

function useSystemColorScheme() {
  useEffect(() => {
    const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
    
    const updateMode = (e: MediaQueryListEvent | MediaQueryList) => {
      document.documentElement.dataset.mode = e.matches ? 'dark' : 'light';
    };

    updateMode(mediaQuery);
    mediaQuery.addEventListener('change', updateMode);
    
    return () => mediaQuery.removeEventListener('change', updateMode);
  }, []);
}

Typography per Theme

Each theme uses a different combination of font families. All fonts are loaded via the Google Fonts import in your CSS.

ThemeDisplay (H1–H5)Title (H6–H9)BodyCode
CoreManropeManropeManropeDM Mono
BrutalismSpace GroteskSpace GroteskDM SansDM Mono
GlassmorphismDM SansDM SansDM SansDM Mono
HumanistDM Serif DisplayPlus Jakarta SansPlus Jakarta SansDM Mono

Customizing Themes

Versa UI is fully customizable. All styling is controlled via CSS custom properties (tokens), making it easy to adapt the design system to your brand.

Changing Fonts

Override the typography tokens in your CSS to use your own brand fonts:

@layer base {
  :root {
    /* Replace with your brand fonts */
    --typography-font-family-body: 'Inter', system-ui, sans-serif;
    --typography-font-family-display: 'Poppins', system-ui, sans-serif;
    --typography-font-family-title: 'Inter', system-ui, sans-serif;
    
    /* Adjust font weights if needed */
    --typography-font-weight-body: 400;
    --typography-font-weight-display: 700;
    --typography-font-weight-title: 600;
  }
}

Don't forget to import your custom fonts:

@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300..700&family=Poppins:wght@400;600;700&display=swap');

Changing Brand Colors

Override the brand color tokens to match your brand palette:

@layer base {
  :root {
    /* Primary brand color (used for buttons, links, focus states) */
    --color-brand-primary-medium: #6366F1;    /* Main brand color */
    --color-brand-primary-strong: #4F46E5;    /* Darker shade */
    --color-brand-primary-stronger: #4338CA;  /* Even darker */
    --color-brand-primary-subtle: #818CF8;    /* Lighter shade */
    --color-brand-primary-subtler: #C7D2FE;   /* Even lighter */
    --color-brand-primary-subtlest: #EEF2FF;  /* Lightest/background */
    
    /* Secondary, tertiary, quaternary colors follow same pattern */
    --color-brand-secondary-medium: #8B5CF6;
    --color-brand-secondary-strong: #7C3AED;
    /* ... */
  }
  
  /* Dark mode overrides */
  [data-mode="dark"] {
    --color-brand-primary-medium: #818CF8;
    --color-brand-primary-strong: #A5B4FC;
    --color-brand-primary-subtle: #4F46E5;
    --color-brand-primary-subtler: #312E81;
    --color-brand-primary-subtlest: #1E1B4B;
  }
}

Use a tool like Realtime Colors or Coolors to generate a harmonious color palette, then map the shades to the Versa UI token structure.

Changing Corner Radius, Spacing & Shadows

@layer base {
  :root {
    /* Corner radius — default and control */
    --corner-radius-default-medium: 8px;
    --corner-radius-control-medium: 6px;

    /* Spacing scale (1–13) */
    --spacing-4: 8px;
    --spacing-6: 16px;
    --spacing-8: 24px;

    /* Elevation shadows */
    --elevation-small-1-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
    --elevation-medium-1-shadow: 0 2px 8px 0 rgba(0, 0, 0, 0.06);
    --elevation-large-1-shadow: 0 3px 12px 0 rgba(0, 0, 0, 0.08);
  }
}

Creating a Custom Theme

To create an entirely new theme, define all tokens under a new data-theme selector:

@layer base {
  [data-theme="my-brand"][data-mode="light"] {
    /* All your custom token values */
    --typography-font-family-body: 'Your Font', sans-serif;
    --color-brand-primary-medium: #YOUR_COLOR;
    --corner-radius-default-medium: 0px; /* Sharp corners */
    /* ... rest of tokens */
  }
  
  [data-theme="my-brand"][data-mode="dark"] {
    /* Dark mode variants */
  }
}

Then activate it:

<html data-theme="my-brand" data-mode="light">

When creating custom themes, ensure you define both light and dark mode variants for all color tokens. Missing tokens will fall back to the default theme values.


Expressive Style

Expressive Style is an enhanced, theme-adaptive visual treatment that adds color, depth, and visual emphasis to selected UI elements. It is available as a component style variant across interactive components such as Button, Segmented Control, Container Tabs, Side Navigation Item, Contained Slider, Alert, Toast, and Status Tag.

Expressive Tokens & Architecture

Versa UI uses expressive tokens to define the visual characteristics of Expressive Style. These tokens are composed into ready-to-use CSS styles that can be applied directly to UI elements:

  • Expressive Color Tokens (--color-expressive-fill-*, --color-expressive-outline-*) — Define the individual color stops (top, middle, and bottom) for fills and outlines across semantic roles (Primary, Secondary, Success, Warning, Error, and Neutral). Depending on the active theme, they produce smooth gradients or distinct theme-specific boundaries.
  • Expressive Gradient Styles (--gradient-expressive-fill-*, --gradient-expressive-outline-*) — Combine top and bottom color tokens into pre-configured linear gradients that can be applied directly to UI elements via CSS background.
  • Expressive Effect Tokens (--effects-expressive-inset-*) — Define directional depth through inner-shadow offsets, blur radii, and specular highlight/ambient shadow colors.
  • Expressive Inset Styles (--expressive-inset-*) — Combine inset effect tokens and outer elevation into ready-to-use layered box-shadow styles (small, medium, and large) that add tactile depth to controls.

Because these tokens are theme-adaptive, the same Expressive Style takes on a different visual treatment across themes (such as soft layered depths in Core, frosted translucency in Glassmorphism, sharp high-contrast fills in Brutalism, and rich classical gradients in Humanist) while preserving the intended visual emphasis.

Using Expressive Style in React Components

To enable Expressive Style on supported components, pass style="expressive" (or buttonStyle="expressive" on Button).

Components configured with an Expressive variant also automatically pair with a duotone icon style, allowing icons to pair seamlessly with the expressive fill, outline, and depth treatment:

import { Button } from '@versaui/ui/components/Button';
import { Alert } from '@versaui/ui/components/Alert';
import { StatusTag } from '@versaui/ui/components/Tag';
import { Sparkle, CheckCircle, ShieldWarning } from '@phosphor-icons/react';

export function ExpressiveDemo() {
  return (
    <div className="flex flex-col gap-4">
      {/* Expressive Button with duotone icon */}
      <Button
        variant="primary"
        buttonStyle="expressive"
        leadingIcon={<Sparkle />}
      >
        Upgrade to Pro
      </Button>

      {/* Expressive Alert with gradient background and inset shadow */}
      <Alert
        state="success"
        style="expressive"
        title="Deployment Complete"
        icon={<CheckCircle />}
      >
        All theme tokens and assets compiled successfully.
      </Alert>

      {/* Expressive Status Tag */}
      <StatusTag
        status="warning"
        style="expressive"
        icon={<ShieldWarning />}
        label="Action Required"
      />
    </div>
  );
}

Custom Expressive Elements

Expressive Color and Effect Styles can also be applied independently to custom UI elements, allowing you to create expressive treatments beyond predefined component variants:

export function CustomExpressiveCard({
  title,
  children,
}: {
  title: string;
  children: React.ReactNode;
}) {
  return (
    <div
      className="p-6 rounded-xl border border-transparent"
      style={{
        background: `
          var(--gradient-expressive-fill-neutral) padding-box,
          var(--gradient-expressive-outline-neutral) border-box
        `,
        backgroundOrigin: 'border-box',
        backgroundClip: 'padding-box, border-box',
        boxShadow: 'var(--expressive-inset-subtle-medium)',
      }}
    >
      <h3 className="text-sm font-semibold text-[var(--color-neutral-text-strong)] mb-1">
        {title}
      </h3>
      <p className="text-sm text-[var(--color-neutral-text-medium)]">
        {children}
      </p>
    </div>
  );
}

Theme Source Files

Browse the complete CSS source for each theme below. Pro themes require a paid license to access.

Loading core theme source...

On this page