Documentation Tailwind CSS v4
SmartMaker utilise TailwindCSS v4 pour la stylisation. Cette version introduit une nouvelle syntaxe CSS-first avec @theme et @layer.
TailwindCSS 4 est intégré via le plugin Vite :
// vite.config.js
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import tailwindcss from "@tailwindcss/vite";
export default defineConfig({
plugins: [
react(),
tailwindcss(),
],
});
/* src/assets/styles/style.css */ @import "tailwindcss"; @layer theme, base, components; @import "./theme.css" layer(theme); @import "./base.css" layer(base);
/* src/assets/styles/theme.css */
@theme {
/* Couleurs principales */
--color-primary: #5fbabf;
--color-primary-light: #8cd4d8;
--color-primary-dark: #4a9599;
--color-secondary: #fc8c8c;
--color-secondary-light: #fdb5b5;
--color-secondary-dark: #e67070;
/* Couleurs sémantiques */
--color-success: #22c55e;
--color-warning: #f59e0b;
--color-error: #ef4444;
--color-info: #3b82f6;
/* Couleurs de fond */
--color-background: #ffffff;
--color-surface: #f8fafc;
--color-muted: #f1f5f9;
/* Couleurs de texte */
--color-foreground: #0f172a;
--color-foreground-muted: #64748b;
/* Espacements customs */
--spacing-page: 1rem;
--spacing-card: 1.5rem;
/* Rayons de bordure */
--radius-sm: 0.375rem;
--radius-md: 0.5rem;
--radius-lg: 1rem;
--radius-full: 9999px;
/* Ombres */
--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);
/* Typographie */
--font-sans: 'Inter', system-ui, sans-serif;
--font-mono: 'Fira Code', monospace;
}
Les variables @theme génèrent automatiquement des classes Tailwind :
<div className="bg-primary text-white"> Fond primaire </div> <div className="text-foreground-muted"> Texte atténué </div> <div className="rounded-lg shadow-md p-card"> Carte avec espacement </div>
/* 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 pour accessibilité */
:focus-visible {
@apply outline-2 outline-offset-2 outline-primary;
}
/* Liens */
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;
}
}
src/assets/
├── styles/
│ ├── style.css # Point d'entrée
│ ├── theme.css # Variables par défaut
│ └── base.css # Règles globales
└── themes/
├── light.css # Thème clair
└── dark.css # Thème sombre
/* src/assets/themes/light.css */
@theme {
--color-background: #ffffff;
--color-surface: #f8fafc;
--color-foreground: #0f172a;
--color-foreground-muted: #64748b;
}
/* src/assets/themes/dark.css */
@theme {
--color-background: #0f172a;
--color-surface: #1e293b;
--color-foreground: #f8fafc;
--color-foreground-muted: #94a3b8;
}
// src/main.jsx
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import "./assets/styles/style.css";
// Charger le thème depuis les settings
const theme = localStorage.getItem('theme') || 'light';
import(`./assets/themes/${theme}.css`);
import { App } from "./App";
createRoot(document.getElementById("root")).render(
<StrictMode>
<App />
</StrictMode>
);
import { useGlobalStates } from '@cap-rel/smartcommon';
import { useEffect } from 'react';
export const useTheme = () => {
const [theme, setTheme] = useGlobalStates('settings.theme');
useEffect(() => {
// Appliquer le thème au document
document.documentElement.setAttribute('data-theme', theme);
// Charger le CSS du thème
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 };
};
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>
);
};
SmartCommon permet de définir des variantes pour les composants :
// 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",
},
},
},
};
import { Button, Input } from '@cap-rel/smartcommon';
const MyForm = () => {
return (
<form>
<Input name="email" variant="filled" />
<Button variant="secondary">Envoyer</Button>
<Button variant="ghost">Annuler</Button>
</form>
);
};
/* 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;
}
}
/* src/assets/styles/theme.css */
@theme {
--color-background: #ffffff;
--color-foreground: #0f172a;
}
@media (prefers-color-scheme: dark) {
@theme {
--color-background: #0f172a;
--color-foreground: #f8fafc;
}
}
/* Thème par défaut (clair) */
@theme {
--color-background: #ffffff;
--color-foreground: #0f172a;
}
/* Thème sombre via classe */
.dark {
--color-background: #0f172a;
--color-foreground: #f8fafc;
}
// Toggle dark mode
document.documentElement.classList.toggle('dark');
@theme pour les variables réutiliséesbg-${color})theme.css)base.css)prefers-reduced-motionfocus-visible pour le focus