Showcase Contract
The showcase is one registry-driven, endless page with a persistent navigation shell and 46 retained render modules:
flowchart LR Registry["registry.ts"] --> ModuleMap["modules.ts eager glob and integrity checks"] Registry --> Loader["+page.ts scroll-target resolver"] Registry --> Layout["+layout.svelte"] Layout --> Drawer["ComponentDrawer.svelte"] ModuleMap --> Page["+page.svelte endless renderer and scroll spy"] Loader --> Page Page --> Section["ComponentSection.svelte"] Section --> Modules["46 slug +page.svelte render modules"] Modules --> Shell["ComponentPage.svelte"] Page --> State["active-section.svelte.ts"] State --> Drawer State --> Layout Shell --> Demo["Demo.svelte"] DemoSource["Demo component source"] --> Raw["Vite ?raw import"] Raw --> Demo Demo --> Code["CodeBlock.svelte"]
Every Mermaid label containing . or + is quoted above; the diagram represents source relationships, not deployment state.
1. Registry authority
src/lib/showcase/registry.ts is the single source of truth for:
- route, section, and deep-link slug;
- display name;
- category and category order;
- one-line blurb;
- optional Bits UI namespace; and
- whether an entry is a utility.
bySlug supplies lookup, grouped() supplies ordered drawer groups, and docsUrl() maps every showcase entry to its local wiki guide at https://wiki.bits.loca.zone/components/<slug>. Registry order drives the persistent drawer order.
2. Route contract
/components is one endless scrollable page. It renders all 46 registry components top to bottom in registry order, regardless of the query string. src/lib/showcase/ComponentSection.svelte wraps each render module in <section id="<slug>" data-component-section="<slug>">.
src/routes/components/modules.ts owns the eager import.meta.glob('./*/+page.svelte', { eager: true }) map and exports componentSections in registry order. Its initialization checks reject both invalid states:
- an unregistered physical render module;
- a registered entry without a physical render module.
The 46 src/routes/components/<slug>/+page.svelte files remain render modules imported by the endless page. They are not canonical navigation targets.
?component=<slug> is a scroll target, not a content filter. src/routes/components/+page.ts returns only { slug }; missing, empty, and unknown values fall back to the first registry entry as that target without redirecting or returning an error. The other 45 components remain in the document for every target.
The scroll-target load boundary is:
export const load: PageLoad = ({ url }) => {
const requested = url.searchParams.get('component');
return { slug: requested && bySlug[requested] ? requested : components[0].slug };
};src/routes/components/+layout.ts applies a 307 compatibility redirect from a known legacy /components/<slug> path to /components?component=<slug>. Unknown legacy paths continue to SvelteKit’s 404 handling.
src/hooks.server.ts restricts preload hints to stylesheets and fonts. Without it the framework can advertise the immutable module graph in a link response header large enough to exceed an edge proxy’s header buffer. An eager page containing 46 module graphs makes this guard more important, not less: it prevents the oversized-Link-header 502 from returning.
3. Persistent drawer and scroll state
src/routes/components/+layout.svelte mounts ComponentDrawer exactly once. The page therefore has one component-search input and one global / shortcut listener.
ComponentDrawer.svelte:
- searches the lowercase concatenation of each entry’s name, blurb, and slug;
- preserves registry category order through
grouped(); - renders grouped headings with filtered result counts;
- keeps
/components?component=<slug>on every entry as the canonical, shareable deep link; - intercepts only an unmodified left click, scrolls to the matching section, and syncs the URL with SvelteKit shallow
replaceState, without navigation or a load re-run; - lets modified and non-left clicks fall through to normal browser behavior; and
- uses zero-delay, icon-led entry tooltips.
A single IntersectionObserver scroll spy in src/routes/components/+page.svelte writes the visible slug to activeSection.slug in src/lib/showcase/active-section.svelte.ts. The drawer reads this shared state for aria-current="page" and its visual highlight; the compact header reads the same state for its current-component label.
At lg and wider the drawer is a sticky, docked 16rem column. On compact widths the same mounted drawer becomes a fixed sheet below the h-14 global header; its toggle, close button, section-click callback, and Escape handler control visibility.
4. Shared component page
ComponentPage.svelte accepts slug, optional anatomy, optional notes, and child content. It uses bySlug for component metadata and renders the category, name, blurb, namespace import guidance, local-guide link, anatomy, demos, and notes.
The endless route owns the document’s single <title> and single h1. ComponentPage.svelte no longer has a <svelte:head> block because 46 stacked instances would compete for document metadata, and each per-component heading is an h2 beneath the page heading.
Pagination was removed. The drawer provides in-page component navigation, and the category chip is a non-interactive <span class="chip">.
5. Demo shell and source handoff
Demo.svelte accepts this interface:
type Props = {
title: string;
description?: string;
/** raw source of the demo component, imported with `?raw` */
src?: string;
/** extra vertical room for overlay-heavy demos */
tall?: boolean;
children: Snippet;
};A render module supplies component-specific demos and notes without duplicating the registry record. Each demo imports its component and a ?raw source string, then passes the raw string through src.
The source disclosure exists only when src is truthy. Demo.svelte passes that string to CodeBlock.svelte; importing a raw string without passing it to Demo is insufficient. tall adds vertical room without changing overlay behavior.
Invariants
- Every registry slug has exactly one retained
src/routes/components/<slug>/+page.svelterender module, and every such module resolves in the registry. /componentsrenders all 46 modules in registry order in one document, each inside<section id="<slug>" data-component-section="<slug>">.- The page has exactly one document
<title>and oneh1; every per-componentComponentPageheading is anh2. ?component=<slug>selects a scroll target only; it never removes the other 45 components from the document.- Missing, empty, and unknown targets fall back to
components[0]without rewriting the URL. - Known legacy slug paths use the 307 compatibility redirect; unknown legacy paths remain 404.
componentSectionscomes from the eager module map inmodules.ts, follows registry order, and rejects unregistered or missing physical modules.ComponentDraweris mounted once; unmodified left clicks scroll in-page and shallow-sync the URL, while modified and non-left clicks retain native browser behavior.- One page-level
IntersectionObserverowns active-section tracking; the drawer and compact header read the sharedactiveSection.slug. - Registry order, not filesystem discovery, controls section, drawer category, and drawer entry order.
- Pagination is absent, the drawer is the component navigation, and the category chip is non-interactive.
src/hooks.server.tsmust restrict preload hints to stylesheets and fonts so the eager module graph cannot recreate the oversized-Link-header edge failure.- A demo stays self-contained under
src/lib/demos/<slug>/. - Source reveal requires a
?rawimport passed throughsrc. - Component-specific, non-obvious API guidance belongs in the
notessnippet. - Primary guide URLs come from
docsUrl()and stay on the local wiki; upstream references are secondary links inside generated guides. - Generated live guide links use
https://bits.loca.zone/components?component=<slug>; known legacy links resolve through the compatibility redirect. bun run generate:component-guidesderives all 46 guides, their index, and the reference catalog from the registry; never hand-maintain a parallel component list.
loca.zone · bits.loca.zone · Bits UI · Vite