Extend NDS patterns and migrate shared UI surfaces
All checks were successful
Deploy Sokko G / deploy (push) Successful in 17s

This commit is contained in:
Shinuwa 2026-09-02 15:50:04 +02:00
parent fef7735a1d
commit 9aa98c892d
41 changed files with 1035 additions and 405 deletions

View file

@ -17,14 +17,14 @@ export function AboutPage({ siteContent }) {
return (
<div className="about-page">
<section className="page-hero">
<section className="page-hero nds-page-hero">
<div>
<p className="eyebrow">{content.eyebrow}</p>
<h1>{content.title}</h1>
<p><RichText text={content.description} /></p>
</div>
</section>
<section className="about-story-section nebula-panel" aria-labelledby="about-story-title">
<section className="about-story-section nebula-panel nds-panel" aria-labelledby="about-story-title">
<div className="about-section-heading">
<p className="eyebrow">{content.sectionsEyebrow}</p>
<h2 id="about-story-title">{content.sectionsTitle}</h2>
@ -86,7 +86,7 @@ export function AboutPage({ siteContent }) {
)}
</section>
<HomeOnboardingSection content={siteContent.home.onboarding} className="about-onboarding-section" />
<section className="about-tools-section nebula-panel">
<section className="about-tools-section nebula-panel nds-panel">
<div className="page-heading">
<div>
<p className="eyebrow">Toolbox</p>
@ -115,7 +115,7 @@ export function AboutPage({ siteContent }) {
<a className="about-tools-library-button nds-button" data-size="md" data-variant="primary" href="#/library"><Icon name="book" />{content.toolsLibraryLink}</a>
</section>
{content.contribute && (
<section className="about-contribute-section nebula-panel" id="contribuer" aria-labelledby="about-contribute-title">
<section className="about-contribute-section nebula-panel nds-panel" id="contribuer" aria-labelledby="about-contribute-title">
<div className="about-section-heading">
<p className="eyebrow">{content.contribute.eyebrow}</p>
<h2 id="about-contribute-title">{content.contribute.title}</h2>

View file

@ -19,6 +19,10 @@ const VARIANT_VALUES = {
textarea: ["default", "ghost"],
swatch: ["color", "rainbow"],
event: ["default", "dark"],
surface: ["default", "interactive", "tool", "filter", "modal"],
badge: ["primary", "neutral", "gold", "success", "warning", "danger"],
feedback: ["info", "success", "warning", "danger"],
position: ["left", "right"],
none: ["default"]
};
@ -266,9 +270,185 @@ const COMPONENTS = [
}
];
const PATTERN_COMPONENTS = [
{
id: "nds-panel",
name: "Panel",
className: "nds-panel",
sizes: SIZE_VALUES.none,
variants: ["default", "interactive"],
attributes: { "data-variant": ["default", "interactive"] },
render: ({ attributes, variant }) => (
<article className="nds-panel design-system-pattern-card" {...attributes}>
<p className="eyebrow">{variant}</p>
<strong>Surface de lecture</strong>
<span>Fond sombre, bordure Nebula et accent dore contenu.</span>
</article>
)
},
{
id: "nds-card",
name: "Card",
className: "nds-card",
sizes: SIZE_VALUES.none,
variants: ["default", "interactive"],
attributes: { "data-variant": ["default", "interactive"] },
render: ({ attributes }) => (
<article className="nds-card design-system-pattern-card" {...attributes}>
<div className="nds-card-cover design-system-pattern-cover"><Icon name="toolbox" /></div>
<div className="nds-card-body">
<p className="eyebrow">Card</p>
<strong>Element repete</strong>
<div className="nds-card-actions"><button className="nds-button" data-size="xs" type="button">Ouvrir</button></div>
</div>
</article>
)
},
{
id: "nds-tool-panel",
name: "Tool Panel",
className: "nds-tool-panel",
sizes: SIZE_VALUES.none,
variants: ["tool", "interactive"],
attributes: { "data-variant": ["tool", "interactive"] },
render: ({ attributes }) => (
<section className="nds-tool-panel design-system-pattern-card" {...attributes}>
<header className="nds-section-heading"><strong>Outil</strong><button className="nds-icon-button" data-size="xs" type="button" aria-label="Ajouter"><Icon name="add" /></button></header>
<p>Surface de travail compacte pour les toolboxes.</p>
</section>
)
},
{
id: "nds-toolbar",
name: "Toolbar",
className: "nds-toolbar",
sizes: SIZE_VALUES.none,
variants: VARIANT_VALUES.none,
attributes: {},
render: () => (
<div className="nds-toolbar design-system-pattern-inline">
<button className="nds-button" data-size="xs" data-variant="primary" type="button">Ajouter</button>
<button className="nds-icon-button" data-size="xs" type="button" aria-label="Parametres"><Icon name="settings" /></button>
<button className="nds-icon-button" data-size="xs" data-variant="danger" type="button" aria-label="Supprimer"><Icon name="trash" /></button>
</div>
)
},
{
id: "nds-section-heading",
name: "Section Heading",
className: "nds-section-heading",
sizes: SIZE_VALUES.none,
variants: VARIANT_VALUES.none,
attributes: {},
render: () => (
<header className="nds-section-heading design-system-pattern-wide">
<div><p className="eyebrow">Heading</p><strong>Section</strong></div>
<button className="nds-button" data-size="xs" type="button">Action</button>
</header>
)
},
{
id: "nds-page-hero",
name: "Page Hero",
className: "nds-page-hero",
sizes: SIZE_VALUES.none,
variants: VARIANT_VALUES.none,
attributes: {},
render: () => (
<section className="nds-page-hero design-system-pattern-hero">
<div><p className="eyebrow">Hero</p><strong>Page concise</strong><span>Introduction courte et lisible.</span></div>
</section>
)
},
{
id: "nds-badge",
name: "Badge",
className: "nds-badge",
sizes: SIZE_VALUES.none,
variants: VARIANT_VALUES.badge,
attributes: { "data-variant": VARIANT_VALUES.badge },
render: ({ attributes, variant }) => <span className="nds-badge" {...attributes}>{variant}</span>
},
{
id: "nds-chip",
name: "Chip",
className: "nds-chip",
sizes: ["sm", "default"],
variants: VARIANT_VALUES.none,
attributes: { "data-size": ["sm", "default"], "data-state": STATE_VALUES },
render: ({ attributes, state }) => <button className="nds-chip" {...attributes} type="button" disabled={state === "disabled"} aria-pressed={state === "active"}><Icon name="checklist" />Filtre</button>
},
{
id: "nds-empty-state",
name: "Empty State",
className: "nds-empty-state",
sizes: SIZE_VALUES.none,
variants: VARIANT_VALUES.none,
attributes: {},
render: () => <div className="nds-empty-state design-system-pattern-card"><strong>Aucun contenu</strong><span>Message court et action optionnelle.</span></div>
},
{
id: "nds-inline-notice",
name: "Inline Notice",
className: "nds-inline-notice",
sizes: SIZE_VALUES.none,
variants: VARIANT_VALUES.feedback,
attributes: { "data-variant": VARIANT_VALUES.feedback },
render: ({ attributes, variant }) => <p className="nds-inline-notice" {...attributes}>{variant === "info" ? "Information compacte" : `${variant} compact`}</p>
},
{
id: "nds-progress",
name: "Progress",
className: "nds-progress",
sizes: SIZE_VALUES.none,
variants: ["primary", "success", "warning", "danger"],
attributes: { "data-variant": ["primary", "success", "warning", "danger"] },
render: ({ attributes, variant }) => <span className="nds-progress" {...attributes} aria-label={`Progression ${variant}`}><span style={{ width: variant === "danger" ? "86%" : "58%" }} /></span>
},
{
id: "nds-filter-panel",
name: "Filter Panel",
className: "nds-filter-panel",
sizes: SIZE_VALUES.none,
variants: VARIANT_VALUES.none,
attributes: {},
render: () => (
<aside className="nds-filter-panel design-system-pattern-card">
<header className="nds-section-heading"><strong>Filtres</strong><button className="nds-icon-button" data-size="xs" type="button" aria-label="Reset"><Icon name="rubber" /></button></header>
<button className="nds-chip" data-size="sm" type="button" aria-pressed="true">Actif</button>
</aside>
)
},
{
id: "nds-reorder-indicator",
name: "Reorder Indicator",
className: "nds-reorder-indicator",
sizes: SIZE_VALUES.none,
variants: VARIANT_VALUES.none,
configurable: { "data-axis": ["vertical", "horizontal"], "data-placement": ["before", "after"] },
attributes: { "data-axis": ["vertical", "horizontal"], "data-placement": ["before", "after"] },
render: ({ attributes }) => <div className="nds-reorder-indicator design-system-pattern-reorder" {...attributes}>Drop target</div>
},
{
id: "nds-overlays",
name: "Overlay Patterns",
className: "nds-modal / nds-drawer / nds-toast / nds-popover",
sizes: SIZE_VALUES.none,
variants: ["modal", "drawer", "toast", "popover"],
attributes: { "data-variant": ["modal", "drawer", "toast", "popover"] },
render: ({ variant }) => {
if (variant === "drawer") return <div className="nds-drawer-panel design-system-pattern-overlay">Drawer panel</div>;
if (variant === "toast") return <div className="nds-toast design-system-pattern-toast"><span>Alerte globale</span><button className="nds-icon-button" data-size="xs" type="button" aria-label="Fermer"><Icon name="close" /></button></div>;
if (variant === "popover") return <div className="nds-popover design-system-pattern-overlay">Menu flottant</div>;
return <div className="nds-modal design-system-pattern-overlay"><strong>Modale</strong><footer className="nds-modal-footer"><button className="nds-button" data-size="xs" type="button">Annuler</button></footer></div>;
}
}
];
const NAV_ITEMS = [
{ id: "foundations", label: "Foundations" },
...COMPONENTS.map(({ id, name }) => ({ id, label: name })),
...PATTERN_COMPONENTS.map(({ id, name }) => ({ id, label: name })),
{ id: "date-picker-buttons", label: "Date Picker Buttons" },
{ id: "specialized-components", label: "Specialized Components" },
{ id: "audit", label: "Audit" }
@ -629,6 +809,7 @@ export function DesignSystemPage() {
<main className="design-system-main">
<FoundationsSection />
{COMPONENTS.map((component) => <ComponentCatalogSection key={component.id} component={component} />)}
{PATTERN_COMPONENTS.map((component) => <ComponentCatalogSection key={component.id} component={component} />)}
<DatePickerButtonsSection />
<SpecializedComponentsSection />
<AuditSection />

View file

@ -14,7 +14,7 @@ export function HomePage({ siteContent, toolboxes, actions }) {
<p className="eyebrow">{content.hero.eyebrow}</p>
<h1>{content.hero.title}</h1>
<p>{content.hero.description}</p>
<div className="actions">
<div className="actions nds-toolbar">
<a className="nds-button" data-size="md" data-variant="primary" href="#/toolboxes">{content.hero.primaryAction}</a>
<a className="nds-button" data-size="md" href="#/games">{content.hero.secondaryAction}</a>
</div>
@ -25,7 +25,7 @@ export function HomePage({ siteContent, toolboxes, actions }) {
</div>
</section>
{showOnboarding && <HomeOnboardingSection content={content.onboarding} onCreateToolbox={() => actions.setCreateModal({ tutorial: true })} />}
<section className="origin-section nebula-panel" aria-labelledby="origin-title">
<section className="origin-section nebula-panel nds-panel" aria-labelledby="origin-title">
<div className="origin-copy">
<p className="eyebrow">{content.origin.eyebrow}</p>
<h2 id="origin-title">{content.origin.title}</h2>

View file

@ -212,14 +212,14 @@ export function LibraryPage({ siteContent, actions }) {
return (
<div className="library-page">
<section className="page-hero library-hero">
<section className="page-hero nds-page-hero library-hero">
<div>
<p className="eyebrow">{content.eyebrow}</p>
<h1>{content.title}</h1>
<p>{content.description}</p>
</div>
</section>
<section className="library-summary nebula-panel" aria-label={content.summaryLabel}>
<section className="library-summary nebula-panel nds-panel" aria-label={content.summaryLabel}>
{content.summaryItems.map((item) => (
<article key={item.title}>
<span className="library-summary-icon" aria-hidden="true">
@ -232,8 +232,8 @@ export function LibraryPage({ siteContent, actions }) {
</article>
))}
</section>
{libraryError && <section className="empty"><h2>Librairie indisponible</h2><p>{libraryError}</p></section>}
{!libraryError && !libraryPayload && <section className="empty"><p>Chargement des exemples...</p></section>}
{libraryError && <section className="empty nds-empty-state"><h2>Librairie indisponible</h2><p>{libraryError}</p></section>}
{!libraryError && !libraryPayload && <section className="empty nds-empty-state"><p>Chargement des exemples...</p></section>}
{libraryPayload && (
<>
<div className="library-doc-layout">
@ -288,7 +288,7 @@ export function LibraryPage({ siteContent, actions }) {
function LibraryDocNav({ content, categories, activeCategoryKey, onSelectCategory }) {
return (
<nav className="library-doc-nav nebula-panel" aria-label={content.tocLabel || "Sommaire des outils"}>
<nav className="library-doc-nav nebula-panel nds-panel" aria-label={content.tocLabel || "Sommaire des outils"}>
<div className="library-toc-heading">
<h2>{content.docTocTitle || content.tocTitle || "Sommaire"}</h2>
<p>{content.tocDescription || "Accédez directement à un exemple d’outil dans la page."}</p>
@ -338,7 +338,7 @@ function LibraryToolExample({ module, sourceData, description, categoryTitle, do
const title = module.title || definition.label || "Outil";
return (
<section id={getLibraryAnchorId(module)} className="library-tool-section">
<section id={getLibraryAnchorId(module)} className="library-tool-section nds-panel">
<div className="library-tool-intro">
<div>
<div className="library-tool-title-row">
@ -347,13 +347,13 @@ function LibraryToolExample({ module, sourceData, description, categoryTitle, do
</span>
<h2>{title}</h2>
</div>
<span className="library-tool-badge">{categoryTitle}</span>
<span className="library-tool-badge nds-badge">{categoryTitle}</span>
{description && <p>{description}</p>}
</div>
</div>
<div className={`library-example-layout ${Array.isArray(docs?.controls) && docs.controls.length ? "has-legend" : ""}`}>
<ToolControlLegend items={docs?.controls} heading={controlLegendHeading || "Repères"} />
<article className={`module library-tool-example ${scrollable ? "is-scrollable" : ""}`} data-module-type={module.type}>
<article className={`module nds-tool-panel library-tool-example ${scrollable ? "is-scrollable" : ""}`} data-module-type={module.type}>
<header>
<div>
<h2>{definition.label}</h2>
@ -393,7 +393,7 @@ function LibraryToolExample({ module, sourceData, description, categoryTitle, do
</div>
</header>
<div className={`module-content ${scrollable ? "is-scrollable legacy-scrollbar" : ""}`}>
<Suspense fallback={<div className="empty"><p>Chargement de l'outil...</p></div>}>
<Suspense fallback={<div className="empty nds-empty-state"><p>Chargement de l'outil...</p></div>}>
<Component
toolboxId={LIBRARY_TOOLBOX_ID}
moduleId={module.id}