Theme System
The frontend uses a semantic design system built on Tailwind CSS v4 and CSS custom properties. Components receive styling through semantic IDs rather than explicit color props, and all colors adapt to the user's selected theme.
Color Architecture
Each theme defines 6 semantic colors:
| Token | CSS Variable | Purpose |
|---|---|---|
| Primary | --primary | Main actions, focus states, active selections |
| Secondary | --secondary | Hover states, accents |
| Tertiary | --tertiary | Muted elements, icons |
| Light | --light | Borders, dividers |
| Background | --background | Backgrounds, disabled states |
| Text Dark | --text-dark | Text content |
21 pre-built themes are available in lib/config/themes.ts, including Metallic Chic (default), Ocean Blue, Forest Green, Royal Purple, Ruby Red, Sunset Orange, Slate Gray, and more.
CSS Variable Usage
Colors are referenced via CSS variables in both inline styles and Tailwind arbitrary values:
/* Direct CSS */
border-color: var(--light);
color: var(--text-dark);
/* Tailwind arbitrary values */
className="border-[var(--primary)] text-[var(--text-dark)]"
The STYLE_VARIABLES object in components/ux/TWSharedStyles.tsx provides pre-built Tailwind class combinations:
colors.borderDefault // "border-[var(--light)]"
colors.borderFocus // "focus-within:border-[var(--primary)]"
colors.textDefault // "text-[var(--text-dark)]"
colors.textSelected // "text-[var(--primary)]"
Dark / Light Mode
Dark mode uses a data-attribute selector configured in Tailwind v4:
@variant dark (&:where([data-theme="dark"]))
NextThemesProvider manages the data-theme attribute. User theme preference (themeId) is stored in the user profile in the database. On first visit, the system defaults to light.
Semantic ID Mapping
Components resolve their styling through semantic IDs defined in lib/theme/designTokens.ts:
Buttons
| Style Key | Appearance |
|---|---|
positive-primary | Filled with theme primary color |
positive-stroke | Outlined with theme border |
positive-text | Ghost / text-only |
negative-primary | Destructive (always red) |
Mapped IDs: save --> positive-primary, cancel --> positive-stroke, delete --> negative-primary (30+ IDs total).
Inputs
| Style Key | Appearance |
|---|---|
standard | Default borders with theme focus ring |
search | Emphasized focus for search fields |
Links
| Style Key | Appearance |
|---|---|
primary | Action links (theme primary) |
navigation | Menu links (text-dark, primary on hover) |
inline | Always underlined content links |
subtle | Low-emphasis footer links |
TW* Component Integration
Custom TW* components (TWButton, TWModal, TWInput, TWSelect, etc.) use the aiqlick plugin prefix. Theme colors flow into TW* components via CSS custom properties so that buttons, selects, and inputs receive consistent styling without explicit color props.
Adding a New Theme
Add an entry to lib/config/themes.ts:
"my-theme": {
id: "my-theme",
name: "My Theme",
colors: {
primary: "#FF6B6B",
secondary: "#4ECDC4",
tertiary: "#95E1D3",
light: "#F7F7F7",
background: "#FAFAFA",
textDark: "#2C3E50",
},
}
All components update automatically -- no per-component changes needed.
The interactive theme preview is available at /devtools/colors in development mode. It shows all 21 themes with live button, input, and link examples.