From 1468973f8144c93491d2a249d97c0d81120a68f1 Mon Sep 17 00:00:00 2001 From: Nathan Panchout Date: Mon, 26 Jan 2026 20:08:17 +0100 Subject: [PATCH] =?UTF-8?q?=F0=9F=93=9D(openspec)=20add=20field-variant-cl?= =?UTF-8?q?assic=20change=20artifacts?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add proposal, design, specs and tasks documentation - Add verification report - Archive completed change Co-Authored-By: Claude Opus 4.5 --- .gitignore | 4 + .../design.md | 167 ++++++++++++++++++ .../proposal.md | 118 +++++++++++++ .../specs/field-variant/spec.md | 100 +++++++++++ .../2026-01-26-field-variant-classic/tasks.md | 52 ++++++ .../verification.md | 130 ++++++++++++++ openspec/config.yaml | 20 +++ openspec/specs/field-variant/spec.md | 100 +++++++++++ 8 files changed, 691 insertions(+) create mode 100644 openspec/changes/archive/2026-01-26-field-variant-classic/design.md create mode 100644 openspec/changes/archive/2026-01-26-field-variant-classic/proposal.md create mode 100644 openspec/changes/archive/2026-01-26-field-variant-classic/specs/field-variant/spec.md create mode 100644 openspec/changes/archive/2026-01-26-field-variant-classic/tasks.md create mode 100644 openspec/changes/archive/2026-01-26-field-variant-classic/verification.md create mode 100644 openspec/config.yaml create mode 100644 openspec/specs/field-variant/spec.md diff --git a/.gitignore b/.gitignore index e0d6829..5809a98 100644 --- a/.gitignore +++ b/.gitignore @@ -36,3 +36,7 @@ ghpages-output # Project ignore files packages/react/chromatic.config.json packages/tokens/src/lib/cunningham.ts + +# Claude Code +CLAUDE.md +.claude* diff --git a/openspec/changes/archive/2026-01-26-field-variant-classic/design.md b/openspec/changes/archive/2026-01-26-field-variant-classic/design.md new file mode 100644 index 0000000..9b11164 --- /dev/null +++ b/openspec/changes/archive/2026-01-26-field-variant-classic/design.md @@ -0,0 +1,167 @@ +## Context + +Le design system Cunningham utilise actuellement un pattern "floating label" pour tous les champs de formulaire. Ce pattern est implémenté via le composant `LabelledBox` qui est utilisé par Input, TextArea, Select et DatePicker. + +Architecture actuelle : +``` +Field (wrapper: state, text, fullWidth) + └── LabelledBox (gère le label flottant) + └── Composant natif (input, textarea, etc.) +``` + +Le `LabelledBox` gère : +- Le positionnement du label (absolu, avec transition) +- L'état `labelAsPlaceholder` (label en grand = placeholder, label petit = au-dessus) +- Le CSS des transitions + +## Goals / Non-Goals + +**Goals:** +- Ajouter une variante `classic` avec label externe et placeholder natif +- Maintenir la rétrocompatibilité (floating = défaut) +- Supporter la variante classic sur tous les composants concernés +- Avoir un champ plus compact en mode classic + +**Non-Goals:** +- Changer le comportement par défaut des composants existants +- Créer des composants séparés (ClassicInput, etc.) +- Modifier Checkbox, Radio, Switch (déjà label externe) + +## Decisions + +### 1. Type `FieldVariant` partagé + +Créer un type exporté dans `packages/react/src/components/Forms/types.ts` : + +```typescript +export type FieldVariant = "floating" | "classic"; +``` + +**Pourquoi** : Assure la cohérence du typage entre tous les composants. + +### 2. Architecture en mode Classic + +En mode classic, **le label est rendu EN DEHORS du wrapper bordé**, pas à l'intérieur : + +``` +Field (wrapper) + └── label.c__input__label (EN DEHORS - nouveau) + └── div.c__input__wrapper (wrapper bordé) + └── Composant natif (input) +``` + +Vs mode floating (existant) : +``` +Field (wrapper) + └── div.c__input__wrapper (wrapper bordé) + └── LabelledBox (gère le label flottant) + └── Composant natif (input) +``` + +**Pourquoi** : Le label doit être visuellement au-dessus de la bordure du champ, pas à l'intérieur. + +### 3. CSS pour la variante classic + +Chaque composant a sa propre classe label et un modifier `--classic` sur le wrapper : + +```scss +// Label externe +.c__input__label { + display: block; + font-size: var(--c--components--forms-labelledbox--classic-label-font-size); + color: var(--c--components--forms-labelledbox--label-color--small); + margin-bottom: var(--c--components--forms-labelledbox--classic-label-margin-bottom); +} + +// Wrapper en mode classic +.c__input__wrapper--classic { + align-items: center; // Centrage vertical du contenu + height: 2.75rem; // Hauteur réduite (vs 3.5rem en floating) +} +``` + +Classes par composant : +- Input : `.c__input__label`, `.c__input__wrapper--classic` +- TextArea : `.c__textarea__label`, `.c__textarea__wrapper--classic` +- Select : `.c__select__label`, `.c__select--classic` +- DatePicker : `.c__date-picker__label`, `.c__date-picker--classic` + +### 4. Gestion du placeholder par composant + +Chaque composant gère son placeholder selon la variante : + +**Input / TextArea** : +```typescript +// Si variant="classic", passer placeholder au /