Skip to content

Tokens sémantiques

Les tokens sémantiques sont le cœur du système de design. Ils transforment des couleurs brutes en concepts significatifs, permettant une cohérence parfaite et une maintenance simplifiée.

Qu'est-ce qu'un token sémantique ?

Un token sémantique attribue un sens contextuel à une valeur technique.

Exemple

scss
// ❌ Valeur technique (couleur brute)
color: #111827;

// ✅ Token sémantique (intention claire)
color: var(--su-text-primary);

Avantages :

  • 🎯 Intention claire : "texte principal" vs "gris 900"
  • 🔄 Adaptabilité : Change automatiquement selon le thème et le mode
  • 🛠️ Maintenance : Un seul endroit à modifier
  • Accessibilité : Garanties de contraste intégrées

Architecture des tokens

Foundations (Palettes brutes)

Tokens sémantiques (Signification)

Variables CSS (Application via data-theme + data-theme-mode)

Composants (Utilisation)

Catégories de tokens

1. Texte

Tokens pour tous les contenus textuels, avec contraste WCAG garanti.

--su-text-primary

Texte principal, contraste maximum (16.8:1 en mode light).

vue
<style scoped lang="scss">
.heading {
  color: var(--su-text-primary);
}
</style>

Usage : Titres, texte principal, contenu important.

--su-text-secondary

Texte secondaire, bon contraste (9.3:1 en mode light).

vue
<style scoped lang="scss">
.description {
  color: var(--su-text-secondary);
}
</style>

Usage : Descriptions, sous-titres, métadonnées.

--su-text-tertiary

Texte tertiaire, contraste minimum AA (4.7:1 en mode light).

vue
<style scoped lang="scss">
.caption {
  color: var(--su-text-tertiary);
}
</style>

Usage : Labels, timestamps, informations auxiliaires.

--su-text-disabled

Texte désactivé, indication visuelle d'indisponibilité.

vue
<style scoped lang="scss">
.input:disabled {
  color: var(--su-text-disabled);
}
</style>

Usage : Champs désactivés, actions indisponibles.

--su-text-inverse

Texte sur fond sombre (ou inversé selon le thème).

vue
<style scoped lang="scss">
.dark-banner {
  background-color: var(--su-primary-default);
  color: var(--su-text-inverse);
}
</style>

Usage : Texte sur boutons primaires, bannières colorées.

--su-text-on-inverse

Texte sur un fond inversé (--su-bg-inverse).

vue
<style scoped lang="scss">
.inverted-section {
  background-color: var(--su-bg-inverse);
  color: var(--su-text-on-inverse);
}
</style>

Usage : Texte dans les sections qui contrastent avec le fond principal (ex. hero sombre sur page claire).

2. Liens

Tokens spécifiques pour les liens hypertexte.

Couleur par défaut des liens.

vue
<style scoped lang="scss">
a {
  color: var(--su-link-default);
  text-decoration: underline;
}
</style>

Couleur au survol.

vue
<style scoped lang="scss">
a:hover {
  color: var(--su-link-hover);
}
</style>

Liens déjà visités (améliore la navigation).

vue
<style scoped lang="scss">
a:visited {
  color: var(--su-link-visited);
}
</style>

Liens secondaires, moins proéminents.

vue
<style scoped lang="scss">
.footer-link {
  color: var(--su-link-muted);
}
</style>

Valeur RGB espace-séparée du lien par défaut, pour les transparences.

vue
<style scoped lang="scss">
.link-overlay {
  // Utilisation avec la syntaxe rgb() moderne
  background-color: rgb(var(--su-link-default-rgb) / 15%);
  border-color: rgb(var(--su-link-default-rgb) / 50%);
}
</style>

Usage : Fonds semi-transparents basés sur la couleur du lien, badges, indicateurs.

3. Backgrounds

Tokens pour tous les arrière-plans.

--su-bg-canvas

Fond global de l'application.

vue
<style scoped lang="scss">
body {
  background-color: var(--su-bg-canvas);
}
</style>

Usage : Background de la page principale.

--su-bg-surface

Surface pour cartes, modales, panels.

vue
<style scoped lang="scss">
.card {
  background-color: var(--su-bg-surface);
}
</style>

Usage : Cartes, modales, menus déroulants.

--su-bg-surface-elevated

Surface surélevée (avec ombre).

vue
<style scoped lang="scss">
.modal {
  background-color: var(--su-bg-surface-elevated);
  box-shadow: var(--su-shadow-lg);
}
</style>

Usage : Modales, tooltips, éléments flottants.

--su-bg-overlay

Overlay semi-transparent pour backdrops.

vue
<style scoped lang="scss">
.modal-backdrop {
  background-color: var(--su-bg-overlay);
}
</style>

Usage : Fonds de modales, menus plein écran.

--su-bg-hover

Background au survol d'éléments interactifs.

vue
<style scoped lang="scss">
.list-item:hover {
  background-color: var(--su-bg-hover);
}
</style>

Usage : Items de liste, boutons secondaires.

--su-bg-active

Background en état actif/pressé.

vue
<style scoped lang="scss">
.list-item:active {
  background-color: var(--su-bg-active);
}
</style>

Usage : État cliqué, boutons pressés.

--su-bg-selected

Background pour éléments sélectionnés.

vue
<style scoped lang="scss">
.tab--active {
  background-color: var(--su-bg-selected);
}
</style>

Usage : Tabs actifs, items sélectionnés.

--su-bg-disabled

Background désactivé.

vue
<style scoped lang="scss">
.button:disabled {
  background-color: var(--su-bg-disabled);
}
</style>

Usage : Boutons, champs désactivés.

--su-bg-inverse

Fond inversé - contraste avec le fond principal du thème.

vue
<style scoped lang="scss">
.hero-banner {
  background-color: var(--su-bg-inverse);
  color: var(--su-text-on-inverse);
}
</style>

Usage : Sections promotionnelles, bannières hero, zones de mise en avant qui doivent contraster avec le contexte sans forcer un thème entier.

--su-bg-inverse-subtle

Fond inversé secondaire, moins prononcé que --su-bg-inverse.

vue
<style scoped lang="scss">
.callout {
  background-color: var(--su-bg-inverse-subtle);
  color: var(--su-text-on-inverse);
}
</style>

Usage : Encadrés, callouts, zones d'information légèrement contrastées.

--su-surface-inverse

Surface posée sur un fond inversé (équivalent de --su-bg-surface dans un contexte inversé).

vue
<style scoped lang="scss">
.hero-card {
  background-color: var(--su-bg-inverse);

  .hero-card__panel {
    background-color: var(--su-surface-inverse);
  }
}
</style>

4. Bordures

Tokens pour toutes les bordures.

--su-border-default

Bordure standard.

vue
<style scoped lang="scss">
.card {
  border: 1px solid var(--su-border-default);
}
</style>

Usage : Cartes, séparateurs, outlines.

--su-border-subtle

Bordure très subtile.

vue
<style scoped lang="scss">
.divider {
  border-bottom: 1px solid var(--su-border-subtle);
}
</style>

Usage : Séparateurs légers, lignes de grille.

--su-border-strong

Bordure renforcée.

vue
<style scoped lang="scss">
.card:hover {
  border-color: var(--su-border-strong);
}
</style>

Usage : Hover states, éléments mis en avant.

--su-border-focus

Bordure de focus (accessibilité).

vue
<style scoped lang="scss">
.input:focus {
  border-color: var(--su-border-focus);
  outline: 2px solid var(--su-border-focus);
}
</style>

Usage : Focus visible, navigation au clavier.

--su-border-disabled

Bordure désactivée.

vue
<style scoped lang="scss">
.input:disabled {
  border-color: var(--su-border-disabled);
}
</style>

Usage : Champs désactivés.

--su-border-inverse

Bordure dans un contexte de fond inversé.

vue
<style scoped lang="scss">
.inverted-card {
  background-color: var(--su-bg-inverse);
  border: 1px solid var(--su-border-inverse);
}
</style>

Usage : Séparateurs et contours sur fonds inversés.

5. États

Tokens pour les messages d'état et feedbacks.

Success (Succès)

vue
<style scoped lang="scss">
.alert-success {
  color: var(--su-state-success);
  background-color: var(--su-state-success-bg);
}
</style>

Usage : Validation, opération réussie, badges actifs.

Warning (Avertissement)

vue
<style scoped lang="scss">
.alert-warning {
  color: var(--su-state-warning);
  background-color: var(--su-state-warning-bg);
}
</style>

Usage : Avertissements, actions requérant attention.

Error (Erreur)

vue
<style scoped lang="scss">
.alert-error {
  color: var(--su-state-error);
  background-color: var(--su-state-error-bg);
}
</style>

Usage : Erreurs, échecs, actions destructives.

Info (Information)

vue
<style scoped lang="scss">
.alert-info {
  color: var(--su-state-info);
  background-color: var(--su-state-info-bg);
}
</style>

Usage : Informations neutres, aide contextuelle.

6. Actions primaires

Tokens pour les boutons et actions principales.

--su-primary-default

État par défaut du bouton primaire.

vue
<style scoped lang="scss">
.button-primary {
  background-color: var(--su-primary-default);
  color: var(--su-primary-text);
}
</style>

--su-primary-hover

État survol.

vue
<style scoped lang="scss">
.button-primary:hover {
  background-color: var(--su-primary-hover);
}
</style>

--su-primary-active

État actif/pressé.

vue
<style scoped lang="scss">
.button-primary:active {
  background-color: var(--su-primary-active);
}
</style>

--su-primary-disabled

État désactivé.

vue
<style scoped lang="scss">
.button-primary:disabled {
  background-color: var(--su-primary-disabled);
}
</style>

--su-primary-text

Texte sur bouton primaire.

vue
<style scoped lang="scss">
.button-primary {
  color: var(--su-primary-text);
}
</style>

7. Actions secondaires

Tokens pour les boutons secondaires.

vue
<style scoped lang="scss">
.button-secondary {
  background-color: var(--su-secondary-default);
  color: var(--su-secondary-text);

  &:hover {
    background-color: var(--su-secondary-hover);
  }

  &:active {
    background-color: var(--su-secondary-active);
  }

  &:disabled {
    background-color: var(--su-secondary-disabled);
  }
}
</style>

8. Ombres

Token pour la couleur des ombres (s'adapte au thème).

vue
<style scoped lang="scss">
.card {
  box-shadow: 0 4px 6px var(--su-shadow-color);
}
</style>

Tokens statiques

Ces tokens ne changent pas selon le thème.

Typographie

scss
.text {
  font-family: var(--su-font-family-base);
  font-size: var(--su-font-size-base);
  line-height: var(--su-line-height-normal);
}

Disponibles :

  • --su-font-family-base : Police principale
  • --su-font-family-mono : Police monospace
  • --su-font-size-xs à --su-font-size-3xl : Échelle de tailles
  • --su-line-height-tight|normal|relaxed : Interlignages

Espacement

scss
.component {
  padding: var(--su-spacing-4);
  margin-bottom: var(--su-spacing-6);
  gap: var(--su-spacing-2);
}

Disponibles :

  • --su-spacing-1 : 0.25rem (4px)
  • --su-spacing-2 : 0.5rem (8px)
  • --su-spacing-3 : 0.75rem (12px)
  • --su-spacing-4 : 1rem (16px)
  • --su-spacing-6 : 1.5rem (24px)
  • --su-spacing-8 : 2rem (32px)
  • --su-spacing-12 : 3rem (48px)

Border Radius

scss
.card {
  border-radius: var(--su-radius-lg);
}

.button {
  border-radius: var(--su-radius-md);
}

.avatar {
  border-radius: var(--su-radius-full);
}

Disponibles :

  • --su-radius-sm : 0.25rem (4px)
  • --su-radius-md : 0.375rem (6px)
  • --su-radius-lg : 0.5rem (8px)
  • --su-radius-xl : 0.75rem (12px)
  • --su-radius-full : 9999px (cercle)

Ombres

scss
.card {
  box-shadow: var(--su-shadow-sm);
}

.modal {
  box-shadow: var(--su-shadow-lg);
}

Disponibles :

  • --su-shadow-xs : Ombre très légère
  • --su-shadow-sm : Ombre légère
  • --su-shadow-md : Ombre moyenne
  • --su-shadow-lg : Ombre forte

Animations

scss
.button {
  transition-duration: var(--su-duration-normal);
  transition-timing-function: var(--su-ease-in-out);
}

Disponibles :

  • --su-duration-fast : 150ms (ou 0ms si reduced-motion)
  • --su-duration-normal : 200ms (ou 0ms si reduced-motion)
  • --su-duration-slow : 300ms (ou 0ms si reduced-motion)
  • --su-ease-in-out : cubic-bezier(0.4, 0, 0.2, 1)
  • --su-animation-scale : 1 (ou 0 si reduced-motion)

Focus

scss
.input:focus {
  outline: var(--su-focus-ring-width) solid var(--su-border-focus);
  outline-offset: var(--su-focus-ring-offset);
}

Disponibles :

  • --su-focus-ring-width : 2px
  • --su-focus-ring-offset : 2px

Exemples d'utilisation

Carte complète

vue
<template>
  <div class="card">
    <h3 class="card__title">Titre de la carte</h3>
    <p class="card__description">Description secondaire</p>
    <button class="card__action">Action</button>
  </div>
</template>

<style scoped lang="scss">
.card {
  background-color: var(--su-bg-surface);
  border: 1px solid var(--su-border-default);
  border-radius: var(--su-radius-lg);
  padding: var(--su-spacing-6);
  box-shadow: var(--su-shadow-sm);

  &:hover {
    border-color: var(--su-border-strong);
  }
}

.card__title {
  color: var(--su-text-primary);
  font-size: var(--su-font-size-xl);
  margin: 0 0 var(--su-spacing-2);
}

.card__description {
  color: var(--su-text-secondary);
  font-size: var(--su-font-size-base);
  margin: 0 0 var(--su-spacing-4);
}

.card__action {
  background-color: var(--su-primary-default);
  color: var(--su-primary-text);
  padding: var(--su-spacing-3) var(--su-spacing-4);
  border: none;
  border-radius: var(--su-radius-md);
  cursor: pointer;
  transition: background-color var(--su-duration-normal);

  &:hover {
    background-color: var(--su-primary-hover);
  }

  &:focus-visible {
    outline: var(--su-focus-ring-width) solid var(--su-border-focus);
    outline-offset: var(--su-focus-ring-offset);
  }
}
</style>

Alerte avec état

vue
<template>
  <div :class="['alert', `alert--${type}`]">
    <IconComponent :type="type" />
    <div class="alert__content">
      <strong class="alert__title">{{ title }}</strong>
      <p class="alert__message">{{ message }}</p>
    </div>
  </div>
</template>

<style scoped lang="scss">
.alert {
  display: flex;
  gap: var(--su-spacing-3);
  padding: var(--su-spacing-4);
  border-radius: var(--su-radius-md);
  border-left: 3px solid;

  &--success {
    color: var(--su-state-success);
    background-color: var(--su-state-success-bg);
    border-left-color: var(--su-state-success);
  }

  &--warning {
    color: var(--su-state-warning);
    background-color: var(--su-state-warning-bg);
    border-left-color: var(--su-state-warning);
  }

  &--error {
    color: var(--su-state-error);
    background-color: var(--su-state-error-bg);
    border-left-color: var(--su-state-error);
  }

  &--info {
    color: var(--su-state-info);
    background-color: var(--su-state-info-bg);
    border-left-color: var(--su-state-info);
  }
}

.alert__title {
  display: block;
  margin-bottom: var(--su-spacing-1);
  font-weight: 600;
}

.alert__message {
  margin: 0;
  opacity: 0.9;
}
</style>

Formulaire

vue
<template>
  <div class="form-field">
    <label class="form-field__label" :for="id">{{ label }}</label>
    <input
      :id="id"
      class="form-field__input"
      :disabled="disabled"
      :class="{ 'form-field__input--error': hasError }"
    />
    <span v-if="hasError" class="form-field__error">{{ errorMessage }}</span>
  </div>
</template>

<style scoped lang="scss">
.form-field {
  display: flex;
  flex-direction: column;
  gap: var(--su-spacing-2);
}

.form-field__label {
  color: var(--su-text-primary);
  font-size: var(--su-font-size-sm);
  font-weight: 500;
}

.form-field__input {
  padding: var(--su-spacing-3) var(--su-spacing-4);
  font-size: var(--su-font-size-base);
  color: var(--su-text-primary);
  background-color: var(--su-bg-surface);
  border: 2px solid var(--su-border-default);
  border-radius: var(--su-radius-md);
  transition: border-color var(--su-duration-normal);

  &:hover:not(:disabled) {
    border-color: var(--su-border-strong);
  }

  &:focus {
    border-color: var(--su-border-focus);
    outline: none;
  }

  &:disabled {
    background-color: var(--su-bg-disabled);
    color: var(--su-text-disabled);
    border-color: var(--su-border-disabled);
    cursor: not-allowed;
  }

  &--error {
    border-color: var(--su-state-error);
  }
}

.form-field__error {
  color: var(--su-state-error);
  font-size: var(--su-font-size-sm);
}
</style>

Bonnes pratiques

✅ À faire

  1. Toujours utiliser les tokens plutôt que les valeurs brutes
  2. Choisir le token le plus sémantique pour le contexte
  3. Respecter la hiérarchie : primary > secondary > tertiary
  4. Tester tous les thèmes et les deux modes pour vérifier la cohérence
  5. Utiliser les mixins pour les patterns répétitifs
  6. Utiliser --su-bg-inverse + --su-text-on-inverse pour les sections inversées (plutôt que de forcer un thème avec data-theme-mode)

❌ À éviter

  1. Couleurs en dur : color: #111827
  2. Tokens non sémantiques : utiliser primary-600 au lieu de primary-default
  3. Mélanger les niveaux : utiliser des couleurs brutes avec des tokens ❌
  4. Ignorer les états : ne pas gérer hover, focus, disabled ❌
  5. Oublier l'accessibilité : contraste insuffisant ❌

Extension des tokens

Ajouter des tokens personnalisés

Si vous avez besoin de tokens spécifiques à votre application :

scss
// Dans votre thème personnalisé -  tokens/light.scss
$theme-custom-light: (
  // Tokens standard
  'text-primary': #1a202c,
  'bg-surface': #ffffff,

  // Tokens personnalisés
  'brand-highlight': #ff6b6b,
  'accent-secondary': #4ecdc4,
  'sidebar-bg': #f8f9fa,
);

Puis utilisez-les :

scss
.custom-component {
  background-color: var(--su-brand-highlight);
  border-left: 3px solid var(--su-accent-secondary);
}

Inspection des tokens

Via DevTools

javascript
// Console navigateur
const root = document.documentElement;
const styles = getComputedStyle(root);

// Voir tous les tokens
for (let i = 0; i < styles.length; i++) {
  const prop = styles[i];
  if (prop.startsWith('--su-')) {
    console.log(prop, ':', styles.getPropertyValue(prop));
  }
}

// Voir un token spécifique
console.log('Primary:', styles.getPropertyValue('--su-primary-default'));

Via Vue DevTools

vue
<script setup>
import { onMounted } from 'vue';

onMounted(() => {
  const primary = getComputedStyle(document.documentElement)
    .getPropertyValue('--su-primary-default');
  console.log('Couleur primaire:', primary);
});
</script>

Tableau récapitulatif complet

Tokens de couleur

TokenMode LightMode DarkUsage
--su-text-primary#111827 (16.8:1)#f9fafb (17.1:1)Texte principal
--su-text-secondary#374151 (9.3:1)#e5e7eb (12.6:1)Texte secondaire
--su-text-tertiary#6b7280 (4.7:1)#9ca3af (4.5:1)Texte tertiaire
--su-text-disabled#9ca3af#6b7280Texte désactivé
--su-text-inverse#f9fafb#111827Texte inversé
--su-text-on-inverse#f9fafb#111827Texte sur fond inversé
--su-link-default#2563eb#60a5faLien par défaut
--su-link-hover#1d4ed8#93c5fdLien survol
--su-link-visited#1e40af#a855f7Lien visité
--su-link-muted#6b7280#6b7280Lien secondaire
--su-link-default-rgb37 99 23596 165 250RGB lien pour transparences
--su-bg-canvas#f9fafb#030712Fond global
--su-bg-surface#ffffff#111827Surface/carte
--su-bg-surface-elevated#ffffff#1f2937Surface surélevée
--su-bg-overlayrgba(0,0,0,0.5)rgba(0,0,0,0.7)Overlay modal
--su-bg-hover#f3f4f6#1f2937Fond survol
--su-bg-active#e5e7eb#374151Fond actif
--su-bg-selected#eff6ffrgba(59,130,246,0.2)Fond sélectionné
--su-bg-disabled#f3f4f6#1f2937Fond désactivé
--su-bg-inverse#111827#f9fafbFond inversé
--su-bg-inverse-subtle#1f2937#f3f4f6Fond inversé secondaire
--su-surface-inverse#374151#e5e7ebSurface sur fond inversé
--su-border-default#d1d5db#4b5563Bordure standard
--su-border-subtle#e5e7eb#374151Bordure subtile
--su-border-strong#9ca3af#6b7280Bordure forte
--su-border-focus#3b82f6#60a5faBordure focus
--su-border-disabled#e5e7eb#374151Bordure désactivée
--su-border-inverse#4b5563#d1d5dbBordure sur fond inversé
--su-state-success#059669#4ade80État succès
--su-state-success-bg#ecfdf5rgba(74,222,128,0.15)Fond succès
--su-state-warning#d97706#facc15État warning
--su-state-warning-bg#fffbebrgba(250,204,21,0.15)Fond warning
--su-state-error#dc2626#f87171État erreur
--su-state-error-bg#fef2f2rgba(248,113,113,0.15)Fond erreur
--su-state-info#0891b2#38bdf8État info
--su-state-info-bg#ecfeffrgba(56,189,248,0.15)Fond info
--su-primary-default#2563eb#3b82f6Action primaire
--su-primary-hover#1d4ed8#60a5faPrimaire survol
--su-primary-active#1e40af#1e40afPrimaire actif
--su-primary-disabled#93c5fd#374151Primaire désactivé
--su-primary-text#ffffff#ffffffTexte sur primaire
--su-secondary-default#f3f4f6#1f2937Action secondaire
--su-secondary-hover#e5e7eb#374151Secondaire survol
--su-secondary-active#d1d5db#4b5563Secondaire actif
--su-secondary-disabled#f9fafb#111827Secondaire désactivé
--su-secondary-text#111827#f9fafbTexte sur secondaire
--su-shadow-colorrgba(0,0,0,0.1)rgba(0,0,0,0.5)Couleur ombres

Tokens statiques

TokenValeurDescription
Typographie
--su-font-family-baseSystem fontsPolice principale
--su-font-family-monoMonospacePolice code
--su-font-size-xs0.75rem (12px)Taille XS
--su-font-size-sm0.875rem (14px)Taille Small
--su-font-size-base1rem (16px)Taille base
--su-font-size-lg1.125rem (18px)Taille Large
--su-font-size-xl1.25rem (20px)Taille XL
--su-font-size-2xl1.5rem (24px)Taille 2XL
--su-font-size-3xl1.875rem (30px)Taille 3XL
--su-line-height-tight1.25Interligne serré
--su-line-height-normal1.5Interligne normal
--su-line-height-relaxed1.75Interligne aéré
Espacement
--su-spacing-10.25rem (4px)Espacement XS
--su-spacing-20.5rem (8px)Espacement S
--su-spacing-30.75rem (12px)Espacement M
--su-spacing-41rem (16px)Espacement base
--su-spacing-61.5rem (24px)Espacement L
--su-spacing-82rem (32px)Espacement XL
--su-spacing-123rem (48px)Espacement 2XL
Border Radius
--su-radius-sm0.25rem (4px)Rayon small
--su-radius-md0.375rem (6px)Rayon medium
--su-radius-lg0.5rem (8px)Rayon large
--su-radius-xl0.75rem (12px)Rayon XL
--su-radius-full9999pxRayon complet (cercle)
Ombres
--su-shadow-xs0 1px 2px rgba(...)Ombre très légère
--su-shadow-sm0 1px 3px rgba(...)Ombre légère
--su-shadow-md0 4px 6px rgba(...)Ombre moyenne
--su-shadow-lg0 10px 15px rgba(...)Ombre forte
Animations
--su-duration-fast150ms (ou 0ms)Durée rapide
--su-duration-normal200ms (ou 0ms)Durée normale
--su-duration-slow300ms (ou 0ms)Durée lente
--su-ease-in-outcubic-bezier(...)Easing par défaut
--su-animation-scale1 (ou 0)Multiplicateur animation
Focus
--su-focus-ring-width2pxÉpaisseur focus ring
--su-focus-ring-offset2pxDécalage focus ring

Patterns de composition

Pattern 1 : Composant interactif complet

vue
<style scoped lang="scss">
@use '@/styles/core/mixins' as *;

.interactive-component {
  // Base
  display: flex;
  align-items: center;
  gap: var(--su-spacing-3);
  padding: var(--su-spacing-4);

  // Apparence
  background-color: var(--su-bg-surface);
  border: 2px solid var(--su-border-default);
  border-radius: var(--su-radius-lg);
  color: var(--su-text-primary);

  // Typographie
  font-family: var(--su-font-family-base);
  font-size: var(--su-font-size-base);
  line-height: var(--su-line-height-normal);

  // Comportement
  cursor: pointer;
  @include transition(background-color, border-color, transform, box-shadow);

  // États
  &:hover:not(:disabled) {
    background-color: var(--su-bg-hover);
    border-color: var(--su-border-strong);
    box-shadow: var(--su-shadow-md);
    transform: translateY(calc(-2px * var(--su-animation-scale)));
  }

  &:active:not(:disabled) {
    background-color: var(--su-bg-active);
    transform: translateY(0);
  }

  &:focus-visible {
    @include focus-ring;
  }

  &:disabled {
    background-color: var(--su-bg-disabled);
    border-color: var(--su-border-disabled);
    color: var(--su-text-disabled);
    cursor: not-allowed;
    opacity: 0.6;
  }
}
</style>

Pattern 2 : Section inversée

vue
<style scoped lang="scss">
.hero {
  background-color: var(--su-bg-inverse);
  color: var(--su-text-on-inverse);
  border-bottom: 1px solid var(--su-border-inverse);
  padding: var(--su-spacing-12) var(--su-spacing-8);

  &__card {
    background-color: var(--su-surface-inverse);
    border: 1px solid var(--su-border-inverse);
    border-radius: var(--su-radius-lg);
    padding: var(--su-spacing-6);
  }

  // Lien avec transparence via --su-link-default-rgb
  a {
    color: var(--su-link-default);
    background-color: rgb(var(--su-link-default-rgb) / 10%);
    padding: 2px 6px;
    border-radius: var(--su-radius-sm);

    &:hover {
      background-color: rgb(var(--su-link-default-rgb) / 20%);
    }
  }
}
</style>

Pattern 3 : Système de notification

vue
<style scoped lang="scss">
.notification {
  display: flex;
  align-items: flex-start;
  gap: var(--su-spacing-3);
  padding: var(--su-spacing-4);
  border-radius: var(--su-radius-md);
  border-left: 4px solid;
  box-shadow: var(--su-shadow-sm);

  &--success {
    background-color: var(--su-state-success-bg);
    border-left-color: var(--su-state-success);

    .notification__icon,
    .notification__title {
      color: var(--su-state-success);
    }
  }

  &--warning {
    background-color: var(--su-state-warning-bg);
    border-left-color: var(--su-state-warning);

    .notification__icon,
    .notification__title {
      color: var(--su-state-warning);
    }
  }

  &--error {
    background-color: var(--su-state-error-bg);
    border-left-color: var(--su-state-error);

    .notification__icon,
    .notification__title {
      color: var(--su-state-error);
    }
  }

  &--info {
    background-color: var(--su-state-info-bg);
    border-left-color: var(--su-state-info);

    .notification__icon,
    .notification__title {
      color: var(--su-state-info);
    }
  }
}

.notification__message {
  margin: 0;
  font-size: var(--su-font-size-sm);
  color: var(--su-text-secondary);
}
</style>

Migration depuis d'autres systèmes

Depuis Tailwind CSS

scss
// Tailwind → Design System

// Colors
.text-gray-900 → color: var(--su-text-primary)
.text-gray-600 → color: var(--su-text-secondary)
.text-gray-400 → color: var(--su-text-tertiary)
.bg-white → background-color: var(--su-bg-surface)
.bg-gray-50 → background-color: var(--su-bg-canvas)
.border-gray-300 → border-color: var(--su-border-default)

// Spacing
.p-4 → padding: var(--su-spacing-4)
.m-6 → margin: var(--su-spacing-6)
.gap-2 → gap: var(--su-spacing-2)

// Radius
.rounded → border-radius: var(--su-radius-sm)
.rounded-md → border-radius: var(--su-radius-md)
.rounded-lg → border-radius: var(--su-radius-lg)
.rounded-full → border-radius: var(--su-radius-full)

// Typography
.text-sm → font-size: var(--su-font-size-sm)
.text-base → font-size: var(--su-font-size-base)
.text-xl → font-size: var(--su-font-size-xl)

Depuis Bootstrap

scss
// Bootstrap → Design System

// Colors
.text-primary → color: var(--su-primary-default)
.text-secondary → color: var(--su-text-secondary)
.text-muted → color: var(--su-text-tertiary)
.bg-light → background-color: var(--su-bg-canvas)
.bg-white → background-color: var(--su-bg-surface)

// Components
.btn-primary → background-color: var(--su-primary-default)
.btn-secondary → background-color: var(--su-secondary-default)
.alert-success → utiliser tokens --su-state-success
.alert-danger → utiliser tokens --su-state-error

// Spacing
.p-3 → padding: var(--su-spacing-3)
.mb-4 → margin-bottom: var(--su-spacing-4)

Ressources

Publié sous licence MIT.