diff --git a/web/routes/blocks/+layout@.svelte b/web/routes/blocks/+layout@.svelte index 21c344d30..8024ef7e8 100644 --- a/web/routes/blocks/+layout@.svelte +++ b/web/routes/blocks/+layout@.svelte @@ -1,30 +1,68 @@
diff --git a/web/routes/blocks/_lib/catalog.ts b/web/routes/blocks/_lib/catalog.ts new file mode 100644 index 000000000..84540d668 --- /dev/null +++ b/web/routes/blocks/_lib/catalog.ts @@ -0,0 +1,61 @@ +/** + * The blocks catalog — one source for the section rail and the gallery index, + * so a block that lands does not have to be remembered in two places. + * Mirrors `docs/process/PLAN-blocks.md` (F2–F4). + */ +export interface BlockEntry { + slug: string; + label: string; + /** A live demo exists at `/blocks/{slug}`. */ + shipped: boolean; +} + +export interface BlockGroup { + title: string; + items: BlockEntry[]; +} + +export const BLOCK_CATALOG: BlockGroup[] = [ + { + title: 'Site', + items: [ + { slug: 'site-header', label: 'Site header', shipped: true }, + { slug: 'hero', label: 'Hero', shipped: false }, + { slug: 'feature-grid', label: 'Feature grid', shipped: false }, + { slug: 'pricing', label: 'Pricing', shipped: false }, + { slug: 'testimonials', label: 'Testimonials', shipped: false }, + { slug: 'faq', label: 'FAQ', shipped: false }, + { slug: 'stats-band', label: 'Stats band', shipped: false }, + { slug: 'cta', label: 'CTA', shipped: false }, + { slug: 'newsletter', label: 'Newsletter', shipped: false }, + { slug: 'site-footer', label: 'Site footer', shipped: false }, + { slug: 'banner', label: 'Banner', shipped: false }, + { slug: 'team', label: 'Team', shipped: false }, + { slug: 'contact', label: 'Contact', shipped: false }, + { slug: 'content-section', label: 'Content section', shipped: false } + ] + }, + { + title: 'App', + items: [ + { slug: 'app-shell', label: 'App shell', shipped: false }, + { slug: 'auth', label: 'Auth', shipped: false }, + { slug: 'data-table', label: 'Data table', shipped: false }, + { slug: 'dashboard', label: 'Dashboard', shipped: false }, + { slug: 'settings', label: 'Settings', shipped: false }, + { slug: 'user-menu', label: 'User menu', shipped: false }, + { slug: 'notifications', label: 'Notifications', shipped: false }, + { slug: 'wizard', label: 'Wizard', shipped: false }, + { slug: 'error-page', label: 'Error page', shipped: false }, + { slug: 'kanban', label: 'Kanban', shipped: false } + ] + }, + { + title: 'Docs', + items: [ + { slug: 'docs-shell', label: 'Docs shell', shipped: false }, + { slug: 'code-showcase', label: 'Code showcase', shipped: false }, + { slug: 'props-table', label: 'Props table', shipped: false } + ] + } +]; diff --git a/web/routes/blocks/blocks.css b/web/routes/blocks/blocks.css new file mode 100644 index 000000000..37343f33d --- /dev/null +++ b/web/routes/blocks/blocks.css @@ -0,0 +1,63 @@ +/* + * Chrome of the /blocks section — the shell only: topbar, rail and content + * column. The CONTROLS inside it are canon components (ToggleGroup, Button, + * Link…), like the rest of the site; this file just places the three regions + * and paints their edges with foundation tokens. + * + * (The B contract's «no CSS» rule is about `src/uix/blocks/`, the tier itself. + * This is app-land chrome for the demo site, the same role `uix.css` plays for + * the component docs.) + */ + +[data-blocks-shell] { + min-block-size: 100dvh; + background: var(--color-surface-default); + color: var(--color-content-primary); +} + +[data-blocks-topbar] { + position: sticky; + inset-block-start: 0; + z-index: var(--z-index-raised); + display: flex; + align-items: center; + justify-content: space-between; + gap: var(--space-4); + padding-inline: var(--space-4); + padding-block: var(--space-2); + background: color-mix(in srgb, var(--color-surface-default) 88%, transparent); + backdrop-filter: blur(8px); + border-block-end: var(--border-width) solid var(--color-border-subtle); +} + +[data-blocks-body] { + display: flex; + align-items: stretch; + min-block-size: 0; +} + +[data-blocks-rail] { + flex: none; + inline-size: 15rem; + padding: var(--space-4); + border-inline-end: var(--border-width) solid var(--color-border-subtle); + position: sticky; + inset-block-start: var(--space-12); + align-self: start; + max-block-size: calc(100dvh - var(--space-12)); + overflow-y: auto; + overscroll-behavior: contain; +} + +/* Below `lg` the rail folds away: the topbar keeps the axes, and the gallery + is one tap away. A demo section does not need a drawer of its own. */ +@media (width < 64rem) { + [data-blocks-rail] { + display: none; + } +} + +[data-blocks-content] { + flex: 1 1 auto; + min-inline-size: 0; +}