Skip to content

Theme Components ​

God Panel's theme components provide comprehensive tools for customizing the visual appearance of your application. From color schemes to typography, these components make it easy to create a cohesive and branded experience.

Component Categories ​

🎨 Color & Appearance ​

  • ThemeProvider: Global theme context
  • ColorPalette: Color selection interface
  • ThemeSwitcher: Dark/light mode toggle
  • CustomProperties: CSS variable management

📝 Typography ​

  • FontSelector: Font family selection
  • TextSize: Typography scale controls
  • LineHeight: Line height adjustments
  • FontWeight: Weight customization

🎯 Layout & Spacing ​

  • SpacingControls: Margin and padding settings
  • BorderRadius: Corner radius controls
  • ShadowControls: Box shadow customization
  • LayoutPreview: Visual layout testing

ThemeProvider Component ​

Global theme context provider that manages theme state and CSS custom properties.

vue
<template>
  <ThemeProvider
    :theme="currentTheme"
    :mode="themeMode"
    @theme-change="handleThemeChange"
    @mode-change="handleModeChange"
  >
    <div class="min-h-screen">
      <!-- Application content -->
      <ThemeSwitcher />

      <div class="container mx-auto p-6">
        <h1 class="text-3xl font-bold mb-6">Dashboard</h1>

        <!-- Theme-aware components -->
        <Card class="mb-6">
          <p>This card adapts to the current theme automatically.</p>
        </Card>
      </div>
    </div>
  </ThemeProvider>
</template>

<script setup>
const themeMode = ref('light')
const currentTheme = ref(defaultTheme)

const themes = {
  light: {
    colors: {
      primary: '#3b82f6',
      secondary: '#6b7280',
      success: '#10b981',
      warning: '#f59e0b',
      error: '#ef4444',
      background: '#ffffff',
      surface: '#f9fafb',
      text: '#111827',
      'text-muted': '#6b7280'
    },
    spacing: {
      xs: '0.25rem',
      sm: '0.5rem',
      md: '1rem',
      lg: '1.5rem',
      xl: '2rem'
    }
  },
  dark: {
    colors: {
      primary: '#60a5fa',
      secondary: '#9ca3af',
      success: '#34d399',
      warning: '#fbbf24',
      error: '#f87171',
      background: '#111827',
      surface: '#1f2937',
      text: '#f9fafb',
      'text-muted': '#9ca3af'
    },
    spacing: {
      xs: '0.25rem',
      sm: '0.5rem',
      md: '1rem',
      lg: '1.5rem',
      xl: '2rem'
    }
  }
}

const defaultTheme = themes.light

const handleThemeChange = (newTheme) => {
  currentTheme.value = newTheme
  // Persist theme preference
  localStorage.setItem('theme', JSON.stringify(newTheme))
}

const handleModeChange = (newMode) => {
  themeMode.value = newMode
  currentTheme.value = themes[newMode]

  // Update document classes
  if (newMode === 'dark') {
    document.documentElement.classList.add('dark')
  } else {
    document.documentElement.classList.remove('dark')
  }

  // Persist mode preference
  localStorage.setItem('themeMode', newMode)
}
</script>

Props ​

PropTypeDefaultDescription
themeobject-Current theme configuration
modestring'light'Theme mode (light/dark)
auto-detectbooleantrueAuto-detect system preference

Events ​

EventPayloadDescription
theme-changetheme objectTheme configuration changed
mode-changemode stringTheme mode changed

ThemeSwitcher Component ​

Toggle between light and dark modes with smooth transitions.

vue
<template>
  <div class="flex items-center space-x-4">
    <!-- Simple toggle -->
    <ThemeSwitcher />

    <!-- With custom options -->
    <ThemeSwitcher
      :options="themeOptions"
      :show-labels="true"
      position="bottom"
    />

    <!-- Compact version -->
    <ThemeSwitcher
      variant="compact"
      icon-only
    />
  </div>
</template>

<script setup>
const themeOptions = [
  { label: 'Light', value: 'light', icon: 'sun' },
  { label: 'Dark', value: 'dark', icon: 'moon' },
  { label: 'System', value: 'system', icon: 'monitor' }
]
</script>

Props ​

PropTypeDefaultDescription
variantstring'default'Switcher style variant
optionsarray-Available theme options
show-labelsbooleanfalseShow option labels
icon-onlybooleanfalseHide labels, show only icons
positionstring'top'Dropdown position

ColorPalette Component ​

Interactive color picker for theme customization.

vue
<template>
  <div class="grid grid-cols-1 md:grid-cols-2 gap-6">
    <!-- Primary colors -->
    <ColorPalette
      v-model="theme.colors.primary"
      title="Primary Colors"
      :colors="primaryPalette"
      :allow-custom="true"
      @change="updatePrimaryColors"
    />

    <!-- Accent colors -->
    <ColorPalette
      v-model="theme.colors.accent"
      title="Accent Colors"
      :colors="accentPalette"
      @change="updateAccentColors"
    />

    <!-- Neutral colors -->
    <ColorPalette
      v-model="theme.colors.neutral"
      title="Neutral Colors"
      :colors="neutralPalette"
      mode="grid"
      @change="updateNeutralColors"
    />
  </div>
</template>

<script setup>
const theme = ref({
  colors: {
    primary: '#3b82f6',
    accent: '#10b981',
    neutral: '#6b7280'
  }
})

const primaryPalette = [
  '#3b82f6', '#1d4ed8', '#1e40af', '#1e3a8a',
  '#ef4444', '#dc2626', '#b91c1c', '#991b1b',
  '#10b981', '#059669', '#047857', '#065f46',
  '#f59e0b', '#d97706', '#b45309', '#92400e'
]

const accentPalette = [
  '#10b981', '#06b6d4', '#8b5cf6', '#ec4899',
  '#f59e0b', '#ef4444', '#84cc16', '#6366f1'
]

const neutralPalette = [
  '#ffffff', '#f9fafb', '#f3f4f6', '#e5e7eb',
  '#d1d5db', '#9ca3af', '#6b7280', '#374151',
  '#1f2937', '#111827', '#000000'
]

const updatePrimaryColors = (colors) => {
  // Update CSS custom properties
  document.documentElement.style.setProperty('--color-primary', colors.main)
  document.documentElement.style.setProperty('--color-primary-50', colors.lightest)
  document.documentElement.style.setProperty('--color-primary-900', colors.darkest)
}

const updateAccentColors = (colors) => {
  document.documentElement.style.setProperty('--color-accent', colors.main)
}

const updateNeutralColors = (colors) => {
  colors.forEach((color, index) => {
    document.documentElement.style.setProperty(`--color-neutral-${index * 100}`, color)
  })
}
</script>

Props ​

PropTypeDefaultDescription
modelValuestring | array-Selected colors (v-model)
titlestring-Palette title
colorsarray-Available colors
modestring'list'Display mode (list/grid)
allow-custombooleanfalseAllow custom color input

Events ​

EventPayloadDescription
changecolor objectColor selection changed

FontSelector Component ​

Typography customization with font family selection.

vue
<template>
  <div class="space-y-6">
    <!-- Heading fonts -->
    <FontSelector
      v-model="theme.fonts.heading"
      label="Heading Font"
      :fonts="headingFonts"
      :preview-text="headingPreview"
      category="headings"
    />

    <!-- Body fonts -->
    <FontSelector
      v-model="theme.fonts.body"
      label="Body Font"
      :fonts="bodyFonts"
      :preview-text="bodyPreview"
      category="body"
    />

    <!-- Monospace fonts -->
    <FontSelector
      v-model="theme.fonts.mono"
      label="Code Font"
      :fonts="monoFonts"
      :preview-text="codePreview"
      category="monospace"
    />
  </div>
</template>

<script setup>
const theme = ref({
  fonts: {
    heading: 'Inter',
    body: 'Inter',
    mono: 'JetBrains Mono'
  }
})

const headingFonts = [
  'Inter', 'Roboto', 'Open Sans', 'Lato', 'Montserrat',
  'Poppins', 'Nunito', 'Source Sans Pro', 'Work Sans', 'Space Grotesk'
]

const bodyFonts = [
  'Inter', 'Roboto', 'Open Sans', 'Lato', 'Source Sans Pro',
  'Work Sans', 'Nunito', 'Poppins', 'Space Grotesk', 'IBM Plex Sans'
]

const monoFonts = [
  'JetBrains Mono', 'Fira Code', 'Source Code Pro', 'Monaco',
  'Consolas', 'Courier New', 'SF Mono', 'IBM Plex Mono'
]

const headingPreview = 'The quick brown fox jumps over the lazy dog'
const bodyPreview = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.'
const codePreview = 'const theme = { fontFamily: "Inter" }'
</script>

Props ​

PropTypeDefaultDescription
modelValuestring-Selected font (v-model)
labelstring-Font selector label
fontsarray-Available fonts
preview-textstring-Preview text
categorystring-Font category

SpacingControls Component ​

Interactive controls for margin, padding, and spacing settings.

vue
<template>
  <div class="grid grid-cols-1 md:grid-cols-2 gap-6">
    <!-- Margin controls -->
    <SpacingControls
      v-model="theme.spacing.margin"
      title="Margins"
      :options="marginOptions"
      unit="rem"
    />

    <!-- Padding controls -->
    <SpacingControls
      v-model="theme.spacing.padding"
      title="Padding"
      :options="paddingOptions"
      unit="rem"
    />

    <!-- Border radius -->
    <SpacingControls
      v-model="theme.borderRadius"
      title="Border Radius"
      :options="radiusOptions"
      unit="px"
    />

    <!-- Shadow controls -->
    <SpacingControls
      v-model="theme.shadows"
      title="Box Shadows"
      :options="shadowOptions"
      unit=""
    />
  </div>
</template>

<script setup>
const theme = ref({
  spacing: {
    margin: { xs: 0.25, sm: 0.5, md: 1, lg: 1.5, xl: 2 },
    padding: { xs: 0.25, sm: 0.5, md: 1, lg: 1.5, xl: 2 }
  },
  borderRadius: { sm: 4, md: 8, lg: 12, xl: 16 },
  shadows: {
    sm: '0 1px 2px 0 rgba(0, 0, 0, 0.05)',
    md: '0 4px 6px -1px rgba(0, 0, 0, 0.1)',
    lg: '0 10px 15px -3px rgba(0, 0, 0, 0.1)',
    xl: '0 20px 25px -5px rgba(0, 0, 0, 0.1)'
  }
})

const marginOptions = [0, 0.25, 0.5, 1, 1.5, 2, 3, 4, 6, 8]
const paddingOptions = [0, 0.25, 0.5, 1, 1.5, 2, 3, 4, 6, 8]
const radiusOptions = [0, 2, 4, 6, 8, 12, 16, 24, 32]
const shadowOptions = ['none', 'sm', 'md', 'lg', 'xl', '2xl']
</script>

LayoutPreview Component ​

Real-time preview of theme changes with visual feedback.

vue
<template>
  <div class="space-y-6">
    <!-- Theme preview -->
    <LayoutPreview
      :theme="currentTheme"
      :components="previewComponents"
      class="border rounded-lg"
    />

    <!-- Component showcase -->
    <div class="grid grid-cols-2 md:grid-cols-4 gap-4">
      <Card v-for="component in previewComponents" :key="component.name">
        <template #header>
          <h4 class="font-medium">{{ component.name }}</h4>
        </template>
        <component :is="component.component" />
      </Card>
    </div>
  </div>
</template>

<script setup>
const currentTheme = ref(defaultTheme)

const previewComponents = [
  {
    name: 'Button',
    component: 'Button',
    props: { variant: 'primary' }
  },
  {
    name: 'Card',
    component: 'Card',
    props: {}
  },
  {
    name: 'Input',
    component: 'Input',
    props: { label: 'Sample Input' }
  },
  {
    name: 'Badge',
    component: 'Badge',
    props: { variant: 'success' }
  }
]
</script>

CSS Custom Properties ​

Theme Variables ​

css
/* Root theme variables */
:root {
  /* Colors */
  --color-primary: #3b82f6;
  --color-primary-50: #eff6ff;
  --color-primary-100: #dbeafe;
  --color-primary-500: #3b82f6;
  --color-primary-900: #1e3a8a;

  /* Typography */
  --font-family-heading: 'Inter', sans-serif;
  --font-family-body: 'Inter', sans-serif;
  --font-family-mono: 'JetBrains Mono', monospace;

  --font-size-xs: 0.75rem;
  --font-size-sm: 0.875rem;
  --font-size-base: 1rem;
  --font-size-lg: 1.125rem;
  --font-size-xl: 1.25rem;

  /* Spacing */
  --space-xs: 0.25rem;
  --space-sm: 0.5rem;
  --space-md: 1rem;
  --space-lg: 1.5rem;
  --space-xl: 2rem;

  /* Borders */
  --radius-sm: 0.25rem;
  --radius-md: 0.5rem;
  --radius-lg: 0.75rem;
  --radius-xl: 1rem;

  /* Shadows */
  --shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
  --shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
  --shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
  --shadow-xl: 0 20px 25px -5px rgba(0, 0, 0, 0.1);
}

/* Dark theme */
[data-theme="dark"] {
  --color-primary: #60a5fa;
  --color-primary-50: #1e3a8a;
  --color-primary-100: #1e40af;
  --color-primary-500: #60a5fa;
  --color-primary-900: #dbeafe;

  --color-background: #111827;
  --color-surface: #1f2937;
  --color-text: #f9fafb;
  --color-text-muted: #9ca3af;
}

/* High contrast theme */
[data-theme="high-contrast"] {
  --color-primary: #000000;
  --color-background: #ffffff;
  --color-text: #000000;
  --border-width: 2px;
}

Dynamic Theme Updates ​

vue
<script setup>
const updateThemeProperty = (property, value) => {
  document.documentElement.style.setProperty(property, value)

  // Update theme object
  const path = property.replace('--', '').split('-')
  let target = theme.value

  for (let i = 0; i < path.length - 1; i++) {
    target = target[path[i]] = target[path[i]] || {}
  }

  target[path[path.length - 1]] = value
}
</script>

Advanced Features ​

Theme Inheritance ​

vue
<script setup>
// Base theme
const baseTheme = {
  colors: {
    primary: '#3b82f6',
    secondary: '#6b7280'
  }
}

// Extended theme
const extendedTheme = {
  ...baseTheme,
  colors: {
    ...baseTheme.colors,
    accent: '#10b981',
    success: '#22c55e'
  }
}
</script>

Theme Validation ​

vue
<script setup>
const validateTheme = (theme) => {
  const errors = []

  // Check required colors
  const requiredColors = ['primary', 'background', 'text']
  requiredColors.forEach(color => {
    if (!theme.colors[color]) {
      errors.push(`Missing required color: ${color}`)
    }
  })

  // Check contrast ratios
  const contrastRatio = getContrastRatio(
    theme.colors.primary,
    theme.colors.background
  )

  if (contrastRatio < 4.5) {
    errors.push('Primary color contrast ratio too low')
  }

  return errors
}
</script>

Integration Examples ​

With CSS-in-JS ​

vue
<script setup>
import { useTheme } from '~/composables/useTheme'

const { theme, updateTheme } = useTheme()

// Apply theme to component styles
const componentStyles = computed(() => ({
  backgroundColor: theme.value.colors.background,
  color: theme.value.colors.text,
  borderColor: theme.value.colors.border
}))
</script>

With Tailwind CSS ​

vue
<script setup>
// Generate Tailwind theme configuration
const tailwindTheme = computed(() => ({
  colors: {
    primary: theme.value.colors.primary,
    secondary: theme.value.colors.secondary,
    success: theme.value.colors.success,
    warning: theme.value.colors.warning,
    error: theme.value.colors.error
  },
  fontFamily: {
    heading: theme.value.fonts.heading,
    body: theme.value.fonts.body,
    mono: theme.value.fonts.mono
  }
}))
</script>

Best Practices ​

Performance ​

  1. Preload theme fonts
  2. Cache theme calculations
  3. Debounce rapid changes
  4. Lazy load theme assets
  5. Optimize CSS custom properties

Accessibility ​

  1. Maintain contrast ratios above 4.5:1
  2. Test with screen readers
  3. Provide theme descriptions
  4. Consider motion preferences
  5. Validate color combinations

User Experience ​

  1. Provide smooth transitions
  2. Show loading states for theme changes
  3. Allow theme preview before applying
  4. Remember user preferences
  5. Offer reset functionality

Troubleshooting ​

Common Issues ​

Theme not applying:

  • Check CSS custom property names
  • Verify theme provider setup
  • Check for JavaScript errors

Colors not updating:

  • Ensure color format is valid
  • Check CSS specificity
  • Verify theme context

Fonts not loading:

  • Check font loading strategy
  • Verify font URLs
  • Check network connectivity

Contributing ​

Help improve theme components:

  1. Add new theme options
  2. Improve accessibility
  3. Optimize performance
  4. Add comprehensive tests
  5. Create theme templates

Resources ​


Next: Component Overview for complete component reference and Authentication Components for user management.

Released under the MIT License.