PD-228 — Plan d'implémentation
📚 Navigation User Story
| Document | | | ---------- | -- | | 📋 [Spécification](PD-228-specification.md) | | | 🛠️ **Plan d'implémentation** | *(ce document)* | | ✅ [Critères d'acceptation](PD-228-acceptability.md) | | | 📝 [Retour d'expérience](PD-228-rex.md) | | [← Retour à site-vitrine](../PD-225-epic.md) · [↑ Index User Story](index.md)
1. Découpage en composants
Structure Design System
src/
├── styles/
│ ├── tokens/
│ │ ├── colors.css # Palette de couleurs
│ │ ├── typography.css # Polices, tailles
│ │ ├── spacing.css # Marges, paddings
│ │ ├── borders.css # Radius, bordures
│ │ └── shadows.css # Ombres
│ ├── base/
│ │ ├── reset.css # CSS reset minimal
│ │ └── globals.css # Styles globaux
│ ├── components/
│ │ ├── buttons.css # Styles boutons
│ │ ├── cards.css # Styles cartes
│ │ ├── forms.css # Styles formulaires
│ │ └── navigation.css # Styles navigation
│ └── index.css # Import centralisé
└── components/
├── Button.astro
├── Card.astro
└── ...
Composants clés
| Composant | Responsabilité |
tokens/*.css | Variables CSS (source de vérité) |
base/reset.css | Normalisation cross-browser |
components/*.css | Styles des composants UI |
*.astro | Composants utilisant les tokens |
2. Flux techniques
Chargement CSS
BaseLayout.astro
│
└── import '../styles/index.css'
│
├── @import 'tokens/colors.css'
├── @import 'tokens/typography.css'
├── @import 'tokens/spacing.css'
├── @import 'base/reset.css'
└── @import 'components/*.css'
Utilisation dans composants
---
// Button.astro
interface Props {
variant?: 'primary' | 'secondary';
size?: 'sm' | 'md' | 'lg';
}
const { variant = 'primary', size = 'md' } = Astro.props;
---
<button class={`btn btn-${variant} btn-${size}`}>
<slot />
</button>
3. Mapping invariants → mécanismes
| Invariant | Mécanisme technique |
| INV-1 : Couleurs via tokens | Variables CSS dans :root |
| INV-2 : Espacements cohérents | Échelle de spacing prédéfinie |
Tokens couleurs
/* src/styles/tokens/colors.css */
:root {
/* Couleurs principales */
--color-bleu-profond: #0A2342;
--color-bleu-securite: #185ADB;
--color-bleu-clair: #E9F1FF;
/* Texte */
--color-graphite: #111827;
--color-gris-neutre: #4B5563;
--color-gris-clair: #D1D5DB;
/* Sémantiques */
--color-success: #10B981;
--color-warning: #F59E0B;
--color-error: #EF4444;
/* Alias */
--color-text-primary: var(--color-graphite);
--color-text-secondary: var(--color-gris-neutre);
--color-background: #FFFFFF;
--color-surface: var(--color-bleu-clair);
}
Tokens typographie
/* src/styles/tokens/typography.css */
:root {
/* Familles */
--font-family-sans: 'Inter', system-ui, sans-serif;
--font-family-display: 'Space Grotesk', var(--font-family-sans);
/* Tailles */
--font-size-xs: 0.75rem; /* 12px */
--font-size-sm: 0.875rem; /* 14px */
--font-size-base: 1rem; /* 16px */
--font-size-lg: 1.125rem; /* 18px */
--font-size-xl: 1.25rem; /* 20px */
--font-size-2xl: 1.5rem; /* 24px */
--font-size-3xl: 1.875rem; /* 30px */
--font-size-4xl: 2.25rem; /* 36px */
/* Poids */
--font-weight-normal: 400;
--font-weight-medium: 500;
--font-weight-semibold: 600;
--font-weight-bold: 700;
/* Line heights */
--line-height-tight: 1.2;
--line-height-normal: 1.5;
--line-height-relaxed: 1.6;
}
Tokens spacing
/* src/styles/tokens/spacing.css */
:root {
--space-xs: 0.5rem; /* 8px */
--space-sm: 1rem; /* 16px */
--space-md: 1.5rem; /* 24px */
--space-lg: 2rem; /* 32px */
--space-xl: 3rem; /* 48px */
--space-2xl: 4rem; /* 64px */
--space-3xl: 6rem; /* 96px */
}
Composant bouton
/* src/styles/components/buttons.css */
.btn {
display: inline-flex;
align-items: center;
justify-content: center;
min-height: 48px; /* WCAG touch target */
padding: var(--space-xs) var(--space-md);
font-family: var(--font-family-sans);
font-weight: var(--font-weight-semibold);
border-radius: var(--radius-md);
transition: var(--transition-base);
cursor: pointer;
border: none;
}
.btn-primary {
background: var(--color-bleu-securite);
color: white;
}
.btn-primary:hover {
background: #1248B5;
}
.btn-secondary {
background: transparent;
border: 2px solid var(--color-bleu-securite);
color: var(--color-bleu-securite);
}
4. Gestion des erreurs
| Erreur | Cause | Mitigation |
| Token non défini | Variable CSS manquante | Fallback explicite |
| Style inline | Développeur contourne | Lint CSS interdit inline |
| Couleur hardcodée | Hex direct dans code | Règle stylelint |
| Incohérence spacing | Valeur arbitraire | Règle stylelint |
Configuration Stylelint
{
"extends": ["stylelint-config-standard"],
"rules": {
"color-no-hex": true,
"declaration-property-value-disallowed-list": {
"margin": ["/^\\d+px$/"],
"padding": ["/^\\d+px$/"]
},
"selector-class-pattern": "^[a-z][a-z0-9-]*$"
}
}
5. Impacts sécurité
| Aspect | Mesure |
| CSS injection | Pas de CSS dynamique |
| @import externe | Interdit (lint) |
| Fonts externes | @fontsource local uniquement |
6. Hypothèses techniques
| Hypothèse | Justification |
| CSS custom properties supportées | Navigateurs modernes (>95% support) |
| Pas de CSS-in-JS | Astro préfère CSS natif |
| Polices Inter + Space Grotesk | Charte graphique existante |
| Pas de préprocesseur | CSS natif suffisant |
7. Points de vigilance
| Point | Risque | Action |
| Fallbacks | Variable non définie | Toujours définir fallback |
| Dark mode | Non prévu initialement | Préparer structure tokens |
| Print styles | Oubli impression | Ajouter @media print |
| Responsive | Breakpoints incohérents | Définir tokens breakpoints |
| Composants | Styles dupliqués | Factoriser dans CSS |
Fichiers à créer
| Fichier | Description |
src/styles/tokens/colors.css | Palette couleurs |
src/styles/tokens/typography.css | Typographie |
src/styles/tokens/spacing.css | Espacements |
src/styles/tokens/borders.css | Bordures, radius |
src/styles/tokens/shadows.css | Ombres |
src/styles/base/reset.css | CSS reset |
src/styles/components/buttons.css | Boutons |
src/styles/components/cards.css | Cartes |
src/styles/index.css | Point d'entrée |
.stylelintrc.json | Configuration lint |