diff --git a/apps/web/src/components/ui/Card.tsx b/apps/web/src/components/ui/Card.tsx new file mode 100644 index 0000000..b826f6f --- /dev/null +++ b/apps/web/src/components/ui/Card.tsx @@ -0,0 +1,45 @@ +import { forwardRef } from "react"; +import { cva, type VariantProps } from "class-variance-authority"; +import { cn } from "@/lib/utils"; + +/** + * Card — surface standardisée. + * - `default` : bg-white, border line, ombre douce. Pour les formulaires. + * - `flat` : bg-cream-2, pas d'ombre. Pour les blocs informatifs. + * - `hero` : bg-white, ombre carte (plus marquée). Pour les éléments-héros. + * + * Le radius est 14px (--radius-card) — un peu plus rond que les boutons (6px). + * Cohérent avec la landing. + */ +const cardVariants = cva( + cn("rounded-card", "transition-shadow duration-150"), + { + variants: { + variant: { + default: "bg-white border border-line shadow-soft", + flat: "bg-cream-2", + hero: "bg-white border border-line shadow-card", + }, + padding: { + none: "p-0", + sm: "p-5", + md: "p-7", + lg: "p-9", + }, + }, + defaultVariants: { + variant: "default", + padding: "md", + }, + }, +); + +export type CardProps = React.HTMLAttributes & + VariantProps; + +export const Card = forwardRef( + ({ className, variant, padding, ...props }, ref) => ( +
+ ), +); +Card.displayName = "Card"; diff --git a/apps/web/src/components/ui/Chip.tsx b/apps/web/src/components/ui/Chip.tsx new file mode 100644 index 0000000..937543b --- /dev/null +++ b/apps/web/src/components/ui/Chip.tsx @@ -0,0 +1,46 @@ +import { forwardRef } from "react"; +import { Check } from "lucide-react"; +import { cn } from "@/lib/utils"; + +/** + * Chip — pastille sélectionnable. Utilisée pour : + * - les filtres de la liste de factures + * - les choix de volume mensuel à l'onboarding + * - les tonalités de relance dans l'éditeur de plan + * + * Forme légèrement allongée (radius 999px = pill), bordure 1px. + * Sélectionnée : fond rubis-glow + texte rubis-deep + ✓ — pas de plein rubis + * (on garde le rubis pour les CTA, cf. règle d'or de la marque). + */ +export type ChipProps = React.ButtonHTMLAttributes & { + selected?: boolean; + /** Affiche le ✓ quand sélectionné. Default true. */ + withCheck?: boolean; +}; + +export const Chip = forwardRef( + ({ className, selected = false, withCheck = true, children, type = "button", ...props }, ref) => { + return ( + + ); + }, +); +Chip.displayName = "Chip"; diff --git a/apps/web/src/components/ui/Stepper.tsx b/apps/web/src/components/ui/Stepper.tsx new file mode 100644 index 0000000..b62094d --- /dev/null +++ b/apps/web/src/components/ui/Stepper.tsx @@ -0,0 +1,84 @@ +import { Check } from "lucide-react"; +import { cn } from "@/lib/utils"; + +/** + * Stepper horizontal pour wizards multi-étapes (onboarding). + * - Étape courante : cercle plein rubis, label rubis + * - Étape complétée : cercle rubis-glow + ✓ rubis-deep + * - Étape future : cercle vide bordure line, label muted + * - Lignes entre les cercles : rubis si la précédente est complétée + * + * Pas une UI standard "1—2—3 cliquable". Les étapes ne sont pas navigables : + * c'est un wizard linéaire (cf. /docs/produit.md). + */ +type Step = { + /** Identifiant stable pour `key`. */ + id: string; + label: string; +}; + +type StepperProps = { + steps: ReadonlyArray; + /** Index 0-based de l'étape courante. */ + currentIndex: number; + className?: string; +}; + +export function Stepper({ steps, currentIndex, className }: StepperProps) { + return ( +
    + {steps.map((step, idx) => { + const status: "done" | "current" | "todo" = + idx < currentIndex ? "done" : idx === currentIndex ? "current" : "todo"; + const isLast = idx === steps.length - 1; + return ( +
  1. +
    + + {status === "done" ? ( + + + {step.label} + +
    + {!isLast && ( +
  2. + ); + })} +
+ ); +} diff --git a/apps/web/src/components/ui/Textarea.tsx b/apps/web/src/components/ui/Textarea.tsx new file mode 100644 index 0000000..4f1dcc8 --- /dev/null +++ b/apps/web/src/components/ui/Textarea.tsx @@ -0,0 +1,30 @@ +import { forwardRef } from "react"; +import { cn } from "@/lib/utils"; + +/** + * Textarea — même règles que Input (1px line, focus rubis-glow), avec + * un comportement `auto-resize` optionnel pour les signatures et notes. + */ +export type TextareaProps = React.TextareaHTMLAttributes; + +export const Textarea = forwardRef( + ({ className, rows = 4, ...props }, ref) => { + return ( +