[FR] Bundle Symfony pour le theming visuel — 5 thèmes prédéfinis, bascule instantanée via CSS variables, personnalisation admin, préférence par utilisateur. [EN] Symfony bundle for visual theming — 5 built-in themes, instant switching via CSS variables, admin customization, per-user preferences.
- 5 thèmes prédéfinis — Light, Dark, Ocean, Forest, Sunset — chacun avec une palette complète
- Bascule instantanée — changement de thème sans rechargement de page via CSS custom properties
- Anti-flash — script inline synchrone qui restaure le thème depuis
localStorageavant le premier paint - Préférence par utilisateur — chaque utilisateur peut choisir son propre thème
- Détection système — détecte automatiquement
prefers-color-scheme: dark - Personnalisation admin — color pickers, CSS custom injectable, logo personnalisable
- Preview en temps réel — aperçu des modifications avant sauvegarde
- Thèmes custom — ajout de thèmes via YAML ou programmatiquement (builder)
- Stockage DBAL — pas besoin de Doctrine ORM, fonctionne avec DBAL seul
- Cache Symfony — thème actif caché, invalidé automatiquement
- Controller AJAX inclus — 4 routes prêtes à l'emploi
- JavaScript vanilla — zéro dépendance, API
OwlTheme.set(),OwlTheme.get() - CSS BEM — styles modulaires, responsive mobile
- Intégration owl-settings — si
owl-settings-bundleest installé, la préférence thème y est stockée - Commande d'installation —
php bin/console owl:theme:installcrée les tables automatiquement
| Thème | Description | Couleur primaire |
|---|---|---|
| Light | Clair et neutre, idéal pour le bureau | #3B82F6 |
| Dark | Sombre, réduit la fatigue oculaire | #60A5FA |
| Ocean | Tons bleus professionnels | #0EA5E9 |
| Forest | Tons verts naturels, apaisant | #16A34A |
| Sunset | Tons orange et ambrés chaleureux | #EA580C |
Chaque thème définit 24 variables CSS : couleurs principales, statut, arrière-plans, texte, bordures, ombres, layout (sidebar/header).
composer require owl-concept/theme-bundleSi le flex recipe n'est pas disponible, ajoutez le bundle manuellement :
// config/bundles.php
return [
// ...
OwlConcept\ThemeBundle\OwlThemeBundle::class => ['all' => true],
];Créez les tables en base de données :
php bin/console owl:theme:install # Affiche le SQL
php bin/console owl:theme:install --force # Exécute le SQLImportez les routes du bundle :
# config/routes/owl_theme.yaml
owl_theme:
resource: '@OwlThemeBundle/Resources/config/routes.yaml'# config/packages/owl_theme.yaml
owl_theme:
default_theme: light # Thème par défaut
allow_user_themes: true # Préférence par utilisateur
detect_system_preference: true # Détecte prefers-color-scheme
transition_duration: '0.3s' # Durée de transition CSS
cache_pool: cache.app # Pool de cache Symfony
cache_ttl: 3600 # TTL du cache en secondes
css_class_prefix: owl-theme # Préfixe CSS BEM
themes_table: owl_themes # Nom de la table des thèmes
active_table: owl_theme_active # Nom de la table du thème actif
# Thèmes custom (optionnel)
themes:
my_brand:
--owl-primary: "#E11D48"
--owl-bg: "#FFF1F2"
--owl-text: "#881337"{# base.html.twig #}
<head>
{{ owl_theme_head() }}
</head>Cela génère :
- Un bloc
<style>avec les variables CSS du thème actif - Un
<script>synchrone qui restaure le thème depuislocalStorage(anti-flash) - Les données JSON du thème pour le JavaScript
{# Mode boutons (par défaut) #}
{% include '@OwlTheme/switcher.html.twig' %}
{# Mode dropdown #}
{% include '@OwlTheme/switcher.html.twig' with { switcher_mode: 'dropdown' } %}{% include '@OwlTheme/admin.html.twig' %}.my-button {
background-color: var(--owl-primary);
color: var(--owl-text-inverse);
border-radius: var(--owl-radius);
}
.my-sidebar {
background: var(--owl-sidebar-bg);
color: var(--owl-sidebar-text);
}// Dans un service ou un listener
$themeBuilder->addTheme('corporate', 'Corporate', [
'--owl-primary' => '#1D4ED8',
'--owl-bg' => '#F8FAFC',
'--owl-sidebar-bg' => '#1E3A5F',
], 'Thème aux couleurs de l\'entreprise');// Changer de thème (instantané + persistance AJAX)
OwlTheme.set('dark');
// Obtenir le thème actuel
OwlTheme.get(); // → 'dark'
// Lister les thèmes disponibles
OwlTheme.list(); // → ['light', 'dark', 'ocean', 'forest', 'sunset']
// Écouter les changements de thème
OwlTheme.onChange(function(newTheme, oldTheme) {
console.log('Thème changé de ' + oldTheme + ' vers ' + newTheme);
});
// Détecter la préférence système
OwlTheme.detectSystemPreference(); // → 'dark' ou 'light'La page admin (@OwlTheme/admin.html.twig) permet de :
- Modifier les couleurs de chaque thème via des color pickers
- Injecter du CSS personnalisé (textarea)
- Définir un logo personnalisé (URL)
- Prévisualiser les modifications en temps réel avant sauvegarde
- Réinitialiser un thème à ses valeurs par défaut
| Fonction | Retour | Description |
|---|---|---|
owl_theme_head() |
string (HTML) |
Bloc <style> + <script> à inclure dans <head> |
owl_theme() |
string |
Clé du thème actif (ex: 'dark') |
owl_theme_list() |
array |
Liste des thèmes [{key, label, primary}] |
owl_theme_config('--owl-primary') |
string|null |
Valeur d'une variable CSS du thème actif |
owl_theme_data() |
array |
Données complètes du thème (pour JS) |
owl_theme_prefix() |
string |
Préfixe CSS configuré |
Si owl-concept/settings-bundle est installé, le bundle s'y intègre automatiquement :
- La préférence de thème est stockée comme une user preference dans owl-settings
- Aucune configuration supplémentaire nécessaire
- Si owl-settings n'est pas installé, le bundle fonctionne de manière autonome avec ses propres tables
owl_theme_active
├── scope VARCHAR(50) ── PK composite ('global' ou 'user')
├── scope_id VARCHAR(255) ── PK composite ('' ou userId)
├── theme_key VARCHAR(100)
└── updated_at DATETIME
owl_themes
├── theme_key VARCHAR(100) ── PK composite
├── scope VARCHAR(50) ── PK composite
├── scope_id VARCHAR(255) ── PK composite
├── overrides TEXT (nullable) ── JSON des variables CSS modifiées
├── custom_css TEXT (nullable)
├── logo_url VARCHAR(500) (nullable)
├── created_at DATETIME
└── updated_at DATETIME
:root {
--owl-primary /* Couleur principale */
--owl-primary-hover /* Couleur principale au survol */
--owl-primary-rgb /* Valeur RGB (pour rgba()) */
--owl-secondary /* Couleur secondaire */
--owl-success /* Succès (vert) */
--owl-warning /* Avertissement (jaune/orange) */
--owl-danger /* Erreur (rouge) */
--owl-info /* Information (cyan) */
--owl-bg /* Arrière-plan principal */
--owl-bg-alt /* Arrière-plan alternatif */
--owl-surface /* Surface (cartes, modales) */
--owl-text /* Texte principal */
--owl-text-muted /* Texte secondaire */
--owl-text-inverse /* Texte inversé */
--owl-border /* Bordure */
--owl-border-light /* Bordure légère */
--owl-shadow /* Ombre */
--owl-radius /* Rayon de bordure */
--owl-font-family /* Police */
--owl-font-size /* Taille de police */
--owl-sidebar-bg /* Sidebar arrière-plan */
--owl-sidebar-text /* Sidebar texte */
--owl-header-bg /* Header arrière-plan */
--owl-header-text /* Header texte */
--owl-transition-duration /* Durée de transition */
}- 5 built-in themes — Light, Dark, Ocean, Forest, Sunset — each with a complete color palette
- Instant switching — theme changes without page reload via CSS custom properties
- Anti-flash — inline synchronous script restores theme from
localStoragebefore first paint - Per-user preference — each user can choose their own theme
- System detection — automatically detects
prefers-color-scheme: dark - Admin customization — color pickers, injectable custom CSS, customizable logo
- Real-time preview — preview changes before saving
- Custom themes — add themes via YAML or programmatically (builder)
- DBAL storage — no Doctrine ORM needed, works with DBAL alone
- Symfony Cache — active theme is cached, automatically invalidated
- Built-in AJAX controller — 4 ready-to-use routes
- Vanilla JavaScript — zero dependencies,
OwlTheme.set(),OwlTheme.get()API - BEM CSS — modular styles, mobile responsive
- owl-settings integration — if
owl-settings-bundleis installed, theme preference is stored there - Install command —
php bin/console owl:theme:installcreates tables automatically
| Theme | Description | Primary Color |
|---|---|---|
| Light | Clean and neutral, ideal for office use | #3B82F6 |
| Dark | Dark mode, reduces eye strain | #60A5FA |
| Ocean | Professional blue tones | #0EA5E9 |
| Forest | Natural green tones, calming | #16A34A |
| Sunset | Warm orange and amber tones | #EA580C |
Each theme defines 24 CSS variables: brand colors, status colors, backgrounds, text, borders, shadows, layout (sidebar/header).
composer require owl-concept/theme-bundleIf the flex recipe is not available, add the bundle manually:
// config/bundles.php
return [
// ...
OwlConcept\ThemeBundle\OwlThemeBundle::class => ['all' => true],
];Create the database tables:
php bin/console owl:theme:install # Show SQL
php bin/console owl:theme:install --force # Execute SQLImport the bundle routes:
# config/routes/owl_theme.yaml
owl_theme:
resource: '@OwlThemeBundle/Resources/config/routes.yaml'# config/packages/owl_theme.yaml
owl_theme:
default_theme: light # Default theme
allow_user_themes: true # Per-user preference
detect_system_preference: true # Detect prefers-color-scheme
transition_duration: '0.3s' # CSS transition duration
cache_pool: cache.app # Symfony cache pool
cache_ttl: 3600 # Cache TTL in seconds
css_class_prefix: owl-theme # BEM CSS prefix
themes_table: owl_themes # Themes table name
active_table: owl_theme_active # Active theme table name
# Custom themes (optional)
themes:
my_brand:
--owl-primary: "#E11D48"
--owl-bg: "#FFF1F2"
--owl-text: "#881337"{# base.html.twig #}
<head>
{{ owl_theme_head() }}
</head>This generates:
- A
<style>block with CSS variables for the active theme - A synchronous
<script>that restores the theme fromlocalStorage(anti-flash) - JSON theme data for JavaScript
{# Button mode (default) #}
{% include '@OwlTheme/switcher.html.twig' %}
{# Dropdown mode #}
{% include '@OwlTheme/switcher.html.twig' with { switcher_mode: 'dropdown' } %}{% include '@OwlTheme/admin.html.twig' %}.my-button {
background-color: var(--owl-primary);
color: var(--owl-text-inverse);
border-radius: var(--owl-radius);
}
.my-sidebar {
background: var(--owl-sidebar-bg);
color: var(--owl-sidebar-text);
}// In a service or listener
$themeBuilder->addTheme('corporate', 'Corporate', [
'--owl-primary' => '#1D4ED8',
'--owl-bg' => '#F8FAFC',
'--owl-sidebar-bg' => '#1E3A5F',
], 'Company branded theme');// Switch theme (instant + AJAX persistence)
OwlTheme.set('dark');
// Get current theme
OwlTheme.get(); // → 'dark'
// List available themes
OwlTheme.list(); // → ['light', 'dark', 'ocean', 'forest', 'sunset']
// Listen for theme changes
OwlTheme.onChange(function(newTheme, oldTheme) {
console.log('Theme changed from ' + oldTheme + ' to ' + newTheme);
});
// Detect system preference
OwlTheme.detectSystemPreference(); // → 'dark' or 'light'The admin page (@OwlTheme/admin.html.twig) allows:
- Modify colors of each theme via color pickers
- Inject custom CSS (textarea)
- Set a custom logo (URL)
- Preview changes in real-time before saving
- Reset a theme to its default values
| Function | Returns | Description |
|---|---|---|
owl_theme_head() |
string (HTML) |
<style> + <script> block for <head> |
owl_theme() |
string |
Active theme key (e.g. 'dark') |
owl_theme_list() |
array |
Theme list [{key, label, primary}] |
owl_theme_config('--owl-primary') |
string|null |
CSS variable value for active theme |
owl_theme_data() |
array |
Complete theme data (for JS) |
owl_theme_prefix() |
string |
Configured CSS prefix |
If owl-concept/settings-bundle is installed, the bundle integrates automatically:
- Theme preference is stored as a user preference in owl-settings
- No additional configuration needed
- If owl-settings is not installed, the bundle works standalone with its own tables
owl_theme_active
├── scope VARCHAR(50) ── Composite PK ('global' or 'user')
├── scope_id VARCHAR(255) ── Composite PK ('' or userId)
├── theme_key VARCHAR(100)
└── updated_at DATETIME
owl_themes
├── theme_key VARCHAR(100) ── Composite PK
├── scope VARCHAR(50) ── Composite PK
├── scope_id VARCHAR(255) ── Composite PK
├── overrides TEXT (nullable) ── JSON of customized CSS variables
├── custom_css TEXT (nullable)
├── logo_url VARCHAR(500) (nullable)
├── created_at DATETIME
└── updated_at DATETIME
:root {
--owl-primary /* Primary color */
--owl-primary-hover /* Primary color on hover */
--owl-primary-rgb /* RGB value (for rgba()) */
--owl-secondary /* Secondary color */
--owl-success /* Success (green) */
--owl-warning /* Warning (yellow/orange) */
--owl-danger /* Error (red) */
--owl-info /* Information (cyan) */
--owl-bg /* Main background */
--owl-bg-alt /* Alternative background */
--owl-surface /* Surface (cards, modals) */
--owl-text /* Main text */
--owl-text-muted /* Secondary text */
--owl-text-inverse /* Inverse text */
--owl-border /* Border */
--owl-border-light /* Light border */
--owl-shadow /* Shadow */
--owl-radius /* Border radius */
--owl-font-family /* Font family */
--owl-font-size /* Font size */
--owl-sidebar-bg /* Sidebar background */
--owl-sidebar-text /* Sidebar text */
--owl-header-bg /* Header background */
--owl-header-text /* Header text */
--owl-transition-duration /* Transition duration */
}Proprietary — OWL Concept