Skip to main content
Version: v1 (Current)

Theme Customization

Learn how to create custom themes for the UI Kit.

Basic Customization

Override Variables

The simplest way to customize is to override CSS variables after importing the styles:

// main.ts
import '@gxp-dev/uikit/styles'
import './my-overrides.css'
/* my-overrides.css */
:root {
--primary: #0066cc;
--primary-foreground: #ffffff;
--radius: 0.25rem;
}

Start Fresh

For complete control, import only base styles:

// main.ts
import '@gxp-dev/uikit/styles/base'
import './my-theme.css'

Creating a Custom Theme

Minimal Theme

A minimal theme needs these core variables:

/* minimal-theme.css */
:root {
/* Page */
--background: #ffffff;
--foreground: #0f172a;

/* Primary */
--primary: #2563eb;
--primary-foreground: #ffffff;

/* Secondary */
--secondary: #f1f5f9;
--secondary-foreground: #0f172a;

/* Borders & Inputs */
--border: #e2e8f0;
--input: #ffffff;
--ring: #2563eb;

/* Radius */
--radius: 0.5rem;
}

Complete Theme

A complete theme with all variables:

/* complete-theme.css */
:root {
/* Page backgrounds */
--background: #ffffff;
--foreground: #0f172a;

/* Card/Modal surfaces */
--card: #ffffff;
--card-foreground: #0f172a;
--popover: #ffffff;
--popover-foreground: #0f172a;

/* Primary actions */
--primary: #2563eb;
--primary-foreground: #ffffff;

/* Secondary actions */
--secondary: #f1f5f9;
--secondary-foreground: #0f172a;

/* Muted/subtle elements */
--muted: #f1f5f9;
--muted-foreground: #64748b;

/* Accents/highlights */
--accent: #f1f5f9;
--accent-foreground: #0f172a;

/* Destructive/danger */
--destructive: #dc2626;
--destructive-foreground: #ffffff;

/* Form elements */
--border: #e2e8f0;
--input: #ffffff;
--ring: #2563eb;

/* Border radius */
--radius: 0.5rem;
}

Theme Examples

Light Corporate Theme

:root {
--background: #f8fafc;
--foreground: #1e293b;

--card: #ffffff;
--card-foreground: #1e293b;
--popover: #ffffff;
--popover-foreground: #1e293b;

--primary: #0f766e;
--primary-foreground: #ffffff;

--secondary: #e2e8f0;
--secondary-foreground: #1e293b;

--muted: #f1f5f9;
--muted-foreground: #64748b;

--accent: #ccfbf1;
--accent-foreground: #0f766e;

--destructive: #b91c1c;
--destructive-foreground: #ffffff;

--border: #cbd5e1;
--input: #ffffff;
--ring: #0f766e;

--radius: 0.375rem;
}

Dark Theme

:root {
--background: #0f172a;
--foreground: #f8fafc;

--card: #1e293b;
--card-foreground: #f8fafc;
--popover: #1e293b;
--popover-foreground: #f8fafc;

--primary: #3b82f6;
--primary-foreground: #ffffff;

--secondary: #334155;
--secondary-foreground: #f8fafc;

--muted: #334155;
--muted-foreground: #94a3b8;

--accent: #334155;
--accent-foreground: #f8fafc;

--destructive: #ef4444;
--destructive-foreground: #ffffff;

--border: #334155;
--input: #1e293b;
--ring: #3b82f6;

--radius: 0.5rem;
}

Brand Color Theme

/* Example: Purple brand */
:root {
--background: #faf5ff;
--foreground: #1e1b4b;

--card: #ffffff;
--card-foreground: #1e1b4b;
--popover: #ffffff;
--popover-foreground: #1e1b4b;

--primary: #7c3aed;
--primary-foreground: #ffffff;

--secondary: #ede9fe;
--secondary-foreground: #5b21b6;

--muted: #f5f3ff;
--muted-foreground: #6b7280;

--accent: #ddd6fe;
--accent-foreground: #5b21b6;

--destructive: #dc2626;
--destructive-foreground: #ffffff;

--border: #c4b5fd;
--input: #ffffff;
--ring: #7c3aed;

--radius: 0.75rem;
}

Dynamic Theming

JavaScript Theme Switching

function setTheme(theme: 'light' | 'dark') {
const root = document.documentElement

if (theme === 'dark') {
root.style.setProperty('--background', '#0f172a')
root.style.setProperty('--foreground', '#f8fafc')
// ... set other dark variables
} else {
root.style.setProperty('--background', '#ffffff')
root.style.setProperty('--foreground', '#0f172a')
// ... set other light variables
}
}

Theme from API

interface ThemeConfig {
backgroundColor: string
textColor: string
primaryColor: string
primaryTextColor: string
// ...
}

function applyTheme(config: ThemeConfig) {
const root = document.documentElement

root.style.setProperty('--background', config.backgroundColor)
root.style.setProperty('--foreground', config.textColor)
root.style.setProperty('--primary', config.primaryColor)
root.style.setProperty('--primary-foreground', config.primaryTextColor)
// ... apply other variables
}

// Fetch and apply theme
const theme = await fetchTheme()
applyTheme(theme)

Platform Integration

When used within the GXP platform, themes are applied automatically:

// Platform handles this
watch(platformTheme, (theme) => {
document.documentElement.style.setProperty('--page_background_color', theme.backgroundColor)
document.documentElement.style.setProperty('--primary_button_background_color', theme.primaryColor)
// The shadcn variables reference platform variables, so they update automatically
})

Component-Level Overrides

Scoped Styling

Override variables for specific components:

<template>
<div class="special-section">
<Button>Special Button</Button>
</div>
</template>

<style scoped>
.special-section {
--primary: #dc2626;
--primary-foreground: #ffffff;
}
</style>

Utility Classes

Use Tailwind's arbitrary value syntax:

<template>
<Button class="bg-[#dc2626] hover:bg-[#b91c1c] text-white">
Custom Red Button
</Button>
</template>

Best Practices

1. Use Semantic Colors

Don't use colors directly; use semantic variables:

/* Good */
.my-component {
background: var(--background);
color: var(--foreground);
}

/* Avoid */
.my-component {
background: #ffffff;
color: #000000;
}

2. Maintain Contrast

Ensure sufficient contrast between foreground and background:

/* Primary and primary-foreground must have good contrast */
--primary: #2563eb; /* Blue */
--primary-foreground: #ffffff; /* White - high contrast */

3. Test Both Themes

If supporting light/dark modes, test components in both:

/* Light mode variables */
:root {
--card: #ffffff;
--card-foreground: #0f172a;
}

/* Dark mode variables */
.dark {
--card: #1e293b;
--card-foreground: #f8fafc;
}

4. Document Custom Themes

When creating a custom theme, document the color palette:

/*
* Brand Theme - Acme Corp
* Primary: Acme Blue (#0066cc)
* Secondary: Light Gray (#f5f5f5)
* Accent: Teal (#0d9488)
* Destructive: Red (#dc2626)
*/