Theming

Customize SoftUI to match your brand by overriding CSS custom properties. No build tools needed.

How It Works

SoftUI is built entirely on CSS custom properties (--sui-*). Override them on :root to change the look of every component at once.

:root {
  --sui-primary: #7C5CFC;         /* Your brand color */
  --sui-radius: 12px;             /* Sharper or rounder corners */
  --sui-font: 'Inter', sans-serif; /* Your font */
}

That's it. Every button, card, input, and component will update automatically.

Colors

The color system has 5 semantic colors, each with a hover variant.

:root {
  /* Primary — buttons, links, active states */
  --sui-primary: #5B54E0;
  --sui-primary-hover: #4A44C4;

  /* Success — confirmations, positive actions */
  --sui-success: #1FA96E;
  --sui-success-hover: #178A59;

  /* Danger — errors, destructive actions */
  --sui-danger: #D03A5C;
  --sui-danger-hover: #B42E4C;

  /* Warning — caution, pending states */
  --sui-warning: #F5A623;
  --sui-warning-hover: #DB921A;

  /* Info — informational, neutral highlights */
  --sui-info: #1A82D4;
  --sui-info-hover: #146BAE;
}

Backgrounds & Surfaces

Three background shades create the neumorphic depth. bg is the base, bg-light is slightly lighter, bg-dark is slightly darker.

:root {
  --sui-bg: #E4E9F0;       /* Base surface */
  --sui-bg-light: #EDF1F7;  /* Lighter (raised highlights) */
  --sui-bg-dark: #D1D9E6;   /* Darker (borders, tracks) */
}

These three values work together to create the soft shadow illusion. When changing them, keep them close in tone — the effect breaks if they're too far apart.

Shadows

Neumorphic shadows use two colors: a dark shadow (bottom-right) and a light shadow (top-left). These are combined into preset shadow values.

:root {
  /* Shadow colors */
  --sui-shadow-light: #FFFFFF;
  --sui-shadow-dark: #B8C0CC;

  /* Raised (outward) shadows */
  --sui-shadow-raised-sm: 3px 3px 8px var(--sui-shadow-dark),
                          -3px -3px 8px var(--sui-shadow-light);
  --sui-shadow-raised:    6px 6px 14px var(--sui-shadow-dark),
                          -6px -6px 14px var(--sui-shadow-light);
  --sui-shadow-raised-lg: 10px 10px 20px var(--sui-shadow-dark),
                          -10px -10px 20px var(--sui-shadow-light);

  /* Inset (pressed) shadows */
  --sui-shadow-inset:    inset 3px 3px 8px var(--sui-shadow-dark),
                         inset -3px -3px 8px var(--sui-shadow-light);
  --sui-shadow-inset-sm: inset 2px 2px 5px var(--sui-shadow-dark),
                         inset -2px -2px 5px var(--sui-shadow-light);
}

/* Tip: just changing --sui-shadow-light and --sui-shadow-dark
   updates ALL shadows automatically */

Text Colors

Three text color tiers for hierarchy.

:root {
  --sui-text: #2D3748;       /* Primary text — headings, body */
  --sui-text-muted: #5A6A7E;  /* Secondary — descriptions, labels */
  --sui-text-light: #6A7D94;  /* Tertiary — hints, placeholders */
}

Border Radius

Five radius tokens control the roundness across all components.

:root {
  --sui-radius-xs: 6px;        /* Small elements (chips, badges) */
  --sui-radius-sm: 10px;       /* Inputs, buttons */
  --sui-radius: 16px;          /* Cards, modals */
  --sui-radius-lg: 24px;       /* Large containers */
  --sui-radius-full: 9999px;   /* Pills, circles */
}

/* Sharp look */
:root {
  --sui-radius-xs: 2px;
  --sui-radius-sm: 4px;
  --sui-radius: 6px;
  --sui-radius-lg: 8px;
}

/* Extra round */
:root {
  --sui-radius-xs: 10px;
  --sui-radius-sm: 14px;
  --sui-radius: 20px;
  --sui-radius-lg: 32px;
}

Typography

Change the font family used across all components.

:root {
  --sui-font: 'Plus Jakarta Sans', -apple-system, BlinkMacSystemFont,
              'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;
}

/* Use Inter */
:root {
  --sui-font: 'Inter', sans-serif;
}

/* Use system fonts only (no Google Fonts) */
:root {
  --sui-font: -apple-system, BlinkMacSystemFont, 'Segoe UI',
              Roboto, Oxygen, Ubuntu, sans-serif;
}

/* Monospace font (used in Copy Button, Color Picker, code elements) */
:root {
  --sui-font-mono: 'JetBrains Mono', 'Fira Code', monospace;
}

Component Variables

Some components expose their own CSS variables for fine-tuning.

/* Modal — backdrop blur amount */
.sui-modal-backdrop {
  --sui-modal-blur: 6px;  /* or use classes: sui-modal-blur-none/sm/md/lg/xl */
}

/* Stack — hover spread distance and multiplier */
.sui-stack-hover {
  --sui-stack-gap: 180px;    /* base spread distance (match your card width) */
  --sui-stack-spread: 0.6;   /* multiplier (0.15 to 1) */
}

Transitions

Three speed tokens for animation timing.

:root {
  --sui-transition-fast: 0.15s;  /* Hover states, toggles */
  --sui-transition-base: 0.25s;  /* Most animations */
  --sui-transition-slow: 0.35s;  /* Page transitions, modals */
  --sui-transition: all var(--sui-transition-base) ease;
}

/* Snappier feel */
:root {
  --sui-transition-fast: 0.1s;
  --sui-transition-base: 0.15s;
  --sui-transition-slow: 0.25s;
}

/* Disable all transitions */
:root {
  --sui-transition-fast: 0s;
  --sui-transition-base: 0s;
  --sui-transition-slow: 0s;
}

Dark Mode

Dark mode overrides backgrounds, shadows, and text colors. Add data-theme="dark" to your <html> element.

<html data-theme="dark">

/* Dark mode variables (already built-in) */
[data-theme="dark"] {
  --sui-bg: #2A2D35;
  --sui-bg-light: #31343C;
  --sui-bg-dark: #23262D;

  --sui-shadow-light: #33363F;
  --sui-shadow-dark: #1E2027;

  --sui-text: #E2E8F0;
  --sui-text-muted: #9BA5B8;
  --sui-text-light: #8A94A6;
}

/* Custom dark theme */
[data-theme="dark"] {
  --sui-bg: #1A1A2E;
  --sui-bg-light: #222240;
  --sui-bg-dark: #141427;
  --sui-primary: #9B59B6;
}

Dark Mode Toggle

Add a toggle button and a few lines of JS to let users switch themes. This also respects system preference and persists the choice.

<!-- Toggle button -->
<button onclick="toggleTheme()">Toggle Dark Mode</button>

<script>
  function toggleTheme() {
    var html = document.documentElement;
    var isDark = html.getAttribute('data-theme') === 'dark';
    html.setAttribute('data-theme', isDark ? 'light' : 'dark');
    localStorage.setItem('sui-theme', isDark ? 'light' : 'dark');
  }

  // Restore saved preference on load
  (function() {
    var saved = localStorage.getItem('sui-theme');
    if (saved) {
      document.documentElement.setAttribute('data-theme', saved);
    } else if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
      document.documentElement.setAttribute('data-theme', 'dark');
    }
  })();

  // Listen for system theme changes
  window.matchMedia('(prefers-color-scheme: dark)')
    .addEventListener('change', function(e) {
      if (!localStorage.getItem('sui-theme')) {
        document.documentElement.setAttribute(
          'data-theme', e.matches ? 'dark' : 'light'
        );
      }
    });
</script>

Priority: saved user preference > system preference > default (light).

Full Example

A complete brand override — just drop this in your CSS after importing SoftUI.

/* My Brand Theme */
:root {
  /* Colors */
  --sui-primary: #0066FF;
  --sui-primary-hover: #0052CC;
  --sui-success: #00C853;
  --sui-danger: #FF1744;

  /* Surfaces */
  --sui-bg: #F0F2F5;
  --sui-bg-light: #F8F9FA;
  --sui-bg-dark: #E4E6EB;
  --sui-shadow-light: #FFFFFF;
  --sui-shadow-dark: #C8CCD4;

  /* Typography */
  --sui-font: 'Inter', sans-serif;
  --sui-text: #1A1A1A;

  /* Shape */
  --sui-radius: 8px;
  --sui-radius-sm: 4px;
  --sui-radius-lg: 12px;

  /* Speed */
  --sui-transition-base: 0.2s;
}

Design Tokens (JSON)

SoftUI ships a tokens.json file with all design values. Use it to sync with Tailwind, Figma plugins, Style Dictionary, or your own design system.

/* Import tokens in JavaScript */
import tokens from 'softui-css/dist/tokens.json';

console.log(tokens.colors.primary);     // "#5B54E0"
console.log(tokens.radius.default);     // "16px"
console.log(tokens.typography.font);    // "'Plus Jakarta Sans', ..."
/* Use with Tailwind CSS */
const tokens = require('softui-css/dist/tokens.json');

module.exports = {
  theme: {
    extend: {
      colors: {
        primary: tokens.colors.primary,
        success: tokens.colors.success,
        danger: tokens.colors.danger,
      },
      borderRadius: {
        soft: tokens.radius.default,
        'soft-sm': tokens.radius.sm,
        'soft-lg': tokens.radius.lg,
      }
    }
  }
};
/* tokens.json structure */
{
  "colors": {
    "primary": "#5B54E0",
    "primary-hover": "#4A44C4",
    "success": "#1FA96E",
    "danger": "#D03A5C",
    "warning": "#F5A623",
    "info": "#1A82D4"
  },
  "backgrounds": { "bg": "#E4E9F0", "bg-light": "#EDF1F7", "bg-dark": "#D1D9E6" },
  "backgrounds-dark": { "bg": "#2A2D35", "bg-light": "#31343C", "bg-dark": "#23262D" },
  "text": { "text": "#2D3748", "text-muted": "#5A6A7E", "text-light": "#6A7D94" },
  "shadows": { "shadow-light": "#FFFFFF", "shadow-dark": "#B8C0CC" },
  "radius": { "xs": "6px", "sm": "10px", "default": "16px", "lg": "24px", "full": "9999px" },
  "typography": { "font": "...", "font-mono": "..." },
  "transitions": { "fast": "0.15s", "base": "0.25s", "slow": "0.35s" }
}

The token file mirrors the CSS custom properties — if you override a variable, update the token too for consistency.

Explore Components View Changelog

Delete this item?

This action cannot be undone. This will permanently delete the item and remove all associated data.