Skip to content

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

vue
<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

vue
<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

ts
import {
  applyQFormBuilderThemePreset,
  createQFormBuilderTheme,
  mergeQFormBuilderTheme,
  normalizeQFormBuilderTheme,
  qFormBuilderThemeToCssVariables,
} from '@vevedh/qform-builder-layer/theme'

Méthodes exposées par la référence de FormBuilder :

ts
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 :

ts
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 :

ts
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-color reçoit uniquement une chaîne QColor compatible ou null ;
  • le QInput de saisie couleur est explicitement un contrôle texte ;
  • q-time et q-datetime convertissent les valeurs FormKit absentes (undefined ou chaîne vide) vers null ;
  • les attributs de schéma format24h, withSeconds et nowBtn sont 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 :

txt
/theme

Elle 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 :

ts
{
  surface: '#0f172a',
  surfaceMuted: '#1e293b',
  text: '#f8fafc',
  textMuted: '#cbd5e1',
  border: '#475569',
}

Les helpers publics acceptent un second argument optionnel :

ts
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, QColor et 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 :

txt
/dark-mode-audit

Commandes associées :

bash
bun run dark-mode:check
bun run dark-mode:runtime:check
bun run e2e:visual:update
bun run e2e:visual

Les 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.

QForm Builder — layer Nuxt 4, Quasar et FormKit pour formulaires dynamiques.