Appearance
Theme System
Vroum uses a Tailwind CSS v4 theme system built on CSS custom properties (OKLCH color space) with full dark mode support.
File Structure
resources/css/
├── app.css # Main entry — imports everything
├── themes/
│ ├── theme.css # Base tokens (semantic aliases + custom palettes)
│ └── vroum.css # Vroum brand overrides (imports theme.css)theme.cssdeclares all semantic color tokens and their default (blue-based) values.vroum.cssre-maps those tokens to the Vroum brand palette (cyan/teal, hue 180).app.cssimportsvroum.css(which chains totheme.css) so the full system is loaded.
How It Works
@theme inlineintheme.cssregisters Tailwind utility names (e.g.bg-primary,text-foreground).- Each utility references a CSS custom property (e.g.
--color-primary → var(--primary)). :rootsets light-mode values;.darkoverrides them for dark mode.vroum.cssuses[data-theme="theme-vroum"]to swap the brand palette in.
Using Theme Classes
Use the semantic token names directly as Tailwind utilities. Never hard-code color values — always use the theme tokens.
Backgrounds
| Tailwind Class | Light Mode | Purpose |
|---|---|---|
bg-background | #ffffff | Page background |
bg-background-1 | ~#f9fafb | Slightly tinted background |
bg-background-2 | ~#f0f1f2 | Deeper tinted background |
bg-plain | #ffffff | Always-white surface |
bg-layer | #ffffff | Container / card-like surface |
bg-surface | ~#f0f1f2 | Recessed surface |
bg-muted | ~#f9fafb | Muted/disabled background |
Text
| Tailwind Class | Light Mode | Purpose |
|---|---|---|
text-foreground | ~#1f2937 | Default body text |
text-foreground-inverse | #ffffff | Text on dark surfaces |
text-muted-foreground | ~#9ca3af | Placeholder / hint text |
text-muted-foreground-1 | ~#6b7280 | Secondary muted text |
text-muted-foreground-2 | ~#4b5563 | Stronger muted text |
Primary (Brand)
The primary palette is the Vroum cyan/teal (hue 180). Use the shade scale or the state tokens.
| Tailwind Class | Vroum Hex (approx) | OKLCH | Purpose |
|---|---|---|---|
bg-primary-50 | #c0f5eb | oklch(92.8% 0.08 180) | Lightest tint |
bg-primary-100 | #8deee0 | oklch(88.6% 0.1 180) | Light tint |
bg-primary-200 | #4fdecb | oklch(80.4% 0.12 180) | Light accent |
bg-primary-300 | #3dc6b1 | oklch(72.4% 0.14 180) | Medium-light accent |
bg-primary-400 | #1fae99 | oklch(64.6% 0.16 180) | Medium accent |
bg-primary-500 | #008a75 | oklch(57% 0.14 180) | Base brand color |
bg-primary-600 | #007462 | oklch(49.6% 0.12 180) | Default primary |
bg-primary-700 | #006050 | oklch(42.4% 0.1 180) | Darker primary |
bg-primary-800 | #004d3f | oklch(35.4% 0.08 180) | Dark accent |
bg-primary-900 | #003b30 | oklch(28.6% 0.06 180) | Very dark |
bg-primary-950 | #003028 | oklch(25.3% 0.05 180) | Darkest |
State tokens (use these for interactive elements):
| Tailwind Class | Resolves To (Light) | Purpose |
|---|---|---|
bg-primary | primary-600 | Default button / accent |
text-primary-foreground | #ffffff | Text on primary background |
bg-primary-hover | primary-700 | Hover state |
bg-primary-focus | primary-700 | Focus state |
bg-primary-active | primary-700 | Active / pressed state |
bg-primary-checked | primary-600 | Checked checkbox / toggle |
border-primary-line | transparent | Primary border (if needed) |
Secondary
| Tailwind Class | Light Mode | Purpose |
|---|---|---|
bg-secondary | ~#1a202e | Dark button / accent |
text-secondary-foreground | #ffffff | Text on secondary bg |
bg-secondary-hover | ~#2d3748 | Hover state |
Destructive
| Tailwind Class | Light Mode | Purpose |
|---|---|---|
bg-destructive | ~#ef4444 | Danger / delete |
text-destructive-foreground | #ffffff | Text on destructive |
bg-destructive-hover | ~#dc2626 | Hover state |
Borders
| Tailwind Class | Light Mode | Purpose |
|---|---|---|
border-border | ~#e5e7eb | Default border |
border-line-1 | ~#f3f4f6 | Subtle separator |
border-line-2 | ~#e5e7eb | Standard separator |
border-line-3 | ~#d1d5db | Prominent border |
border-line-4 | ~#9ca3af | Strong border |
…through line-8 | ~#1f2937 | Strongest border |
Component Tokens
Each UI component has its own set of tokens. They all follow the same pattern:
bg-{component} → background
border-{component}-line → border color
text-{component}-foreground (if applicable)
bg-{component}-hover → hover state
bg-{component}-divider → internal divider
bg-{component}-inverse → inverted accentAvailable component groups:
| Component | Class Prefix | Example |
|---|---|---|
| Navbar | navbar- | bg-navbar, bg-navbar-nav-hover |
| Navbar 1 | navbar-1- | bg-navbar-1, border-navbar-1-line |
| Navbar 2 | navbar-2- | bg-navbar-2, bg-navbar-2-nav-active |
| Sidebar | sidebar- | bg-sidebar, bg-sidebar-nav-active |
| Sidebar 1 | sidebar-1- | bg-sidebar-1, border-sidebar-1-line |
| Sidebar 2 | sidebar-2- | bg-sidebar-2 |
| Card | card- | bg-card, border-card-line |
| Dropdown | dropdown- | bg-dropdown, bg-dropdown-item-hover |
| Select | select- | bg-select, bg-select-item-hover |
| Overlay | overlay- | bg-overlay, border-overlay-line |
| Popover | popover- | bg-popover, border-popover-line |
| Tooltip | tooltip- | bg-tooltip, text-tooltip-foreground |
| Footer | footer- | bg-footer, border-footer-line |
| Table | table- | border-table-line |
| Scrollbar | scrollbar- | bg-scrollbar-track, bg-scrollbar-thumb |
Surface Depth Scale
For layered UIs, the surface scale provides increasing depth:
| Tailwind Class | Light Mode | Dark Mode | Purpose |
|---|---|---|---|
bg-surface | gray-100 | zinc-700 | Level 0 (base) |
bg-surface-1 | gray-200 | zinc-600 | Level 1 |
bg-surface-2 | gray-300 | zinc-500 | Level 2 |
bg-surface-3 | gray-400 | zinc-600 | Level 3 |
bg-surface-4 | gray-500 | zinc-500 | Level 4 |
bg-surface-5 | gray-600 | zinc-400 | Level 5 (deepest) |
Charts
Chart tokens provide hex values for libraries like ApexCharts that don't support OKLCH:
| Token (CSS var) | Hex Value |
|---|---|
--chart-colors-primary-hex | #008a75 |
--chart-colors-chart-1-hex | #008a75 |
--chart-colors-chart-2-hex | #0092a8 |
--chart-colors-chart-3-hex | #3d8e53 |
--chart-colors-chart-4-hex | #ac4c6e |
--chart-colors-chart-5-hex | #a5679f |
--chart-colors-chart-6-hex | #b25f52 |
--chart-colors-chart-7-hex | #478ebc |
--chart-colors-chart-8-hex | #6d7d34 |
--chart-colors-chart-9-hex | #6b7db6 |
--chart-colors-chart-10-hex | #8f7835 |
Access them in JS via getComputedStyle(document.documentElement).getPropertyValue('--chart-colors-primary-hex').
Custom Palettes
Beyond the semantic tokens, these raw color palettes are available as Tailwind utilities:
Vroum Brand (vroum-*)
Vibrant cyan/teal at hue 180. Available shades: 50–950.
html
<div class="bg-vroum-500 text-white">Brand element</div>Vroum Gray (vroum-gray-*)
Teal-tinted neutral gray. Available shades: 50–950.
html
<div class="bg-vroum-gray-100 text-vroum-gray-800">Tinted surface</div>Other Palettes
| Palette | Hue | Usage |
|---|---|---|
khaki-* | 67–91 | Earthy/warm accents |
mauve-* | 334–345 | Soft pink accents |
avocado-* | 120 | Green accents (vibrant) |
avocado-soft-* | 120 | Green accents (muted) |
Dark Mode
Dark mode activates when the .dark class is present on <html>. Toggle it via resources/js/Plugins/switchDark.ts:
ts
import { toggle, isDark, enable, disable } from '@/Plugins/switchDark';
toggle(); // flip current state
isDark(); // returns true/false
enable(); // force dark
disable(); // force lightThe theme automatically adjusts all semantic tokens — no extra classes needed. The Vroum dark mode uses Tailwind's zinc palette for neutral surfaces.
If you need mode-specific overrides in templates, use the dark: variant:
html
<div class="bg-layer dark:bg-zinc-900">
<!-- Usually not needed — the token handles it -->
</div>Examples
Primary Button
html
<button class="bg-primary text-primary-foreground hover:bg-primary-hover focus:bg-primary-focus rounded-lg px-4 py-2">
Save
</button>Card
html
<div class="bg-card border border-card-line rounded-xl p-6">
<h3 class="text-foreground font-semibold">Title</h3>
<p class="text-muted-foreground">Description text</p>
<hr class="border-card-divider my-4" />
<footer class="text-muted-foreground-1 text-sm">Footer</footer>
</div>Sidebar Navigation Item
html
<a class="text-sidebar-nav-foreground hover:bg-sidebar-nav-hover focus:bg-sidebar-nav-focus px-3 py-2 rounded-md">
Dashboard
</a>Destructive Action
html
<button class="bg-destructive text-destructive-foreground hover:bg-destructive-hover rounded-lg px-4 py-2">
Delete
</button>Muted Badge
html
<span class="bg-muted text-muted-foreground-1 rounded-full px-2 py-0.5 text-xs">
Draft
</span>Rules
- Always use semantic tokens (
bg-primary,text-foreground,border-line-2) instead of raw Tailwind colors (bg-blue-600,text-gray-800). - Don't hard-code hex values in templates — the theme system handles light/dark mode automatically.
- Use component tokens for their intended components (
bg-cardfor cards,bg-dropdownfor dropdowns) to ensure consistency. - Use the
-hexchart tokens when passing colors to JS charting libraries that require hex strings. - Run
bg-primary-*for the brand color scale, notbg-vroum-*, unless you need the raw palette outside of theming.