
Quatre guides pour concevoir accessible
Déploiements plus rapides, langage UX partagé
Sept ateliers. Dans chacun : les réglages à gauche, la chose en vrai au milieu, et le code en dessous — en morceaux qui se copient séparément, parce qu'ils ne vont pas au même endroit du fichier. Rien à lire avant de commencer : on touche un réglage, le code se réécrit. Les deux derniers vont plus loin : la carte pose des composants dans un composant, et le formulaire en empile une suite qu'on ordonne.
Ce que tu vois n'est pas une image, c'est la chose elle-même. Si l'aperçu est juste, le code copié donnera exactement ça. La fiche complète — props, tokens, écarts avec Figma — reste sur Composants.
La règle d'abord : dans ce système, ce sont les balises HTML qui portent les styles. Un <h2> est le style « title » — il n'y a aucun CSS à écrire pour l'obtenir. Le CSS ci-dessous ne sert que lorsqu'aucune balise ne convient. Compose librement : si ta combinaison retombe sur une balise existante, l'atelier te le dira et te donnera la balise à la place.
Le texte
Concevoir, c’est rendre évident ce qui ne l’était pas.
1 Dans le bloc <style>, en bas du fichier
.mon-style {
font-family: var(--font-family-body);
font-weight: var(--font-weight-regular);
font-size: var(--font-size-body-lg);
line-height: var(--font-line-height-body);
letter-spacing: var(--font-letter-spacing-normal);
color: var(--color-text-default);
}Les six lignes sont écrites en entier, même celles qui ressemblent au réglage par défaut : posée sur un titre plutôt que sur un paragraphe, ta classe n'hériterait pas des mêmes valeurs.
2 Là où tu veux ce texte
<p class="mon-style">Concevoir, c’est rendre évident ce qui ne l’était pas.</p>La chose sur laquelle on clique.
Le composant
1 Tout en haut du fichier
---
import Button from 'ux-design-system/components/Button.astro';
---Si ton fichier a déjà un bloc ---, n'y ajoute que la ligne d'import.
2 Là où tu veux le bouton
<Button icon="mail-edit">Voir le projet</Button>Une étiquette qui classe. Elle ne se clique pas — une pilule cliquable est un Button.
Le composant
1 Tout en haut du fichier
---
import Tag from 'ux-design-system/components/Tag.astro';
---Si ton fichier a déjà un bloc ---, n'y ajoute que la ligne d'import.
2 Là où tu veux l'étiquette
<Tag>Recherche utilisateur</Tag>Une icône est un nom, pas un chemin. Le jeu complet est surIcônes.
Le composant
1 Tout en haut du fichier
---
import Icon from 'ux-design-system/components/Icon.astro';
---Si ton fichier a déjà un bloc ---, n'y ajoute que la ligne d'import.
2 Là où tu veux l'icône
<Icon name="mail-edit" size="md" />Une fiche : une image, trois lignes de texte, et trois emplacements qu'on remplit soi-même — deux rangées de tags, l'une au-dessus du titre et l'autre sous les informations secondaires, puis les actions. C'est le premier atelier où l'on met des composants dans un composant.
Le composant

Déploiements plus rapides, langage UX partagé

Déploiements plus rapides, langage UX partagé

Déploiements plus rapides, langage UX partagé

Déploiements plus rapides, langage UX partagé
1 Tout en haut du fichier
---
import Card from 'ux-design-system/components/Card.astro';
import Tag from 'ux-design-system/components/Tag.astro';
import Button from 'ux-design-system/components/Button.astro';
import { withBase } from 'ux-design-system/lib/url';
---Il y a plusieurs lignes parce qu'il y a plusieurs composants : la carte, et ce que tu as mis dedans. Elles disparaissent d'elles-mêmes quand un emplacement se vide.
2 Là où tu veux la carte
<Card
title="Quatre guides pour concevoir accessible"
subtitle="Déploiements plus rapides, langage UX partagé"
meta="2025 — atelier de six semaines"
image={{ src: withBase('/images/projet.jpg'), alt: 'Décris ce qu’on voit', width: 822, height: 382 }}
>
<Fragment slot="tags-top">
<Tag>Étude de cas</Tag>
</Fragment>
<Fragment slot="tags">
<Tag>Recherche utilisateur</Tag>
</Fragment>
<Fragment slot="actions">
<Button variant="secondary" size="sm" iconEnd="arrow-narrow-right">Lire l’étude</Button>
</Fragment>
</Card>Une boîte qui s'ouvre par-dessus la page. C'est le site qui l'ouvre, jamais cette page — voir Composants pour .showModal().
Le composant
Montrée ici avec l'attribut open, sans .showModal() — c'est pour ça qu'aucun fond noir ne la recouvre. En vrai c'est ::backdrop qui le pose, gratuitement, dès qu'un site l'ouvre par script.
1 Tout en haut du fichier
---
import Modal from 'ux-design-system/components/Modal.astro';
---Si ton fichier a déjà un bloc ---, n'y ajoute que la ligne d'import.
2 Là où tu veux la modale
<Modal
id="ma-modale"
icon="mail-edit"
heading="Envoyer le message ?"
body="Ta réponse partira à l’adresse du formulaire."
primary={{ label: "Envoyer" }}
secondary={{ label: "Annuler" }}
/>Le seul atelier où l'on ne règle pas un objet mais une suite : on empile des champs, on les ordonne, et le code sort en trois morceaux. Il ne fabrique pas le <form> autour — l'envoi, son bouton et la phrase des obligatoires ne sont pas des composants du système, et ils dépendent de ce que la page en fait.
Le formulaire
1 Tout en haut du fichier
---
import TextField from 'ux-design-system/components/TextField.astro';
import Select from 'ux-design-system/components/Select.astro';
---Une ligne par type de champ posé. Elle s'en va d'elle-même quand le dernier champ de ce type quitte la liste.
2 Là où tu veux les champs
<TextField id="ton-nom" label="Ton nom" required />
<Select
id="sujet"
label="Sujet"
options={['Devis', 'Question', 'Autre']}
/>Les champs, et rien autour : à toi d'écrire le <form>, son bouton d'envoi et la phrase « Les champs marqués * sont obligatoires » que l'astérisque oblige. Un groupe de radios ou de chips, lui, garde son <fieldset> : c'est ce qui porte le nom du groupe, et aucune prop ne le remplace.
3 Dans le style de la page
/* À poser sur ce qui entoure les champs — ton <form>, ou le <div>
qui les groupe. */
.mes-champs {
display: grid;
gap: var(--space-lg);
}
/* Un groupe de radios ou de chips. Le fieldset ne sert qu'à porter le
nom du groupe : son cadre natif s'efface, le système n'en dessine
aucun. */
.mes-champs fieldset {
margin: 0;
padding: 0;
border: none;
}
.mes-champs legend {
margin-bottom: var(--control-field-gap);
padding: 0;
font-family: var(--font-family-body);
font-size: var(--font-size-body);
font-weight: var(--font-weight-medium);
line-height: var(--font-line-height-tight);
color: var(--color-text-default);
}
.mes-choix {
display: flex;
flex-wrap: wrap;
gap: var(--space-sm) var(--space-lg);
}Le système n'a pas de composant « formulaire » : la mise en page revient à l'appelant, et c'est voulu — un token partage une valeur, il ne partage pas une structure. Sans cette règle, les champs se touchent.
Les cinq lignes de la legend sont d'un autre ordre : elles redessinent à la main la typographie de FieldLabel, parce qu'aucun composant ne dessine le nom d'un groupe. Une legend n'a pas de for — ce n'est pas un libellé de champ, et FieldLabel ne peut pas la rendre. C'est un manque du système, pas un choix.
Cinq pannes, dans l'ordre où on les rencontre. Chacune a un signe et un geste.
Le signe. Tu vois <Button> écrit tel quel dans la page, ou une erreur qui dit qu'il n'est pas défini.
Le geste. La ligne d'import manque. Elle va tout en haut du fichier,entre les deux lignes de trois tirets — ce bloc s'appelle le frontmatter, et c'est le seul endroit où un import a le droit d'être.
Le signe. Les textes s'empilent en Times New Roman, le bouton n'est qu'un rectangle gris.
Le geste. Les feuilles du système ne sont pas chargées. Si ta page passe par BaseLayout, elles arrivent toutes seules. Sinon, une fois pour tout le site :
import 'ux-design-system/styles/tokens.css';
import 'ux-design-system/styles/global.css';Dans cet ordre : le second se sert des variables du premier.
Le signe. Le build s'arrête, et l'erreur liste toutes les icônes disponibles.
Le geste. Le nom n'est pas dans le registre — souvent une faute de frappe ou un tiret oublié. Recopie-le depuis Icônes, ou depuis le menu déroulant de l'atelier ci-dessus. Pour en ajouter une, c'est un export Figma déposé dans icons/ puis npm run icons.
Le signe. Aucune erreur, aucun effet. C'est la panne la plus coûteuse, parce qu'elle est muette.
Le geste. npm run build ne vérifie pas les noms de props. C'est npm run check qui le fait, et qui te dira laquelle n'existe pas :
npm run checkÀ lancer avant de pousser, systématiquement.
Le signe. Aucune erreur, et les tags — ou les boutons — ont simplement disparu. La carte s'affiche, elle est juste vide à cet endroit.
Le geste. Vérifie que le contenu est bien enveloppé dans son Fragment. La carte a deux emplacements nommés : ce qui n'annonce pas son nom n'atterrit nulle part, et Astro le laisse tomber sans rien dire.
<!-- ne s’affiche pas : le Tag ne dit pas où il va -->
<Card title="…">
<Tag>Recherche utilisateur</Tag>
</Card>
<!-- s’affiche -->
<Card title="…">
<Fragment slot="tags">
<Tag>Recherche utilisateur</Tag>
</Fragment>
</Card>Le code de l'atelier Card l'écrit toujours correctement : en cas de doute, recopie-le plutôt que de l'écrire de mémoire.
Le signe. Rien à voir en développement, et l'image manque une fois le site publié.
Le geste. Le site est publié sous un préfixe. Tout chemin absolu — image, lien vers une page — passe par withBase() :
---
import { withBase } from 'ux-design-system/lib/url';
---
<img src={withBase('/images/avatar.png')} alt="" />Les icônes échappent à cette règle : inlinées, elles n'ont pas d'URL.