---
source_hash: "f099e219"
title: "Themes"
weight: 200
---

# Themes

Documentation [Tailwind CSS v4](https://tailwindcss.com/docs)

SmartMaker uses **TailwindCSS v4** for styling. This version introduces a new CSS-first syntax with `@theme` and `@layer`.

## TailwindCSS 4 Configuration

### Installation

TailwindCSS 4 is integrated via the Vite plugin:

```
// vite.config.js

import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import tailwindcss from "@tailwindcss/vite";

export default defineConfig({
  plugins: [
    react(),
    tailwindcss(),
  ],
});
```

### CSS Entry Point

```
/* src/assets/styles/style.css */

@import "tailwindcss";

@layer theme, base, components;

@import "./theme.css" layer(theme);
@import "./base.css" layer(base);
```

## Define a Theme

### CSS Variables with @theme

```
/* src/assets/styles/theme.css */

@theme {
  /* Primary colors */
  --color-primary: #5fbabf;
  --color-primary-light: #8cd4d8;
  --color-primary-dark: #4a9599;

  --color-secondary: #fc8c8c;
  --color-secondary-light: #fdb5b5;
  --color-secondary-dark: #e67070;

  /* Semantic colors */
  --color-success: #22c55e;
  --color-warning: #f59e0b;
  --color-error: #ef4444;
  --color-info: #3b82f6;

  /* Background colors */
  --color-background: #ffffff;
  --color-surface: #f8fafc;
  --color-muted: #f1f5f9;

  /* Text colors */
  --color-foreground: #0f172a;
  --color-foreground-muted: #64748b;

  /* Custom spacing */
  --spacing-page: 1rem;
  --spacing-card: 1.5rem;

  /* Border radii */
  --radius-sm: 0.375rem;
  --radius-md: 0.5rem;
  --radius-lg: 1rem;
  --radius-full: 9999px;

  /* Shadows */
  --shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.05);
  --shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1);
  --shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1);

  /* Typography */
  --font-sans: 'Inter', system-ui, sans-serif;
  --font-mono: 'Fira Code', monospace;
}
```

### Usage in Components

The `@theme` variables automatically generate Tailwind classes:

```
<div className="bg-primary text-white">
  Primary background
</div>

<div className="text-foreground-muted">
  Muted text
</div>

<div className="rounded-lg shadow-md p-card">
  Card with spacing
</div>
```

## Base Styles

### Reset and Global Rules

```
/* src/assets/styles/base.css */

@layer base {
  * {
    @apply box-border p-0 m-0;
    -webkit-tap-highlight-color: transparent;
  }

  *::-webkit-scrollbar {
    display: none;
  }

  html {
    @apply scroll-smooth antialiased;
  }

  body {
    @apply bg-background text-foreground font-sans;
  }

  /* Focus visible for accessibility */
  :focus-visible {
    @apply outline-2 outline-offset-2 outline-primary;
  }

  /* Links */
  a {
    @apply text-primary hover:text-primary-dark transition-colors;
  }

  /* Inputs */
  input, textarea, select {
    @apply bg-surface border border-gray-200 rounded-md;
    @apply focus:border-primary focus:ring-1 focus:ring-primary;
  }
}
```

## Create Multiple Themes

### File Structure

```
src/assets/
├── styles/
│   ├── style.css           # Entry point
│   ├── theme.css           # Default variables
│   └── base.css            # Global rules
└── themes/
    ├── light.css           # Light theme
    └── dark.css            # Dark theme
```

### Light Theme (default)

```
/* src/assets/themes/light.css */

@theme {
  --color-background: #ffffff;
  --color-surface: #f8fafc;
  --color-foreground: #0f172a;
  --color-foreground-muted: #64748b;
}
```

### Dark Theme

```
/* src/assets/themes/dark.css */

@theme {
  --color-background: #0f172a;
  --color-surface: #1e293b;
  --color-foreground: #f8fafc;
  --color-foreground-muted: #94a3b8;
}
```

### Dynamic Loading

```
// src/main.jsx

import { StrictMode } from "react";
import { createRoot } from "react-dom/client";

import "./assets/styles/style.css";

// Load theme from settings
const theme = localStorage.getItem('theme') || 'light';
import(`./assets/themes/${theme}.css`);

import { App } from "./App";

createRoot(document.getElementById("root")).render(
  <StrictMode>
    <App />
  </StrictMode>
);
```

### useTheme Hook

```
import { useGlobalStates } from '@cap-rel/smartcommon';
import { useEffect } from 'react';

export const useTheme = () => {
  const [theme, setTheme] = useGlobalStates('settings.theme');

  useEffect(() => {
    // Apply theme to document
    document.documentElement.setAttribute('data-theme', theme);

    // Load theme CSS
    const link = document.createElement('link');
    link.rel = 'stylesheet';
    link.href = `/themes/${theme}.css`;
    document.head.appendChild(link);

    return () => {
      document.head.removeChild(link);
    };
  }, [theme]);

  const toggleTheme = () => {
    setTheme(theme === 'light' ? 'dark' : 'light');
  };

  return { theme, setTheme, toggleTheme };
};
```

### ThemeToggle Component

```
import { useTheme } from '../hooks/useTheme';

const ThemeToggle = () => {
  const { theme, toggleTheme } = useTheme();

  return (
    <button
      onClick={toggleTheme}
      className="p-2 rounded-full bg-surface"
    >
      {theme === 'light' ? '🌙' : '☀️'}
    </button>
  );
};
```

## Component Variants

### With SmartCommon

SmartCommon allows defining variants for components:

```
// appConfig.js

export const config = {
  components: {
    variants: {
      Button: {
        default: "bg-primary text-white rounded-md px-4 py-2",
        secondary: "bg-secondary text-white rounded-md px-4 py-2",
        outline: "border-2 border-primary text-primary rounded-md px-4 py-2",
        ghost: "text-primary hover:bg-primary/10 rounded-md px-4 py-2",
      },
      Input: {
        default: "bg-surface border border-gray-200 rounded-md p-3",
        filled: "bg-muted border-0 rounded-md p-3",
        underline: "border-b-2 border-gray-200 rounded-none p-3",
      },
    },
  },
};
```

### Usage

```
import { Button, Input } from '@cap-rel/smartcommon';

const MyForm = () => {
  return (
    <form>
      <Input name="email" variant="filled" />
      <Button variant="secondary">Submit</Button>
      <Button variant="ghost">Cancel</Button>
    </form>
  );
};
```

## Custom Utility Classes

### With @layer components

```
/* src/assets/styles/components.css */

@layer components {
  .card {
    @apply bg-surface rounded-lg shadow-md p-card;
  }

  .btn {
    @apply inline-flex items-center justify-center;
    @apply px-4 py-2 rounded-md font-medium;
    @apply transition-all duration-200;
    @apply focus:outline-none focus:ring-2 focus:ring-offset-2;
  }

  .btn-primary {
    @apply btn bg-primary text-white;
    @apply hover:bg-primary-dark;
    @apply focus:ring-primary;
  }

  .btn-secondary {
    @apply btn bg-secondary text-white;
    @apply hover:bg-secondary-dark;
    @apply focus:ring-secondary;
  }

  .input {
    @apply w-full px-4 py-3 rounded-md;
    @apply bg-surface border border-gray-200;
    @apply focus:border-primary focus:ring-1 focus:ring-primary;
    @apply placeholder:text-foreground-muted;
  }
}
```

## Automatic Dark Mode

### With prefers-color-scheme

```
/* src/assets/styles/theme.css */

@theme {
  --color-background: #ffffff;
  --color-foreground: #0f172a;
}

@media (prefers-color-scheme: dark) {
  @theme {
    --color-background: #0f172a;
    --color-foreground: #f8fafc;
  }
}
```

### With CSS Class

```
/* Default theme (light) */
@theme {
  --color-background: #ffffff;
  --color-foreground: #0f172a;
}

/* Dark theme via class */
.dark {
  --color-background: #0f172a;
  --color-foreground: #f8fafc;
}
```

```
// Toggle dark mode
document.documentElement.classList.toggle('dark');
```

## Tips

### Performance
- Use `@theme` for reused variables
- Avoid dynamic Tailwind classes (`bg-${color}`)
- Prefer component variants

### Organization
- One file for variables (`theme.css`)
- One file for base rules (`base.css`)
- One file per additional theme

### Accessibility
- Respect WCAG contrast ratios
- Test with `prefers-reduced-motion`
- Use `focus-visible` for focus

## See Also
- [Configuration](/front/configuration) - Component variants
- [SmartCommon](/front/smartcommon) - Available components
- [Animations](/front/animations) - Page transitions
