feat(design-system): reskin Charcoal — tokens dark-default + sidebar navy permanent
- Remplacement intégral index.css par palette Charcoal (DESIGN_SYSTEM.md v2.0)
- Dark = thème par défaut, .light = override via @custom-variant light
- Sidebar navy #0C1528 permanent (identique dark+light)
- Script anti-FOUC inline dans index.html
- Layout : radial-gradient sur <main>, sidebar 230px, max-w-[1100px]
- Renommage tokens Boréal→Charcoal sur ~45 composants
- Inversion dark: → baseline + light: sur primitives shadcn
- Fix logo blanc forcé dans sidebar
- ADR 006 mis à jour
Typecheck: OK · Tests: 122/122 ✅
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
407d1bd134
commit
b68f160bce
61 changed files with 1269 additions and 726 deletions
|
|
@ -19,19 +19,19 @@ const PLACEHOLDER_HEIGHTS = ['h-24', 'h-16', 'h-16', 'h-20'] as const
|
|||
|
||||
export function BlurredProgression({ onUpgrade }: Props) {
|
||||
return (
|
||||
<div className="relative min-h-[320px] overflow-hidden rounded-lg border border-line bg-canvas-2">
|
||||
<div className="relative min-h-[320px] overflow-hidden rounded-lg border border-border bg-surface">
|
||||
<div className="space-y-3 p-4 opacity-25 blur-sm" aria-hidden="true">
|
||||
{PLACEHOLDER_HEIGHTS.map((h, i) => (
|
||||
<div key={i} className={`${h} rounded bg-ink-4`} />
|
||||
<div key={i} className={`${h} rounded bg-surface-hover`} />
|
||||
))}
|
||||
</div>
|
||||
<div className="absolute inset-0 flex flex-col items-center justify-center gap-3 px-4 text-center">
|
||||
<Lock className="size-6 text-ink-4" aria-hidden="true" />
|
||||
<Lock className="size-6 text-ink-secondary" aria-hidden="true" />
|
||||
<div className="space-y-1">
|
||||
<p className="text-sm font-semibold text-ink-2">
|
||||
<p className="text-sm font-semibold text-ink-primary">
|
||||
Profil de préparation — Exclusivité Premium
|
||||
</p>
|
||||
<p className="max-w-sm text-xs text-ink-4">
|
||||
<p className="max-w-sm text-xs text-ink-secondary">
|
||||
Analysez vos erreurs récurrentes, recevez des exercices ciblés long terme, et suivez
|
||||
votre indice de préparation au TCF Canada.
|
||||
</p>
|
||||
|
|
|
|||
|
|
@ -23,10 +23,10 @@ export function NotReadyState({ current, minimum }: Props) {
|
|||
return (
|
||||
<Card variant="raised" className="space-y-4 p-6 text-center">
|
||||
<div className="space-y-1">
|
||||
<h2 className="text-lg font-semibold text-ink-1">Profil de préparation</h2>
|
||||
<p className="text-sm leading-relaxed text-ink-3">
|
||||
<h2 className="text-lg font-semibold text-ink-primary">Profil de préparation</h2>
|
||||
<p className="text-sm leading-relaxed text-ink-secondary">
|
||||
Vous avez réalisé{' '}
|
||||
<span className="font-semibold text-ink-1 tabular-nums">
|
||||
<span className="font-semibold text-ink-primary tabular-nums">
|
||||
{current}/{minimum}
|
||||
</span>{' '}
|
||||
simulations corrigées.{' '}
|
||||
|
|
@ -37,17 +37,14 @@ export function NotReadyState({ current, minimum }: Props) {
|
|||
</div>
|
||||
|
||||
<div
|
||||
className="relative h-2 overflow-hidden rounded-full bg-canvas-2"
|
||||
className="relative h-2 overflow-hidden rounded-full bg-surface"
|
||||
role="progressbar"
|
||||
aria-valuenow={current}
|
||||
aria-valuemin={0}
|
||||
aria-valuemax={minimum}
|
||||
aria-label={`Progression : ${current} sur ${minimum}`}
|
||||
>
|
||||
<div
|
||||
className="h-full bg-expria transition-all duration-300"
|
||||
style={{ width: `${pct}%` }}
|
||||
/>
|
||||
<div className="h-full bg-brand transition-all duration-300" style={{ width: `${pct}%` }} />
|
||||
</div>
|
||||
|
||||
<div className="flex justify-center">
|
||||
|
|
|
|||
|
|
@ -35,10 +35,12 @@ export function PatternExerciceCard({ exercice }: Props) {
|
|||
<div className="space-y-2">
|
||||
<div className="flex flex-wrap items-center gap-2">
|
||||
<Badge variant="neutral">{critereLabel}</Badge>
|
||||
<span className="text-xs font-medium text-ink-4">{exercice.code.replace(/_/g, ' ')}</span>
|
||||
<span className="text-xs font-medium text-ink-secondary">
|
||||
{exercice.code.replace(/_/g, ' ')}
|
||||
</span>
|
||||
</div>
|
||||
{exercice.diagnostic && (
|
||||
<p className="text-sm leading-relaxed text-ink-2">
|
||||
<p className="text-sm leading-relaxed text-ink-primary">
|
||||
<ReactMarkdown
|
||||
disallowedElements={['script', 'iframe']}
|
||||
components={{ p: ({ children }) => <span>{children}</span> }}
|
||||
|
|
@ -50,36 +52,38 @@ export function PatternExerciceCard({ exercice }: Props) {
|
|||
</div>
|
||||
|
||||
{exercice.exercice.consigne && (
|
||||
<div className="space-y-1.5 rounded-md border border-line bg-canvas-2 p-3">
|
||||
<p className="text-[11px] font-semibold uppercase tracking-widest text-ink-5">Consigne</p>
|
||||
<p className="text-sm leading-relaxed text-ink-1">{exercice.exercice.consigne}</p>
|
||||
<div className="space-y-1.5 rounded-md border border-border bg-surface p-3">
|
||||
<p className="text-[11px] font-semibold uppercase tracking-widest text-ink-tertiary">
|
||||
Consigne
|
||||
</p>
|
||||
<p className="text-sm leading-relaxed text-ink-primary">{exercice.exercice.consigne}</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="grid gap-2 sm:grid-cols-2">
|
||||
<div className="space-y-1.5 rounded-md border border-danger/30 bg-danger-bg p-3">
|
||||
<div className="space-y-1.5 rounded-md border border-danger/30 bg-danger-soft p-3">
|
||||
<p className="text-[11px] font-semibold uppercase tracking-widest text-danger">
|
||||
Incorrect
|
||||
</p>
|
||||
<p className="text-sm leading-relaxed text-ink-1 line-through decoration-danger decoration-1">
|
||||
<p className="text-sm leading-relaxed text-ink-primary line-through decoration-danger decoration-1">
|
||||
{exercice.exercice.exemple}
|
||||
</p>
|
||||
</div>
|
||||
<div className="space-y-1.5 rounded-md border border-success/30 bg-success-bg p-3">
|
||||
<div className="space-y-1.5 rounded-md border border-success/30 bg-success-soft p-3">
|
||||
<p className="text-[11px] font-semibold uppercase tracking-widest text-success">
|
||||
Correct
|
||||
</p>
|
||||
<p className="text-sm leading-relaxed text-ink-1">{exercice.exercice.correction}</p>
|
||||
<p className="text-sm leading-relaxed text-ink-primary">{exercice.exercice.correction}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex gap-3 rounded-md border border-warning/30 bg-warning-bg p-3">
|
||||
<div className="flex gap-3 rounded-md border border-warning/30 bg-warning-soft p-3">
|
||||
<Lightbulb className="mt-0.5 size-4 shrink-0 text-warning" aria-hidden="true" />
|
||||
<div className="space-y-1">
|
||||
<p className="text-[11px] font-semibold uppercase tracking-widest text-warning">
|
||||
Astuce de relecture
|
||||
</p>
|
||||
<p className="text-sm leading-relaxed text-ink-1">{exercice.exercice.astuce}</p>
|
||||
<p className="text-sm leading-relaxed text-ink-primary">{exercice.exercice.astuce}</p>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ export function PatternsList({ patterns }: Props) {
|
|||
if (patterns.length === 0) {
|
||||
return (
|
||||
<Card variant="default" className="p-4">
|
||||
<p className="text-sm text-ink-3">
|
||||
<p className="text-sm text-ink-secondary">
|
||||
Aucune erreur récurrente détectée sur vos 5 dernières productions. Continuez ainsi !
|
||||
</p>
|
||||
</Card>
|
||||
|
|
@ -37,10 +37,10 @@ export function PatternsList({ patterns }: Props) {
|
|||
<li key={`${p.critere}-${p.code}-${p.description ?? ''}`}>
|
||||
<Card variant="default" className="flex items-start justify-between gap-3 p-4">
|
||||
<div className="min-w-0 space-y-1">
|
||||
<p className="text-sm font-semibold text-ink-1">
|
||||
<p className="text-sm font-semibold text-ink-primary">
|
||||
{p.description ?? humanizeCode(p.code)}
|
||||
</p>
|
||||
<p className="text-xs text-ink-4">{CRITERE_LABELS[p.critere]}</p>
|
||||
<p className="text-xs text-ink-secondary">{CRITERE_LABELS[p.critere]}</p>
|
||||
</div>
|
||||
<Badge variant="nclc" className="shrink-0 tabular-nums">
|
||||
{p.frequency}/5
|
||||
|
|
|
|||
|
|
@ -29,19 +29,19 @@ export function PreparationIndexHero({ index }: Props) {
|
|||
<Card variant="raised" className="space-y-4 p-6">
|
||||
<div className="flex flex-wrap items-baseline justify-between gap-4">
|
||||
<div>
|
||||
<p className="text-[11px] font-semibold uppercase tracking-widest text-ink-5">
|
||||
<p className="text-[11px] font-semibold uppercase tracking-widest text-ink-tertiary">
|
||||
Indice de préparation
|
||||
</p>
|
||||
<p className="mt-1 tabular-nums text-ink-1">
|
||||
<p className="mt-1 tabular-nums text-ink-primary">
|
||||
<span className="text-5xl font-bold">{index.score}</span>
|
||||
<span className="text-2xl font-medium text-ink-4">/100</span>
|
||||
<span className="text-2xl font-medium text-ink-secondary">/100</span>
|
||||
</p>
|
||||
</div>
|
||||
<p className="max-w-xs text-sm leading-relaxed text-ink-2">{index.message}</p>
|
||||
<p className="max-w-xs text-sm leading-relaxed text-ink-primary">{index.message}</p>
|
||||
</div>
|
||||
|
||||
<div
|
||||
className="relative h-2 overflow-hidden rounded-full bg-canvas-2"
|
||||
className="relative h-2 overflow-hidden rounded-full bg-surface"
|
||||
role="progressbar"
|
||||
aria-valuenow={pct}
|
||||
aria-valuemin={0}
|
||||
|
|
@ -53,7 +53,7 @@ export function PreparationIndexHero({ index }: Props) {
|
|||
style={{ width: `${pct}%` }}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex justify-between text-xs text-ink-4 tabular-nums">
|
||||
<div className="flex justify-between text-xs text-ink-secondary tabular-nums">
|
||||
<span>0</span>
|
||||
<span>40</span>
|
||||
<span>70</span>
|
||||
|
|
|
|||
|
|
@ -31,13 +31,13 @@ export function ProgressionPremium({ data }: Props) {
|
|||
<PreparationIndexHero index={data.preparation_index} />
|
||||
|
||||
<section aria-label="Erreurs récurrentes">
|
||||
<h2 className="mb-3 text-base font-semibold text-ink-1">Erreurs récurrentes</h2>
|
||||
<h2 className="mb-3 text-base font-semibold text-ink-primary">Erreurs récurrentes</h2>
|
||||
<PatternsList patterns={data.patterns} />
|
||||
</section>
|
||||
|
||||
{data.exercises.length > 0 && (
|
||||
<section aria-label="Exercices long terme">
|
||||
<h2 className="mb-3 text-base font-semibold text-ink-1">Exercices long terme</h2>
|
||||
<h2 className="mb-3 text-base font-semibold text-ink-primary">Exercices long terme</h2>
|
||||
<div className="space-y-3">
|
||||
{data.exercises.map((ex, i) => (
|
||||
<PatternExerciceCard key={`${ex.code}-${i}`} exercice={ex} />
|
||||
|
|
@ -47,7 +47,7 @@ export function ProgressionPremium({ data }: Props) {
|
|||
)}
|
||||
|
||||
<Card variant="default" className="p-3">
|
||||
<p className="text-center text-xs text-ink-4">
|
||||
<p className="text-center text-xs text-ink-secondary">
|
||||
Analyse basée sur vos {data.analyzed_productions} dernières productions —{' '}
|
||||
{formatRelativeDate(data.last_analysis)}
|
||||
</p>
|
||||
|
|
|
|||
|
|
@ -21,9 +21,9 @@ import { ProgressionPremium } from '../components/ProgressionPremium'
|
|||
function Skeleton() {
|
||||
return (
|
||||
<div className="space-y-4" aria-busy="true" aria-label="Chargement de votre profil…">
|
||||
<div className="h-32 animate-pulse rounded-lg bg-canvas-2" />
|
||||
<div className="h-24 animate-pulse rounded-lg bg-canvas-2" />
|
||||
<div className="h-48 animate-pulse rounded-lg bg-canvas-2" />
|
||||
<div className="h-32 animate-pulse rounded-lg bg-surface" />
|
||||
<div className="h-24 animate-pulse rounded-lg bg-surface" />
|
||||
<div className="h-48 animate-pulse rounded-lg bg-surface" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
@ -38,8 +38,8 @@ export function ProgressionPage() {
|
|||
return (
|
||||
<main className="mx-auto max-w-3xl space-y-6 px-4 py-6">
|
||||
<header className="space-y-1">
|
||||
<h1 className="text-lg font-semibold text-ink-1">Profil de préparation</h1>
|
||||
<p className="text-sm text-ink-3">
|
||||
<h1 className="text-lg font-semibold text-ink-primary">Profil de préparation</h1>
|
||||
<p className="text-sm text-ink-secondary">
|
||||
Repérez vos erreurs récurrentes et travaillez-les avec des exercices ciblés.
|
||||
</p>
|
||||
</header>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue