Theme Builder et champ couleur Quasar
Le Theme Builder centralise l’identité visuelle des formulaires construits avec FormBuilder et rejoués avec FormViewer.
Il remplace les styles dispersés par un contrat de design tokens sérialisable : palette, typographie, espacements, rayons, variante des contrôles, densité, arrière-plan, profondeur, mouvement et iconographie.
Ouvrir le Theme Builder
Le bouton Thème de la barre supérieure ouvre le panneau latéral. Il propose cinq presets :
- Enterprise ;
- Ocean ;
- Slate ;
- Emerald ;
- Sunset.
Toute modification manuelle fait passer le thème en mode custom. La prévisualisation du panneau est mise à jour immédiatement.
L’onglet Typographie emploie une grille responsive dédiée : taille de base et graisse restent alignées sur deux colonnes lorsque l’espace le permet, puis se replient sur une colonne. La hauteur de ligne affiche sa valeur dans un badge fixe afin d’éviter le chevauchement des labels flottants de slider.
Personnalisation avancée 0.1.12
Le panneau ajoute deux domaines :
- Effets : fond neutre, dégradé linéaire ou radial, angle, bordure, élévation, flou d’arrière-plan et animation d’entrée ;
- Icônes : fournisseur Quasar ou Iconify, pictogrammes des actions primaire/secondaire/destructive, envoi, précédent et suivant, ainsi que leur taille.
L’aperçu rejoue immédiatement les mouvements fade, slide-up, scale et soft-pulse. Le réglage système prefers-reduced-motion les désactive automatiquement.
UnoCSS est utilisé comme couche utilitaire du Theme Builder : presetWind3 pour les raccourcis visuels et animations de l’interface, presetIcons pour les glyphes Iconify. Le catalogue Iconify MDI est embarqué localement avec @iconify-json/mdi ; aucun appel à une API distante n’est requis au rendu.
Le sélecteur d’icônes s’appuie sur QIconPicker. Il reçoit uniquement un catalogue contrôlé par le layer, avec recherche, pagination, navigation clavier et QTooltip Quasar. La dépendance est volontairement figée sur la version stable 2.0.7, compatible Vue 3 / Quasar 2 ; son contrat v2 (model-pagination, itemsPerPage et slot icon) est couvert par les contrôles statiques du projet.
Contrôler le thème depuis l’application hôte
<script setup lang="ts">
import type { QFormBuilderThemeConfig } from '@vevedh/qform-builder-layer/types'
import { createQFormBuilderTheme } from '@vevedh/qform-builder-layer/theme'
const schema = ref([])
const settings = ref({
preview: { width: 768, isFullWidth: false },
previewMode: 'editing' as const,
columns: 'sm' as const,
theme: createQFormBuilderTheme('enterprise'),
})
const theme = ref<QFormBuilderThemeConfig>(settings.value.theme)
const themeOpen = ref(false)
</script>
<template>
<FormBuilder
v-model:schema="schema"
v-model:settings="settings"
v-model:theme="theme"
v-model:theme-open="themeOpen"
/>
</template>Le thème est également inclus dans settings et dans le payload de save(). v-model:theme fournit un accès direct lorsque l’application veut stocker ou éditer le thème séparément.
Rejouer le même thème avec FormViewer
<FormViewer
v-model="values"
:form-fields="schema"
:theme="settings.theme"
/>Le builder et le viewer utilisent le même normaliseur et les mêmes variables CSS. Un formulaire sauvegardé conserve donc son rendu sans dépendre de la page d’administration qui l’a produit.
API publique
import {
applyQFormBuilderThemePreset,
createQFormBuilderTheme,
mergeQFormBuilderTheme,
normalizeQFormBuilderTheme,
qFormBuilderThemeToCssVariables,
} from '@vevedh/qform-builder-layer/theme'Méthodes exposées par la référence de FormBuilder :
builderRef.value?.setTheme({
preset: 'custom',
palette: { primary: '#0057b8' },
})
const currentTheme = builderRef.value?.getTheme()
builderRef.value?.openTheme()
builderRef.value?.closeTheme()Design tokens
Le contrat QFormBuilderThemeConfig couvre :
interface QFormBuilderThemeConfig {
preset: 'enterprise' | 'ocean' | 'slate' | 'emerald' | 'sunset' | 'custom'
palette: {
primary: string
secondary: string
accent: string
positive: string
negative: string
warning: string
info: string
surface: string
surfaceMuted: string
text: string
textMuted: string
border: string
}
typography: {
fontFamily: string
baseSize: number
labelWeight: 400 | 500 | 600 | 700
lineHeight: number
}
spacing: {
fieldGap: number
sectionGap: number
canvasPadding: number
controlPaddingX: number
controlPaddingY: number
}
radius: {
control: number
container: number
button: number
}
inputVariant: 'filled' | 'outlined' | 'standout' | 'borderless'
density: 'comfortable' | 'compact'
shadow: boolean
background: {
type: 'none' | 'linear' | 'radial'
from: string
via: string
to: string
angle: number
}
effects: {
borderWidth: number
elevation: number
backdropBlur: number
}
motion: {
preset: 'none' | 'fade' | 'slide-up' | 'scale' | 'soft-pulse'
duration: number
easing: 'ease' | 'ease-out' | 'ease-in-out' | 'linear'
}
icons: {
provider: 'quasar' | 'iconify'
primaryAction: string
secondaryAction: string
dangerAction: string
submit: string
previous: string
next: string
size: number
}
}Les tokens sont appliqués dans le périmètre .qform-theme. Ils ne modifient pas globalement la palette Quasar de l’application hôte.
Nouveau champ couleur q-color
Le catalogue Community n’utilise plus un simple QInput type="color" dépendant du navigateur. Le nouveau type FormKit q-color associe :
- un champ texte contrôlé ;
- un aperçu de la couleur ;
- un popup Quasar
QColor; - les formats HEX, HEXA, RGB et RGBA ;
- les vues spectre, réglages et palette ;
- une palette personnalisable ;
- un en-tête et un pied de sélecteur masquables ;
- une valeur effaçable.
Exemple de schéma :
const colorField = {
$formkit: 'q-color',
name: 'brandColor',
label: 'Couleur principale',
value: '#2563eb',
formatModel: 'hexa',
defaultView: 'spectrum',
palette: ['#2563eb', '#0f766e', '#7c3aed'],
clearable: true,
}Les anciens schémas utilisant $formkit: 'q-input' avec inputType: 'color' restent rendus pour compatibilité. Ils peuvent être migrés progressivement vers q-color pour bénéficier des nouvelles options.
Validation et sécurité
Le champ q-color suit volontairement le contrat natif de QColor : HEX, HEXA, RGB et RGBA. Les design tokens du Theme Builder utilisent un normaliseur CSS distinct, qui accepte aussi HSL et HSLA. Les valeurs actives telles que url(...), les déclarations CSS concaténées et les familles de polices contenant des caractères de déclaration sont rejetées. Les noms d’icônes sont eux aussi limités à une liste blanche : aucune classe UnoCSS arbitraire provenant d’un schéma ou d’un thème n’est exécutée.
Cette séparation évite de transmettre à QColor un format qu’il ne sait pas interpréter, tout en conservant la souplesse des tokens CSS. La validation protège le rendu du builder ; les thèmes reçus depuis un client doivent néanmoins être validés côté service Feathers/NFZ avant persistance.
Compatibilité des modèles Quasar
Les adapters du layer normalisent les modèles avant de les transmettre aux composants Quasar :
q-colorreçoit uniquement une chaîne QColor compatible ounull;- le
QInputde saisie couleur est explicitement un contrôle texte ; q-timeetq-datetimeconvertissent les valeurs FormKit absentes (undefinedou chaîne vide) versnull;- les attributs de schéma
format24h,withSecondsetnowBtnsont convertis en vrais booléens ; - les attributs propres au picker ne sont pas propagés au contrôle HTML interne.
Ces règles empêchent les avertissements navigateur du type « value cannot be parsed » et les avertissements Vue relatifs au validateur de QTime.
Démonstration
Le playground fournit la route :
/themeElle contient un builder contrôlé, un champ q-color et la synchronisation directe du thème.
Cohérence automatique clair/sombre
Le Theme Builder distingue désormais le thème sérialisé du thème effectif de rendu :
- le thème sauvegardé conserve les couleurs choisies pour la marque et les statuts ;
- lorsque Quasar active le mode sombre, le layer dérive uniquement les surfaces, textes et bordures nécessaires au contraste ;
- aucune mutation n’est appliquée au schéma ni au thème enregistré.
Palette sombre de référence :
{
surface: '#0f172a',
surfaceMuted: '#1e293b',
text: '#f8fafc',
textMuted: '#cbd5e1',
border: '#475569',
}Les helpers publics acceptent un second argument optionnel :
import {
qFormBuilderThemeInputProps,
qFormBuilderThemeToCssVariables,
resolveQFormBuilderThemeForColorMode,
} from '@vevedh/qform-builder-layer/theme'
const effectiveTheme = resolveQFormBuilderThemeForColorMode(savedTheme, {
dark: true,
})
const style = qFormBuilderThemeToCssVariables(savedTheme, {
dark: true,
})
const inputProps = qFormBuilderThemeInputProps(savedTheme, {
dark: true,
})FormBuilder, FormViewer et le panneau Theme Builder utilisent automatiquement useQuasar().dark.isActive. L’application hôte n’a donc rien à synchroniser en plus du mode sombre Quasar.
Composants couverts
Le périmètre .qform-theme normalise notamment :
QInput,QSelect,QFile,QColoret les pickers date/heure ;QEditor, sa toolbar et son contenu ;- cases à cocher, radios, toggles, groupes et boutons segmentés ;
- sliders, ranges, ratings, onglets et stepper ;
- sections, fieldsets, colonnes, overlays d’édition et champs imbriqués ;
- citations, bannières d’information/alerte et blocs de code, y compris dans les anciens schémas.
Les contenus statiques reçoivent une classe sémantique au rendu sans modifier la donnée source. Cette compatibilité évite de devoir migrer immédiatement les formulaires enregistrés avec les anciennes classes bg-*-1 / text-*-9.
Audit visuel
Le playground expose :
/dark-mode-auditCommandes associées :
bun run dark-mode:check
bun run dark-mode:runtime:check
bun run e2e:visual:update
bun run e2e:visualLes snapshots protègent l’aperçu clair, l’aperçu sombre et l’éditeur sombre. Toute nouvelle surface de composant doit utiliser les variables --qfb-surface, --qfb-surface-muted, --qfb-text, --qfb-text-muted et --qfb-border, plutôt qu’une couleur claire codée en dur.