import { mkdirSync, readFileSync, readdirSync, unlinkSync, writeFileSync } from "node:fs";
import { dirname, join } from "node:path";
import process from "node:process";
import { fileURLToPath } from "node:url";
const exampleRoot = join(dirname(fileURLToPath(import.meta.url)), "..");
const workspaceRoot = join(exampleRoot, "..", "..");
const referencePath = join(workspaceRoot, "packages", "ui", "component-reference.json");
const uiComponentsPath = join(workspaceRoot, "packages", "ui", "components");
const pagesDir = join(exampleRoot, "app", "pages");
const detailPagesDir = join(pagesDir, "components");
const layoutsDir = join(exampleRoot, "app", "layouts");
const reference = JSON.parse(readFileSync(referencePath, "utf8"));
function writeIfChanged(path, contents) {
try {
if (readFileSync(path, "utf8") === contents) return;
} catch {
// The generated file does not exist yet.
}
writeFileSync(path, contents, "utf8");
}
const displayName = (name) => name.replace(/([a-z0-9])([A-Z])/g, "$1 $2");
const baseSlugOf = (name) => name.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase();
const slugCounts = new Map();
for (const component of reference.components) {
const slug = baseSlugOf(component.name);
slugCounts.set(slug, (slugCounts.get(slug) ?? 0) + 1);
}
const slugOf = (name) => {
const slug = baseSlugOf(name);
if (slugCounts.get(slug) === 1) return slug;
const component = reference.components.find((entry) => entry.name === name);
return `${slug}-${component?.category ?? "component"}`;
};
const titleCase = (value) =>
value
.split("-")
.map((word) => word[0].toUpperCase() + word.slice(1))
.join(" ");
const identifier = (value) =>
value.replace(/(^|[^A-Za-z0-9_])([A-Za-z0-9])/g, (_, _sep, char) => char.toUpperCase());
const escapeText = (value) =>
String(value).replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">");
// WRN uses braces for reactive expressions. Encode braces inside documentation
// code so hydration keeps them as literal, copyable source text.
const escapeCode = (value) => escapeText(value).replaceAll("{", "{").replaceAll("}", "}");
const escapeAttribute = (value) =>
escapeText(value).replaceAll('"', """).replaceAll("'", "'");
const jsonAttribute = (value) => escapeAttribute(JSON.stringify(value));
function sampleItem(component, index, variation) {
const number = index + 1;
const labels = ["Primary workflow", "Secondary workflow", "Advanced workflow"];
return {
id: `${slugOf(component.name)}-${variation}-${number}`,
key: `${slugOf(component.name)}-${variation}-${number}`,
value: index === 0 ? "primary" : "secondary",
label: index === 0 ? labels[variation] : "Additional option",
title: index === 0 ? labels[variation] : "Supporting example",
description:
variation === 0
? "A clean default configuration for everyday product interfaces."
: variation === 1
? "A compact configuration designed for dense application layouts."
: "A richer configuration with more supporting information and actions.",
text: index === 0 ? "A configurable sample message." : "Another configurable message.",
href: `#${slugOf(component.name)}-demo`,
actionHref: `#${slugOf(component.name)}-demo`,
actionLabel: variation === 2 ? "Explore workflow" : "View details",
icon:
variation === 0
? "icon-[lucide--sparkles]"
: variation === 1
? "icon-[lucide--zap]"
: "icon-[lucide--circle-check]",
iconClass:
variation === 0
? "icon-[lucide--sparkles]"
: variation === 1
? "icon-[lucide--zap]"
: "icon-[lucide--circle-check]",
variant: variation === 0 ? "primary" : variation === 1 ? "secondary" : "success",
status: variation === 2 ? "Complete" : "Active",
time: variation === 0 ? "09:30" : variation === 1 ? "10:15" : "11:45",
current: index === 0,
selected: index === 0,
checked: index === 0,
disabled: variation === 2 && index === 1,
outgoing: index === 1,
open: true,
number,
count: number * (variation + 2) * 8,
percentage: variation === 0 ? 72 : variation === 1 ? 48 : 91,
color: variation === 0 ? "#7c3aed" : variation === 1 ? "#0284c7" : "#059669",
target: "_self",
ariaLabel: `Open ${labels[variation].toLowerCase()} ${number}`,
items: [
{ label: "Nested option A", value: "nested-a" },
{ label: "Nested option B", value: "nested-b" },
],
links: [
{ label: "Documentation", href: "#documentation" },
{ label: "API reference", href: "#api-reference" },
],
values: ["Included", variation === 2 ? "Unlimited" : "Standard"],
};
}
function sampleArray(prop, component, variation) {
const name = prop.name.toLowerCase();
if (name.includes("chartlabel") || name === "labels")
return variation === 1 ? ["Q1", "Q2", "Q3", "Q4"] : ["Mon", "Tue", "Wed", "Thu", "Fri"];
if (name === "data" || name.includes("series"))
return variation === 0
? [18, 32, 27, 48, 64]
: variation === 1
? [64, 51, 43, 35]
: [24, 46, 72, 88];
if (name === "pages")
return [
{ number: 1, href: "#page-1", current: variation === 0, ellipsis: false },
{ number: 2, href: "#page-2", current: variation !== 0, ellipsis: false },
{ number: 3, href: "#page-3", current: false, ellipsis: false },
];
if (name === "plans")
return [
{ name: "Starter", label: "Starter", value: "starter" },
{ name: "Pro", label: "Pro", value: "pro" },
{ name: "Scale", label: "Scale", value: "scale" },
];
if (name === "features")
return [
{ label: "Team members", values: ["5", "Unlimited", "Unlimited"] },
{ label: "Support", values: ["Email", "Priority", "Dedicated"] },
{ label: "Audit history", values: ["7 days", "90 days", "Custom"] },
];
if (name === "logos")
return ["Acme", "Northstar", "Vertex", "Orbit"].map((logo, index) => ({
name: logo,
alt: `${logo} logo`,
href: `#${slugOf(component.name)}-demo`,
external: false,
src: `data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 120 36'%3E%3Crect width='120' height='36' rx='8' fill='%23${variation === 2 ? "059669" : variation === 1 ? "0284c7" : "7c3aed"}'/%3E%3Ctext x='60' y='23' text-anchor='middle' font-family='Arial' font-size='13' fill='white'%3E${logo}%3C/text%3E%3C/svg%3E`,
order: index,
}));
if (name.includes("navigationcolumns"))
return [
{
title: "Product",
links: [
{ label: "Overview", href: "#overview" },
{ label: "Pricing", href: "#pricing" },
],
},
{
title: "Resources",
links: [
{ label: "Guides", href: "#guides" },
{ label: "Support", href: "#support" },
],
},
];
if (name === "sections")
return [
{
label: "Product",
value: "product",
description: "Product navigation examples.",
items: [
{ label: "Platform", href: "#platform", description: "Explore the platform." },
{ label: "Security", href: "#security", description: "Review security controls." },
],
},
{
label: "Developers",
value: "developers",
description: "Developer resources.",
items: [{ label: "Documentation", href: "#docs", description: "Read the guides." }],
},
];
if (name === "toasts")
return [
{
title: variation === 2 ? "Deployment complete" : "Changes saved",
description:
variation === 2
? "The production release completed successfully."
: "Your settings were saved successfully.",
variant: "success",
icon: "icon-[lucide--circle-check]",
open: true,
dismissible: true,
dismissLabel: "Dismiss notification",
},
];
if (name === "stages")
return [
{
title: "Event received",
description: "A customer event started the flow.",
icon: "icon-[lucide--webhook]",
},
{
title: "Delivery",
description: "Eligible channels are selected.",
icon: "icon-[lucide--send]",
items: [{ label: "Email" }, { label: "SMS" }],
},
];
return [sampleItem(component, 0, variation), sampleItem(component, 1, variation)];
}
function defaultValue(prop) {
if (prop.default === null) return undefined;
if (prop.default === "true") return true;
if (prop.default === "false") return false;
if (/^-?\d+(?:\.\d+)?$/.test(prop.default ?? "")) return Number(prop.default);
if (/^".*"$/.test(prop.default ?? "")) {
try {
return JSON.parse(prop.default);
} catch {
return undefined;
}
}
return undefined;
}
function sampleValue(prop, component, variation) {
const name = prop.name.toLowerCase();
if (component.name === "FileUploadProgress") {
const configuration = fileUploadProgressConfiguration(variation);
if (Object.hasOwn(configuration, prop.name)) return configuration[prop.name];
}
if (basicFormComponentNames.has(component.name)) {
const configuration = basicFormConfiguration(component.name, variation);
if (Object.hasOwn(configuration, prop.name)) return configuration[prop.name];
return undefined;
}
if (component.name === "Accordion") {
const configuration = accordionConfiguration(variation);
if (Object.hasOwn(configuration, prop.name)) return configuration[prop.name];
return undefined;
}
if (component.name === "Alert") {
const configuration = alertConfiguration(variation);
if (Object.hasOwn(configuration, prop.name)) return configuration[prop.name];
return undefined;
}
if (component.name === "Avatar") {
const configuration = avatarConfiguration(variation);
if (Object.hasOwn(configuration, prop.name)) return configuration[prop.name];
return undefined;
}
if (component.name === "AvatarGroup") {
const configuration = avatarGroupConfiguration(variation);
if (Object.hasOwn(configuration, prop.name)) return configuration[prop.name];
return undefined;
}
if (component.name === "Badge") {
const configuration = badgeConfiguration(variation);
if (Object.hasOwn(configuration, prop.name)) return configuration[prop.name];
return undefined;
}
if (component.name === "Blockquote") {
const configuration = blockquoteConfiguration(variation);
if (Object.hasOwn(configuration, prop.name)) return configuration[prop.name];
return undefined;
}
if (component.name === "ButtonGroup") {
const configuration = buttonGroupConfiguration(variation);
if (Object.hasOwn(configuration, prop.name)) return configuration[prop.name];
return undefined;
}
if (component.name === "Card") {
const configuration = cardConfiguration(variation);
if (Object.hasOwn(configuration, prop.name)) return configuration[prop.name];
return undefined;
}
if (component.name === "Carousel") {
const configuration = carouselConfiguration(variation);
if (Object.hasOwn(configuration, prop.name)) return configuration[prop.name];
return undefined;
}
if (component.name === "ChatBubble") {
const configuration = chatBubbleConfiguration(variation);
if (Object.hasOwn(configuration, prop.name)) return configuration[prop.name];
return undefined;
}
if (component.name === "Collapse") {
const configuration = collapseConfiguration(variation);
if (Object.hasOwn(configuration, prop.name)) return configuration[prop.name];
return undefined;
}
if (component.name === "AdvancedSelect") {
const configuration = advancedSelectConfiguration(variation);
if (Object.hasOwn(configuration, prop.name)) return configuration[prop.name];
return undefined;
}
if (component.name === "ComboBox") {
const configuration = comboboxConfiguration(variation);
if (Object.hasOwn(configuration, prop.name)) return configuration[prop.name];
return undefined;
}
if (component.name === "PinInput") {
const configuration = pinInputConfiguration(variation);
if (Object.hasOwn(configuration, prop.name)) return configuration[prop.name];
return undefined;
}
if (component.name === "StrongPassword") {
const configuration = strongPasswordConfiguration(variation);
if (Object.hasOwn(configuration, prop.name)) return configuration[prop.name];
return undefined;
}
if (component.name === "TogglePassword") {
const configuration = togglePasswordConfiguration(variation);
if (Object.hasOwn(configuration, prop.name)) return configuration[prop.name];
return undefined;
}
if (component.name === "InputNumber") {
const configuration = inputNumberConfiguration(variation);
if (Object.hasOwn(configuration, prop.name)) {
return configuration[prop.name];
}
return undefined;
}
if (component.name === "ToggleCount") {
const configuration = toggleCountConfiguration(variation);
if (Object.hasOwn(configuration, prop.name)) {
return configuration[prop.name];
}
return undefined;
}
const isArray = prop.default === "[]" || prop.type.includes("[]") || prop.type === "array";
const isObject = prop.default === "{}" || prop.type === "object";
if (isArray) return sampleArray(prop, component, variation);
if (isObject) return sampleItem(component, 0, variation);
if (name === "class") return `showcase-instance showcase-instance--${variation + 1}`;
if (component.name === "Button") {
const configurations = [
{
label: "Save & Submit",
as: "button",
type: "submit",
},
{
label: "Learn more",
as: "a",
href: "#button-demo",
variant: "outline",
size: "sm",
icon: "icon-[lucide--arrow-right]",
iconPosition: "end",
},
{ label: "Upgrade plan", as: "button", variant: "secondary", size: "lg", pill: true },
{
label: "Open settings",
as: "button",
ariaLabel: "Open settings",
variant: "ghost",
size: "icon",
icon: "icon-[lucide--settings]",
},
{
label: "Delete project",
as: "button",
variant: "destructive",
icon: "icon-[lucide--trash-2]",
},
{ label: "Read documentation", as: "a", href: "#button-demo", variant: "link" },
{ label: "Publish", as: "button", loadingLabel: "Publishing…", loading: true },
{ label: "Unavailable action", as: "button", disabled: true, variant: "outline" },
];
const configuration = configurations[variation] ?? configurations[0];
if (Object.hasOwn(configuration, prop.name)) return configuration[prop.name];
return undefined;
}
if (name === "controlclass") return variation === 2 ? "shadow-xl" : "";
if (prop.type === "boolean") {
if (name === "compact") return variation === 1;
if (name === "fullwidth") return variation === 2;
if (name === "loading" || name === "disabled") return false;
if (name === "open" || name === "visible" || name === "dismissible" || name.startsWith("show"))
return true;
return defaultValue(prop) ?? variation === 2;
}
if (prop.type === "number") {
if (name === "value") return 36 + variation * 24;
if (name.includes("count") || name.includes("total") || name.includes("page"))
return 3 + variation * 4;
return defaultValue(prop) ?? variation + 1;
}
if (name.includes("arialabel")) return `${displayName(component.name)} ${variation + 1} example`;
if (name === "title" || name.endsWith("title"))
return variation === 0
? `${displayName(component.name)} example`
: variation === 1
? `Compact ${displayName(component.name)}`
: `Advanced ${displayName(component.name)}`;
if (name.includes("description") || name === "summary")
return variation === 0
? `A polished default ${displayName(component.name).toLowerCase()} for a modern application.`
: variation === 1
? "A compact configuration for dashboards and dense product surfaces."
: "A richer configuration demonstrating additional content and hierarchy.";
if (name === "label" || name.endsWith("label"))
return variation === 0
? displayName(component.name)
: variation === 1
? "Compact example"
: "Advanced example";
if (name.includes("placeholder"))
return variation === 2 ? "Search by name, email, or identifier" : "Enter a sample value";
if (name.includes("email")) return variation === 2 ? "team@example.com" : "developer@example.com";
if (name.includes("href") || name === "action") {
if (component.name === "Button" && variation === 0) return "";
return `#${slugOf(component.name)}-demo`;
}
if (name === "icon")
return variation === 0
? ""
: variation === 1
? "icon-[lucide--arrow-right]"
: "icon-[lucide--trash-2]";
if (name === "variant")
if (component.name === "Button")
return variation === 0 ? "primary" : variation === 1 ? "secondary" : "danger";
if (name === "variant")
return variation === 0
? defaultValue(prop) || "primary"
: variation === 1
? "secondary"
: "success";
if (name === "size")
return variation === 0 ? defaultValue(prop) || "md" : variation === 1 ? "sm" : "lg";
if (name === "align") return variation === 1 ? "center" : variation === 2 ? "end" : "start";
if (name === "value") return `sample-${variation + 1}`;
if (name.includes("count") || name.includes("total") || name.includes("page")) return undefined;
if (prop.required) {
return `${displayName(component.name)} sample`;
}
return undefined;
}
function componentAttributes(component, variation, overrides = {}) {
const attributes = [`data-component="${component.mount}"`];
for (const prop of component.props) {
const value = Object.hasOwn(overrides, prop.name)
? overrides[prop.name]
: sampleValue(prop, component, variation);
if (value === undefined) continue;
const encoded =
typeof value === "object" ? jsonAttribute(value) : escapeAttribute(String(value));
attributes.push(`${prop.name}="${encoded}"`);
}
return attributes;
}
function componentMount(component, variation = 0, overrides = {}) {
const slotMarkup = component.slots
.map((slot) =>
slot === "default"
? defaultSlotMarkup(component, variation)
: `
${escapeText(displayName(slot))} slot
`,
)
.join("");
return `${slotMarkup}
`;
}
const structuralDefaultSlots = new Set([
"AspectRatio",
"AutoGrid",
"card",
"Cluster",
"CodeTabs",
"Container",
"ContentSection",
"FilterBar",
"ScrollArea",
"Section",
"SidebarLayout",
"SplitLayout",
"Stack",
"Sticky",
"StickyLayout",
"Surface",
"Typography",
"VisuallyHidden",
"Well",
"WideContainer",
]);
function defaultSlotMarkup(component, variation) {
if (component.name === "Select")
return 'Starter Growth ';
if (component.name === "Table")
return "Name Status Production Active Staging Ready ";
if (component.name === "Tooltip")
return 'Hover for details ';
if (!structuralDefaultSlots.has(component.name)) return "";
return `${variation === 2 ? "Rich supporting content with additional context." : "Composable content area."}
`;
}
function sourceSnippet(component, variation, overrides = {}) {
const attributes = component.props.flatMap((prop) => {
const value = Object.hasOwn(overrides, prop.name)
? overrides[prop.name]
: sampleValue(prop, component, variation);
if (value === undefined || value === "" || prop.name === "class") return [];
const declaredDefault = defaultValue(prop);
if (
declaredDefault !== undefined &&
typeof value !== "object" &&
String(value) === String(declaredDefault)
)
return [];
if (typeof value === "object") {
const serialized = JSON.stringify(value, null, 2)
.replaceAll("'", "\\'")
.replaceAll("\n", "\n ");
return [`${prop.name}='${serialized}'`];
}
return [`${prop.name}="${String(value).replaceAll('"', '\\"')}"`];
});
const formattedAttributes = attributes.length ? `\n ${attributes.join("\n ")}` : "";
const hasPublicComponentTag = /^[A-Z][A-Za-z0-9_]*$/.test(component.mount);
const usesSlot = component.slots.some(
(slot) => slot !== "default" || defaultSlotMarkup(component, variation) !== "",
);
const slotMarkup = usesSlot
? `\n \n`
: "";
if (hasPublicComponentTag) {
const source = slotMarkup
? `<${component.mount}${formattedAttributes}>${slotMarkup}${component.mount}>`
: formattedAttributes
? `<${component.mount}${formattedAttributes}\n/>`
: `<${component.mount} />`;
return escapeCode(source);
}
const mountAttribute = `data-component="${component.mount}"`;
const source = slotMarkup
? `${slotMarkup}
`
: `
`;
return escapeCode(source);
}
function propsTable(component) {
if (component.props.length === 0)
return ` No props. Compose this component with slots.
`;
return `Prop Type Default Required ${component.props
.map(
(prop) =>
`${escapeCode(prop.name)}${escapeText(prop.type)} ${escapeCode(prop.default ?? "—")}${prop.required ? 'Yes ' : "No"} `,
)
.join("")}
`;
}
function playgroundValue(prop, component) {
const sampled = sampleValue(prop, component, 0);
const value = sampled === undefined ? (defaultValue(prop) ?? "") : sampled;
return typeof value === "object" ? JSON.stringify(value, null, 2) : String(value);
}
function playgroundStateName(prop) {
return `playground_${prop.name.replace(/[^A-Za-z0-9_$]/g, "_")}`;
}
function playgroundStates(component) {
return component.props
.map((prop) => {
const initial = playgroundValue(prop, component);
return ` state ${playgroundStateName(prop)} = ctx.url.searchParams.get("pg_${prop.name}") ?? ${JSON.stringify(initial)}`;
})
.join("\n");
}
function playgroundMount(component) {
const attributes = [
`data-component="${component.mount}"`,
...component.props.map((prop) => `${prop.name}="{${playgroundStateName(prop)}}"`),
];
const slotMarkup = component.slots
.map((slot) =>
slot === "default"
? defaultSlotMarkup(component, 0)
: `${escapeText(displayName(slot))} slot
`,
)
.join("");
return `${slotMarkup}
`;
}
function playgroundControl(prop, component) {
const value = playgroundValue(prop, component);
const id = `playground-${slugOf(component.name)}-${prop.name}`;
const label = escapeText(displayName(prop.name));
const meta = `${escapeText(prop.type)}${prop.required ? " · required" : ""} `;
if (prop.type === "boolean") {
return `${label} ${meta} `;
}
const structured =
prop.default === "[]" ||
prop.default === "{}" ||
prop.type === "array" ||
prop.type === "object" ||
prop.type.includes("[]");
if (structured) {
return `${label} ${meta} `;
}
const options = playgroundOptions(prop, component, value);
if (options.length > 1) {
return `${label} ${meta}${options
.map(
(option) =>
`${escapeText(option || "Auto / default")} `,
)
.join("")} `;
}
const inputType =
prop.type === "number"
? "number"
: prop.name.toLowerCase().includes("color") && /^#[0-9a-f]{6}$/i.test(value)
? "color"
: "text";
return `${label} ${meta} `;
}
const commonPlaygroundOptions = {
align: ["start", "center", "end"],
alignment: ["start", "center", "end"],
as: ["", "button", "a"],
color: ["primary", "secondary", "success", "warning", "danger", "info"],
direction: ["row", "column"],
iconposition: ["start", "end"],
method: ["get", "post"],
orientation: ["horizontal", "vertical"],
position: ["top", "right", "bottom", "left", "start", "end"],
size: ["default", "xs", "sm", "md", "lg", "xl", "icon", "icon-xs", "icon-sm", "icon-lg"],
target: ["_self", "_blank", "_parent", "_top"],
variant: ["default", "outline", "secondary", "ghost", "destructive", "link"],
};
function playgroundOptions(prop, component, currentValue) {
if (prop.type !== "string" || prop.name === "class" || prop.name === "controlClass") return [];
const discovered = new Set();
const add = (value) => {
if (typeof value === "string" && value.length <= 40) discovered.add(value);
};
const common = commonPlaygroundOptions[prop.name.toLowerCase()] ?? [];
for (const option of common) add(option);
if (component.name === "Button" && prop.name === "type") {
for (const option of ["button", "submit", "reset"]) add(option);
}
if (component.name === "Blockquote" && prop.name === "align") {
discovered.clear();
for (const option of ["left", "center", "right"]) add(option);
}
if (component.name === "Blockquote" && prop.name === "variant") {
discovered.clear();
for (const option of ["default", "bordered"]) add(option);
}
const sourcePath = join(uiComponentsPath, `${component.name}.wrn`);
try {
const source = readFileSync(sourcePath, "utf8");
const escapedName = prop.name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
const patterns = [
new RegExp(`\\b${escapedName}\\s*={2,3}\\s*["']([^"'\\n]+)["']`, "g"),
new RegExp(`["']([^"'\\n]+)["']\\s*={2,3}\\s*\\b${escapedName}\\b`, "g"),
];
for (const pattern of patterns) {
for (const match of source.matchAll(pattern)) add(match[1]);
}
} catch {
// The generated reference can include virtual components without a same-name source file.
}
if (common.length === 0 && discovered.size < 2) return [];
const values = new Set();
if (typeof currentValue === "string" && currentValue.length <= 40) values.add(currentValue);
const declaredDefault = defaultValue(prop);
if (typeof declaredDefault === "string" && declaredDefault.length <= 40)
values.add(declaredDefault);
for (const option of discovered) values.add(option);
return [...values];
}
function playgroundSection(component) {
const controls = component.props.map((prop) => playgroundControl(prop, component)).join("");
const publicTag = /^[A-Z][A-Za-z0-9_]*$/.test(component.mount);
return `
Interactive playground Configure ${escapeText(displayName(component.name))} Change any prop and inspect the server-rendered component immediately.
Live preview
${playgroundMount(component)}
${sourceSnippet(component, 0)}
`;
}
const defaultUses = [
{
eyebrow: "Recommended",
title: "Production default",
description: "Balanced spacing, hierarchy, and content for the most common product workflow.",
},
{
eyebrow: "Dense UI",
title: "Compact application",
description:
"A tighter variation for dashboards, side panels, tables, and operational interfaces.",
},
{
eyebrow: "Extended",
title: "Rich configuration",
description:
"A more expressive variation using additional data, stronger emphasis, and optional states.",
},
];
const accordionUses = [
{
eyebrow: "Basic usage",
title: "Default accordion",
description: "Open one section at a time with a clear plus and minus indicator.",
},
{
eyebrow: "Behavior",
title: "Always open",
description: "Allow several content sections to remain expanded at the same time.",
},
{
eyebrow: "Composition",
title: "Nested accordion",
description: "Place a coordinated secondary accordion inside an expanded parent section.",
},
{
eyebrow: "Indicators",
title: "Without an indicator",
description: "Use a minimal text-only accordion when visual indicators are unnecessary.",
},
{
eyebrow: "Indicators",
title: "Chevron indicator",
description: "Communicate expanded state with a rotating chevron.",
},
{
eyebrow: "Layout",
title: "Separated title and indicator",
description: "Place the title and state indicator at opposite ends of each trigger.",
},
{
eyebrow: "Surface",
title: "Bordered accordion",
description: "Group sections inside one bordered surface with dividing lines.",
},
{
eyebrow: "Surface",
title: "Bordered open content",
description: "Render each section as an independent rounded card.",
},
{
eyebrow: "States",
title: "Disabled section",
description:
"Disable an individual section while keeping the rest of the accordion interactive.",
},
];
const alertTitles = [
"Dark solid alert",
"Secondary solid alert",
"Info solid alert",
"Success solid alert",
"Danger solid alert",
"Warning solid alert",
"Light solid alert",
"Soft color alert",
"With supporting text",
"With a list",
"With actions",
"Right-aligned link",
"Discovery notice",
"Bordered alert",
"Dismissible alert",
];
const alertUses = alertTitles.map((title, index) => ({
eyebrow: index < 7 ? "Solid colors" : index < 11 ? "Content" : "Actions & states",
title,
description: `Live ${title.toLowerCase()} with accessible semantics and copyable component code.`,
}));
const avatarTitles = [
"Extra-small circular avatar",
"Small circular avatar",
"Medium circular avatar",
"Large circular avatar",
"Extra-large circular avatar",
"Rounded avatars",
"Circular avatars with top status",
"Circular avatars with bottom status",
"Avatars with brand icons",
"Placeholder icon",
"Placeholder initials",
"Solid color variants",
"Soft color variants",
"Outline color variants",
"White color variants",
"Avatar with tooltip",
"Media object",
];
const avatarUses = avatarTitles.map((title, index) => ({
eyebrow:
index < 6
? "Size & shape"
: index < 8
? "Status"
: index < 11
? "Fallbacks"
: index < 15
? "Color variants"
: "Composition",
title,
description:
index < 5
? "Render one circular profile image at the documented size."
: index === 5
? "Apply softly rounded corners when a circle is not the right fit."
: index < 8
? "Place a clear presence indicator at the requested avatar edge."
: index === 8
? "Overlay a service or platform icon to identify a linked account."
: index === 9
? "Show a generic person icon when no profile image is available."
: index === 10
? "Use initials as an accessible text fallback for a missing image."
: index < 15
? `Use the ${title.split(" ")[0].toLowerCase()} treatment across semantic avatar colors.`
: index === 15
? "Reveal a name or extra context on hover and keyboard focus."
: "Combine an avatar with supporting text for profile and activity rows.",
}));
function avatarConfiguration(variation) {
const image = "/avatar-example.svg";
const configurations = [
{ src: image, alt: "Example profile portrait", size: "xs" },
{ src: image, alt: "Example profile portrait", size: "sm" },
{ src: image, alt: "Example profile portrait", size: "md" },
{ src: image, alt: "Example profile portrait", size: "lg" },
{ src: image, alt: "Example profile portrait", size: "xl" },
{ src: image, alt: "Example rounded profile portrait", shape: "rounded", size: "lg" },
{
src: image,
alt: "Avery Chen",
size: "lg",
status: "online",
statusLabel: "Online",
statusPosition: "top",
},
{
src: image,
alt: "Avery Chen",
size: "lg",
status: "away",
statusLabel: "Away",
statusPosition: "bottom",
},
{
src: image,
alt: "Linked profile",
size: "lg",
badgeIcon: "icon-[lucide--message-square]",
badgeLabel: "Messaging account",
},
{ size: "lg", variant: "soft", alt: "Anonymous profile" },
{ size: "lg", initials: "AC", alt: "Avery Chen" },
{ size: "lg", initials: "AC", color: "success", variant: "solid", alt: "Avery Chen" },
{ size: "lg", initials: "AC", color: "info", variant: "soft", alt: "Avery Chen" },
{ size: "lg", initials: "AC", color: "danger", variant: "outline", alt: "Avery Chen" },
{ size: "lg", initials: "AC", variant: "white", alt: "Avery Chen" },
{
src: image,
alt: "Avery Chen",
size: "lg",
status: "online",
tooltip: "Avery Chen · Online",
name: "Avery Chen",
},
{
src: image,
name: "Mark Wanner",
alt: "Mark Wanner",
size: "lg",
description: "mark@example.com",
},
];
return {
size: "md",
color: "primary",
variant: "solid",
shape: "circle",
...configurations[variation],
};
}
const avatarGroupUses = [
{
eyebrow: "Facepile",
title: "Overlapping stack",
description: "Overlap avatars with negative spacing to create a compact team facepile.",
},
{
eyebrow: "Layout",
title: "Grid layout",
description: "Arrange members in a grid with equal spacing and a clear overflow count.",
},
{
eyebrow: "Border",
title: "Custom border color",
description: "Change the ring color so stacked groups remain distinct on different surfaces.",
},
{
eyebrow: "Tooltip",
title: "Member tooltips",
description: "Reveal each member's name on pointer hover or keyboard focus.",
},
{
eyebrow: "Overflow",
title: "Dropdown overflow",
description: "Open the overflow counter to inspect members hidden by the visible limit.",
},
];
function avatarGroupConfiguration(variation) {
const members = [
{ src: "/avatar-example.svg", name: "Mark Wanner", alt: "Mark Wanner" },
{ src: "/avatar-example-2.svg", name: "Maria Guan", alt: "Maria Guan" },
{ src: "/avatar-example-3.svg", name: "Amil Evara", alt: "Amil Evara" },
{ initials: "EE", name: "Ebele Egbuna", color: "info" },
{ initials: "CL", name: "Chris Lynch", color: "success" },
{ initials: "NS", name: "Nora Singh", color: "warning" },
{ initials: "JR", name: "Jon Rivera", color: "danger" },
{ initials: "AK", name: "Amina Khan", color: "secondary" },
];
const configurations = [
{ layout: "stack", size: "lg", maxVisible: 4, items: members.slice(0, 4) },
{ layout: "grid", size: "md", columns: 3, maxVisible: 7, items: members },
{
layout: "stack",
size: "md",
maxVisible: 4,
borderColor: "#2583ff",
items: members.slice(0, 4),
},
{ layout: "stack", size: "md", maxVisible: 4, showTooltips: true, items: members.slice(0, 4) },
{ layout: "stack", size: "md", maxVisible: 4, showTooltips: true, items: members },
];
return {
color: "primary",
size: "md",
variant: "solid",
shape: "circle",
...configurations[variation],
};
}
const badgeUses = [
{
eyebrow: "Color variants",
title: "Solid badge",
description: "Use a solid fill for a high-emphasis status or category.",
},
{
eyebrow: "Color variants",
title: "Soft badge",
description: "Use a softer fill for a lower-contrast label.",
},
{
eyebrow: "Color variants",
title: "Outline badge",
description: "Use an outlined treatment for lighter visual emphasis.",
},
{
eyebrow: "Color variants",
title: "White badge",
description: "Use a neutral surface with a subtle border and shadow.",
},
{
eyebrow: "Shape",
title: "Rounded badge",
description: "Use softly rounded corners instead of fully pilled edges.",
},
{
eyebrow: "Shape",
title: "Pill badge",
description: "Use a pill shape for compact labels and filter chips.",
},
{
eyebrow: "Content",
title: "Truncated badge",
description: "Constrain long badge text while preserving the full value in the API.",
},
{
eyebrow: "Status",
title: "Badge with status dot",
description: "Add a small dot to reinforce status at a glance.",
},
{
eyebrow: "Content",
title: "Badge with icon",
description: "Pair an icon with text for states, metrics, or trend changes.",
},
{
eyebrow: "Interaction",
title: "Dismissible badge",
description: "Provide a clear remove action for a selected chip.",
},
{
eyebrow: "Content",
title: "Badge with avatar",
description: "Combine a profile image, label, and optional remove action.",
},
{
eyebrow: "Composition",
title: "Badge inside button",
description: "Place a badge beside a button label to show a count.",
},
{
eyebrow: "Composition",
title: "Positioned badge",
description: "Anchor a numeric badge to the corner of an icon button.",
},
{
eyebrow: "Composition",
title: "Dot badge",
description: "Use a small dot when a notification does not need a count.",
},
{
eyebrow: "Animation",
title: "Ping badge",
description: "Pulse a notification badge while respecting reduced-motion preferences.",
},
];
function badgeConfiguration(variation) {
const configurations = [
{ label: "Active", color: "success", variant: "solid" },
{ label: "In review", color: "info", variant: "soft" },
{ label: "Attention", color: "danger", variant: "outline" },
{ label: "Neutral", color: "dark", variant: "white" },
{ label: "Category", color: "info", shape: "rounded" },
{ label: "Pill badge", color: "primary", shape: "pill" },
{
label: "This content is a little too long for the available space",
color: "info",
truncate: true,
maxWidth: "10rem",
shape: "rounded",
},
{ label: "Connected", color: "success", variant: "soft", dot: true },
{
label: "14.5%",
color: "success",
variant: "soft",
icon: "icon-[lucide--trending-up]",
},
{ label: "Design", color: "info", variant: "soft", dismissible: true },
{
label: "Christina",
color: "dark",
variant: "white",
avatarSrc: "/avatar-example-2.svg",
avatarAlt: "Christina",
dismissible: true,
},
{
label: "5",
color: "danger",
size: "xs",
anchorLabel: "Notifications",
anchorLabelText: "Open notifications",
placement: "inline",
},
{
label: "99+",
color: "danger",
size: "xs",
anchorIcon: "icon-[lucide--bell]",
anchorLabelText: "Open notifications",
placement: "top-right",
},
{
label: "",
color: "success",
dotOnly: true,
dotLabel: "New notification",
anchorIcon: "icon-[lucide--bell]",
anchorLabelText: "Open notifications",
placement: "top-right",
},
{
label: "9+",
color: "danger",
animated: true,
anchorIcon: "icon-[lucide--shopping-basket]",
anchorLabelText: "Open cart",
placement: "top-right",
},
];
return {
size: "md",
color: "primary",
variant: "solid",
shape: "pill",
...configurations[variation],
};
}
function alertConfiguration(variation) {
const title = alertTitles[variation] ?? alertTitles[0];
const lower = title.toLowerCase();
const solidColors = ["dark", "secondary", "info", "success", "danger", "warning", "light"];
const solidIndex = Math.min(variation, solidColors.length - 1);
const color =
variation < solidColors.length
? solidColors[solidIndex]
: lower.includes("danger")
? "danger"
: lower.includes("warning")
? "warning"
: lower.includes("success")
? "success"
: "info";
const supporting = lower.includes("supporting");
const discovery = lower.includes("discovery");
return {
title:
variation < solidColors.length
? `${titleCase(color)} alert!`
: supporting
? "Successfully updated."
: lower.includes("list")
? "A problem occurred while submitting your data."
: lower.includes("actions")
? "YouTube would like to send notifications"
: discovery
? "New version published"
: lower.includes("dismissible")
? "Requires JavaScript"
: "A new software update is available.",
description:
variation < solidColors.length
? "You should check in on some of those fields below."
: supporting
? "You have successfully updated your email preferences."
: lower.includes("actions")
? "Notifications may include alerts, sounds, and icon badges. These can be configured in Settings."
: discovery
? "A new version of this page was published. Refresh to see the changes."
: lower.includes("dismissible")
? "This page requires an optional plugin. You can skip this step if it is already installed."
: lower.includes("list")
? ""
: "See what is new in version 3.0.7.",
color,
variant:
variation < solidColors.length
? "solid"
: lower.includes("bordered")
? "bordered"
: supporting
? "accent"
: "soft",
showIcon: variation >= solidColors.length,
items: lower.includes("list")
? [
{ label: "This username is already in use" },
{ label: "Email field cannot be empty" },
{ label: "Please enter a valid phone number" },
]
: [],
actions: lower.includes("actions")
? [
{ label: "Don't allow", href: "#deny" },
{ label: "Allow", href: "#allow" },
]
: [],
actionLabel: lower.includes("dismissible") ? "Learn more" : "",
actionHref: lower.includes("dismissible") ? "#learn-more" : "",
linkLabel: lower.includes("right-aligned") ? "Details" : discovery ? "Refresh" : "",
linkHref: lower.includes("right-aligned") ? "#details" : discovery ? "#refresh" : "",
dismissible: lower.includes("dismissible"),
compact: variation < solidColors.length,
radius: variation < solidColors.length ? "lg" : lower.includes("discovery") ? "xl" : "md",
shadow: lower.includes("bordered") ? "none" : lower.includes("discovery") ? "md" : "sm",
};
}
function blockquoteConfiguration(variation) {
const configurations = [
{
quote:
"I just wanted to say that I'm very happy with my purchase so far. The documentation is outstanding - clear and detailed.",
size: "md",
color: "primary",
align: "left",
variant: "default",
quoteMark: true,
italic: true,
},
{
quote: "Good design makes the complex understandable and the useful feel effortless.",
citation: "Josh Grazioso",
citationTitle: "Product designer",
size: "sm",
color: "info",
align: "center",
variant: "default",
quoteMark: true,
italic: true,
},
{
quote:
"The strongest product experiences are built when clarity, craft, and empathy reinforce one another.",
citation: "Josh Grazioso",
citationTitle: "Source title",
citationUrl: "#blockquote-demo",
avatarSrc: "/avatar-example.svg",
avatarAlt: "Josh Grazioso",
size: "lg",
color: "success",
align: "left",
variant: "bordered",
quoteMark: false,
italic: true,
},
];
return configurations[variation] ?? configurations[0];
}
function accordionConfiguration(variation) {
const body =
"This is the item's accordion body. It is hidden by default until the section is opened. Use it for supporting details, explanations, or progressive disclosure.";
const items = [
{ value: "first", label: "Accordion #1", content: body },
{ value: "second", label: "Accordion #2", content: body },
{ value: "third", label: "Accordion #3", content: body },
];
const configurations = [
{ defaultOpen: ["first"], contentItalic: true },
{ defaultOpen: ["first", "second"], alwaysOpen: true, contentItalic: true },
{
defaultOpen: ["first", "first.sub-first"],
alwaysOpen: true,
contentItalic: true,
items: [
{
...items[0],
children: [
{ value: "sub-first", label: "Sub accordion #1", content: body },
{ value: "sub-second", label: "Sub accordion #2", content: body },
{ value: "sub-third", label: "Sub accordion #3", content: body },
],
},
items[1],
items[2],
],
},
{ defaultOpen: ["first"], showIndicator: false, contentItalic: true },
{
defaultOpen: ["first"],
indicator: "chevron",
indicatorPosition: "start",
variant: "chevron",
contentItalic: true,
},
{
defaultOpen: ["first"],
indicator: "chevron",
indicatorPosition: "end",
variant: "chevron",
contentItalic: true,
},
{ defaultOpen: ["first"], bordered: true, contentItalic: true },
{
defaultOpen: ["second"],
separated: true,
indicatorPosition: "end",
contentItalic: true,
},
{
defaultOpen: ["first"],
items: [items[0], { ...items[1], disabled: true }, items[2]],
contentItalic: true,
},
];
return {
id: `accordion-example-${variation + 1}`,
items,
color: "primary",
size: "default",
...configurations[variation],
};
}
const buttonUses = [
{
eyebrow: "Default",
title: "Primary action",
description: "The standard high-emphasis action for forms and product workflows.",
},
{
eyebrow: "Outline · Small",
title: "Secondary navigation",
description: "A compact link button with an end icon and restrained emphasis.",
},
{
eyebrow: "Secondary · Large · Pill",
title: "Prominent rounded action",
description: "A large secondary action using the optional pill shape.",
},
{
eyebrow: "Ghost · Icon",
title: "Accessible icon action",
description: "An icon-only control with its accessible name supplied through props.",
},
{
eyebrow: "Destructive",
title: "Dangerous action",
description: "A destructive treatment for irreversible or high-risk operations.",
},
{
eyebrow: "Link",
title: "Inline action",
description: "A low-chrome action that behaves and reads like an inline link.",
},
{
eyebrow: "Loading",
title: "Pending action",
description: "The button owns its busy state, loading label, spinner, and disabled behavior.",
},
{
eyebrow: "Disabled",
title: "Unavailable action",
description: "A visibly and semantically disabled control.",
},
];
const buttonGroupUses = [
{
eyebrow: "Basic usage",
title: "Example",
description: "Group related actions into one compact, visually connected control.",
},
{
eyebrow: "Sizes",
title: "Small",
description: "Use small grouped buttons in dense toolbars, tables, and utility surfaces.",
},
{
eyebrow: "Sizes",
title: "Medium",
description: "Use the balanced medium size for most product interfaces.",
},
{
eyebrow: "Sizes",
title: "Large",
description: "Use large grouped buttons for prominent actions and touch-focused layouts.",
},
{
eyebrow: "Responsive stack",
title: "Responsive stack",
description: "Keep actions inline on larger screens and stack them on narrow viewports.",
},
{
eyebrow: "Orientation",
title: "Vertical stack",
description: "Stack connected actions vertically while preserving joined borders.",
},
{
eyebrow: "Toolbar",
title: "Button toolbar",
description: "Expose a labeled toolbar with selectable formatting actions.",
},
];
function buttonGroupConfiguration(variation) {
const actions = [
{ label: "Profile", value: "profile" },
{ label: "Settings", value: "settings" },
{ label: "Messages", value: "messages" },
];
const configurations = [
{ items: actions, value: "profile", selectable: true },
{ items: actions, size: "sm" },
{ items: actions, size: "md" },
{ items: actions, size: "lg" },
{
items: [
{ label: "Save draft", value: "draft" },
{ label: "Preview", value: "preview" },
{ label: "Publish", value: "publish" },
],
responsive: true,
},
{
items: [
{ label: "Account", value: "account" },
{ label: "Security", value: "security" },
{ label: "Notifications", value: "notifications" },
],
orientation: "vertical",
},
{
items: [
{ label: "Bold", value: "bold", icon: "icon-[lucide--bold]" },
{ label: "Italic", value: "italic", icon: "icon-[lucide--italic]" },
{ label: "Underline", value: "underline", icon: "icon-[lucide--underline]" },
],
value: "bold",
selectable: true,
toolbar: true,
ariaLabel: "Text formatting",
},
];
return {
color: "primary",
variant: "outline",
size: "md",
orientation: "horizontal",
attached: true,
...configurations[variation],
};
}
const cardUses = [
{
eyebrow: "Basic usage",
title: "Default card",
description: "Use a basic card with media, text content, and a supporting action.",
},
{
eyebrow: "Content",
title: "Body",
description: "Use the card body for the main content area.",
},
{
eyebrow: "Content",
title: "Simple card",
description: "Use a body-only card when no separate header or footer is needed.",
},
{
eyebrow: "Structure",
title: "Header and footer",
description: "Add a header, footer, or both when the card needs clearer structure.",
},
{
eyebrow: "Structure",
title: "Footer only",
description: "Separate secondary actions or summary details from the card body.",
},
{
eyebrow: "Sizes",
title: "Small",
description: "Use a compact card size for denser lists and side panels.",
},
{
eyebrow: "Sizes",
title: "Default",
description: "Use the default card size for most content blocks.",
},
{
eyebrow: "Sizes",
title: "Large",
description: "Increase card spacing when content needs more breathing room.",
},
{
eyebrow: "Navigation",
title: "Navigation",
description: "Add navigation in the card header for quick section switching.",
},
{
eyebrow: "Navigation",
title: "Navigation with select on mobile",
description: "Replace tabs with a select control when horizontal space is limited.",
},
{
eyebrow: "Images",
title: "Top image",
description: "Place an image at the top for a classic media-card layout.",
},
{
eyebrow: "Images",
title: "Bottom image",
description: "Let content lead before media displayed at the bottom.",
},
{
eyebrow: "Images",
title: "Image overlays",
description: "Use the image as a background and layer card content on top.",
},
{
eyebrow: "Animations",
title: "Image scaling animation on hover",
description: "Scale card media on hover without changing the card layout.",
},
{
eyebrow: "Animations",
title: "Transition on hover",
description: "Animate elevation and position to strengthen the interactive surface.",
},
{
eyebrow: "Card layout",
title: "Horizontal",
description: "Place image and content side by side on larger screens.",
},
{
eyebrow: "Card layout",
title: "Card group",
description: "Render attached equal-height cards that stack responsively.",
},
{
eyebrow: "Panels",
title: "Top bordered card",
description: "Add a colored top border for stronger category or status emphasis.",
},
{
eyebrow: "Panels",
title: "Card actions",
description: "Place compact utility actions in a structured card header.",
},
{
eyebrow: "Panels",
title: "Card with alert",
description: "Embed an alert inside a card to surface important feedback.",
},
{
eyebrow: "Panels",
title: "Centered body content",
description: "Center content for confirmation states and compact summaries.",
},
{
eyebrow: "Panels",
title: "Empty state",
description: "Communicate that no data is available with an accessible empty state.",
},
{
eyebrow: "Panels",
title: "Scrollable body",
description: "Contain long content inside a fixed-height scrolling card body.",
},
];
function cardConfiguration(variation) {
const image = "/card-landscape.svg";
const description =
"Some quick example text to build on the card title and make up the bulk of the card's content.";
const navigation = [
{ label: "My Account", value: "account" },
{ label: "Company", value: "company" },
{ label: "Team Members", value: "team" },
{ label: "Billing", value: "billing" },
];
const base = {
title: "Card title",
description,
actionLabel: "Card link",
actionHref: "#card-demo",
size: "md",
color: "primary",
};
const configurations = [
{ imageSrc: image, imageAlt: "Abstract blue glass shapes", actionLabel: "Go somewhere" },
{ title: "", description, actionLabel: "" },
{ subtitle: "Card subtitle" },
{ header: "Featured", footer: "Last updated 5 mins ago" },
{ footer: "Last updated 5 mins ago" },
{ size: "sm" },
{ size: "md" },
{ size: "lg" },
{ navigation: navigation.slice(0, 3), activeNav: "account", align: "center" },
{ navigation, activeNav: "account", mobileNavigation: true, align: "center" },
{ imageSrc: image, imageAlt: "Abstract blue glass shapes", footer: "Last updated 5 mins ago" },
{
imageSrc: image,
imageAlt: "Abstract blue glass shapes",
imagePosition: "bottom",
footer: "Last updated 5 mins ago",
},
{
imageSrc: image,
imageAlt: "Abstract blue glass shapes",
imagePosition: "overlay",
footer: "Last updated 5 mins ago",
actionLabel: "",
},
{ imageSrc: image, imageAlt: "Abstract blue glass shapes", hover: "image", actionLabel: "" },
{ imageSrc: image, imageAlt: "Abstract blue glass shapes", hover: "raise", actionLabel: "" },
{
imageSrc: image,
imageAlt: "Abstract blue glass shapes",
layout: "horizontal",
footer: "Last updated 5 mins ago",
actionLabel: "",
},
{
items: [
{ imageSrc: image, title: "Card title", description, footer: "Updated 5 mins ago" },
{
imageSrc: image,
title: "Card title",
description: "This card has supporting text below as a natural lead-in.",
footer: "Updated 5 mins ago",
},
{
imageSrc: image,
title: "Card title",
description: `${description} This has even longer content to demonstrate equal height.`,
footer: "Updated 5 mins ago",
},
],
actionLabel: "",
},
{ variant: "top-border" },
{
header: "Card actions",
headerActions: [
{ label: "Refresh", ariaLabel: "Refresh card", icon: "icon-[lucide--refresh-cw]" },
{ label: "Expand", ariaLabel: "Expand card", icon: "icon-[lucide--maximize-2]" },
],
dismissible: true,
},
{
header: "Featured",
alertTitle: "Attention needed!",
alertDescription: "This is an alert box.",
},
{ align: "center" },
{ empty: true, title: "", description: "", actionLabel: "" },
{
description: `${description} ${description} ${description} ${description} ${description}`,
scrollable: true,
maxHeight: "16rem",
actionLabel: "",
},
];
return { ...base, ...configurations[variation] };
}
const carouselUses = [
{
eyebrow: "Basic usage",
title: "Basic usage",
description:
"Cycle through images or content slides with accessible previous and next controls.",
},
{
eyebrow: "Navigation",
title: "Pagination",
description: "Show the current slide and let users jump directly to any slide.",
},
{
eyebrow: "Playback",
title: "Autoplay",
description: "Advance automatically, pausing while users hover or interact with the carousel.",
},
{
eyebrow: "Playback",
title: "Infinite loop",
description: "Continue from the last slide back to the first without disabling controls.",
},
{
eyebrow: "Direction",
title: "RTL",
description: "Reverse movement and controls for right-to-left interfaces.",
},
{
eyebrow: "Layout",
title: "Multiple slides",
description:
"Keep several slides visible at once for products, articles, or media collections.",
},
{
eyebrow: "Layout",
title: "Centered",
description: "Keep a compact active slide centered while adjacent content remains visible.",
},
{
eyebrow: "Pointer interaction",
title: "Draggable",
description: "Drag or swipe slides with a pointer while preserving keyboard controls.",
},
{
eyebrow: "Layout",
title: "Auto height",
description: "Let each slide determine the carousel height for variable-length content.",
},
{
eyebrow: "Scrolling",
title: "Snap scrolling",
description: "Use native horizontal scrolling with mandatory centered snap points.",
},
{
eyebrow: "Status",
title: "Slide counter",
description: "Announce and display the current position and total slide count.",
},
{
eyebrow: "Thumbnails",
title: "Thumbnails (horizontal)",
description: "Use a horizontal thumbnail rail as an alternate set of slide controls.",
},
{
eyebrow: "Thumbnails",
title: "Thumbnails (vertical)",
description: "Place thumbnail navigation beside the carousel on layouts with more room.",
},
];
function carouselConfiguration(variation) {
const items = [
{
label: "First slide",
title: "First slide",
description: "Introduce the collection with a concise, focused message.",
},
{
label: "Second slide",
title: "Second slide",
description: "Move through content using buttons, pagination, keyboard, or touch.",
},
{
label: "Third slide",
title: "Third slide",
description: "Every interaction exposes useful public lifecycle and navigation events.",
},
{
label: "Fourth slide",
title: "Fourth slide",
description: "Flexible for larger collections.",
},
{ label: "Fifth slide", title: "Fifth slide", description: "Responsive at every viewport." },
{ label: "Sixth slide", title: "Sixth slide", description: "Built with accessible semantics." },
];
const configurations = [
{},
{ showPagination: true },
{ showPagination: true, isAutoPlay: true, autoplayInterval: 2500 },
{ showPagination: true, isInfiniteLoop: true },
{ showPagination: true, isRTL: true, activeIndex: 1 },
{ showPagination: true, slidesPerView: 3, activeIndex: 3 },
{ showPagination: true, slidesPerView: 2, isCentered: true },
{ showPagination: true, slidesPerView: 3, isDraggable: true, activeIndex: 1 },
{ showPagination: true, isAutoHeight: true },
{ showPagination: true, slidesPerView: 2, isSnap: true },
{ showCounter: true },
{ thumbnails: "horizontal" },
{ thumbnails: "vertical" },
];
return {
items,
title: "",
description: "",
ariaLabel: carouselUses[variation].title,
color: "primary",
size: "default",
...configurations[variation],
};
}
const chatBubbleUses = [
{
eyebrow: "Basic usage",
title: "Conversation",
description: "Show a basic two-sided conversation with incoming and outgoing messages.",
},
{
eyebrow: "Layout",
title: "One-sided",
description: "Use one-sided bubbles for support transcripts or single-direction message feeds.",
},
{
eyebrow: "Identity",
title: "Messages with avatars",
description: "Add avatars when people need to identify message ownership at a glance.",
},
{
eyebrow: "Message details",
title: "Message metadata",
description: "Add delivery state, timestamps, or recovery actions beneath each message.",
},
];
function chatBubbleConfiguration(variation) {
const items = [
{
id: "question",
direction: "incoming",
author: "Support agent",
title: "How can we help?",
text: "You can ask questions like:",
bullets: [
"What's WireJS UI?",
"How many component examples are there?",
"Is there a PRO version?",
],
},
{
id: "request",
direction: "outgoing",
author: "AZ",
text: "what's wirejs ui?",
},
{
id: "answer",
direction: "incoming",
author: "Support agent",
text: "WireJS UI is an open-source set of reusable UI components built for responsive applications.",
links: [
{ label: "Installation guide", href: "/installation" },
{ label: "Framework guides", href: "/framework-guides" },
],
},
];
if (variation >= 2) {
items[0].avatarSrc = "/avatar-example.svg";
items[0].avatarAlt = "Support agent";
items[0].avatarLabel = "View support agent";
items[1].avatarFallback = "AZ";
items[1].avatarLabel = "View AZ";
items[2].avatarSrc = "/avatar-example.svg";
items[2].avatarAlt = "Support agent";
items[2].avatarLabel = "View support agent";
}
if (variation === 3) {
items[0].status = "Sent";
items[0].statusIcon = "icon-[lucide--check-check]";
items[0].timestamp = "10:32";
items[0].datetime = "2026-07-28T10:32:00";
items[1].status = "Read";
items[1].statusIcon = "icon-[lucide--check-check]";
items[1].timestamp = "10:33";
items[1].datetime = "2026-07-28T10:33:00";
items[2].status = "Not sent";
items[2].statusIcon = "icon-[lucide--circle-alert]";
items[2].statusTone = "danger";
items[2].action = "retry";
items[2].actionLabel = "Retry";
}
return {
items,
oneSided: variation === 1,
showAvatars: variation >= 2,
showMetadata: variation === 3,
ariaLabel: chatBubbleUses[variation].title,
title: "",
description: "",
color: "primary",
size: "default",
};
}
const collapseUses = [
{
eyebrow: "Basic usage",
title: "Default collapse",
description: "Use a button trigger to expand and collapse supporting content.",
},
{
eyebrow: "Inline content",
title: "Read more toggle",
description: "Reveal additional copy inline with a read more and read less trigger.",
},
];
function collapseConfiguration(variation) {
const configurations = [
{
mode: "panel",
items: [
{
id: "collapse-default-panel",
label: "Collapse",
closeLabel: "Collapse",
content:
"This is a collapse body. It is hidden by default until the user expands it, keeping the surrounding interface focused.",
},
],
},
{
mode: "inline",
items: [
{
id: "collapse-read-more-panel",
label: "Read more",
closeLabel: "Read less",
preview:
"WireJS UI is an open-source set of reusable components based on a utility-first styling system.",
content:
"This additional collapse body is hidden by default. Expanding it reveals supporting information while preserving a compact reading experience.",
},
],
},
];
return {
...configurations[variation],
multiple: false,
initialOpenIndexes: [],
ariaLabel: collapseUses[variation].title,
color: "primary",
size: "default",
};
}
const advancedSelectTitles = [
"Default advanced select",
"Grouped options",
"Fixed-position dropdown",
"Placeholder with icon",
"Allow empty selection",
"Multiple selection",
"Multiple selection with optgroups",
"Multiple selection with option templates",
"Multiple selection with counter",
"Multiple selection with conditional counter",
"Counter with option templates",
"Searchable dropdown",
"Minimum search length",
"Search result limit",
"Scroll to selected option",
"Search match mode",
"Search labels and descriptions",
"Tag-style selection",
"Disabled tag selection",
"Option template with icons",
"Option template with avatars",
"Option template with color indicators",
"Advanced select sizes",
"Disabled advanced select",
"Inside a modal",
"Validation states",
"Dynamic validation styling",
"Add or remove options",
"Add or remove options in multiple mode",
"Set a single value programmatically",
"Set multiple values programmatically",
"Remote data source",
"Remote data source (multiple)",
"Remote data tags",
"Option templates with remote data",
"Conditional counter with remote data",
"Avatar template with remote data",
"Preselected values",
"Infinite scroll",
"Inside overflow-hidden containers",
"Destroy and reinitialize",
];
const advancedSelectUses = advancedSelectTitles.map((title, index) => ({
eyebrow: index < 11 ? "Selection" : index < 23 ? "Search & templates" : "Advanced behavior",
title,
description: `Live ${title.toLowerCase()} configuration with copyable component code.`,
}));
function advancedSelectConfiguration(variation) {
const title = advancedSelectTitles[variation] ?? advancedSelectTitles[0];
const lower = title.toLowerCase();
const options = [
{
value: "design",
label: "Design",
description: "Product and visual design",
icon: "icon-[lucide--palette]",
color: "#8b5cf6",
avatar:
"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect width='64' height='64' rx='32' fill='%238b5cf6'/%3E%3Ctext x='32' y='40' text-anchor='middle' font-family='Arial' font-size='24' fill='white'%3ED%3C/text%3E%3C/svg%3E",
},
{
value: "engineering",
label: "Engineering",
description: "Platform and application engineering",
icon: "icon-[lucide--code-2]",
color: "#0ea5e9",
avatar:
"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect width='64' height='64' rx='32' fill='%230ea5e9'/%3E%3Ctext x='32' y='40' text-anchor='middle' font-family='Arial' font-size='24' fill='white'%3EE%3C/text%3E%3C/svg%3E",
},
{
value: "growth",
label: "Growth",
description: "Marketing and customer growth",
icon: "icon-[lucide--trending-up]",
color: "#10b981",
avatar:
"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect width='64' height='64' rx='32' fill='%2310b981'/%3E%3Ctext x='32' y='40' text-anchor='middle' font-family='Arial' font-size='24' fill='white'%3EG%3C/text%3E%3C/svg%3E",
},
{ value: "support", label: "Support", description: "Customer operations", disabled: false },
];
const multiple = lower.includes("multiple") || lower.includes("tags");
const remote = lower.includes("remote") || lower.includes("infinite");
const grouped = lower.includes("group");
return {
label: title,
name: `advanced-select-${variation + 1}`,
value:
multiple || lower.includes("placeholder")
? ""
: lower.includes("preselected")
? "engineering"
: "design",
values: multiple ? ["design", "engineering"] : [],
options: grouped ? [] : options,
groups: grouped
? [
{ label: "Product", options: options.slice(0, 2) },
{ label: "Business", options: options.slice(2) },
]
: [],
placeholder: lower.includes("placeholder") ? "Choose a workspace" : "Choose a team",
placeholderIcon: lower.includes("placeholder") ? "icon-[lucide--building-2]" : "",
multiple,
searchable: lower.includes("search") || remote || variation === 0,
allowEmpty: lower.includes("empty"),
tags: lower.includes("tag"),
disabled: lower.includes("disabled"),
required: lower.includes("validation"),
invalid: false,
validationMessage: "",
minSearchLength: lower.includes("minimum") ? 3 : remote && !lower.includes("infinite") ? 2 : 0,
searchResultLimit: lower.includes("result limit") ? 3 : 0,
searchMode: lower.includes("match mode") ? "startsWith" : "contains",
showCounter: lower.includes("counter"),
maxSelections: lower.includes("conditional") ? 3 : 0,
optionTemplate: lower.includes("avatar")
? "avatar"
: lower.includes("color")
? "color"
: lower.includes("icon") || lower.includes("template")
? "icon"
: "default",
fixed: lower.includes("fixed") || lower.includes("overflow"),
remote,
remoteUrl: remote ? "/api/teams" : "",
infinite: lower.includes("infinite"),
hasMore: lower.includes("infinite"),
size: lower.includes("sizes") ? "lg" : "default",
};
}
const comboboxTitles = [
"Default autocomplete",
"Local HTML options",
"Grouped suggestions",
"Minimum search length",
"Search result limit",
"Starts-with matching",
"Search labels and descriptions",
"Suggestions with icons",
"Suggestions with avatars",
"Suggestions with color indicators",
"Clearable value",
"Custom text values",
"Preselected value",
"Disabled combobox",
"Validation state",
"Remote data with automatic preload",
"Remote search API",
"Remote query parameters",
"Infinite remote suggestions",
"Fixed-position suggestions",
"JavaScript methods",
];
const comboboxUses = comboboxTitles.map((title, index) => ({
eyebrow: index < 7 ? "Autocomplete" : index < 15 ? "Templates & states" : "Remote & API",
title,
description: `Live ${title.toLowerCase()} combobox with editable autocomplete and copyable component code.`,
}));
function comboboxConfiguration(variation) {
const title = comboboxTitles[variation] ?? comboboxTitles[0];
const lower = title.toLowerCase();
const options = [
{
value: "design",
label: "Design",
description: "Product and visual design",
icon: "icon-[lucide--palette]",
color: "#8b5cf6",
avatar:
"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect width='64' height='64' rx='32' fill='%238b5cf6'/%3E%3Ctext x='32' y='40' text-anchor='middle' font-family='Arial' font-size='24' fill='white'%3ED%3C/text%3E%3C/svg%3E",
},
{
value: "engineering",
label: "Engineering",
description: "Platform and application engineering",
icon: "icon-[lucide--code-2]",
color: "#0ea5e9",
avatar:
"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect width='64' height='64' rx='32' fill='%230ea5e9'/%3E%3Ctext x='32' y='40' text-anchor='middle' font-family='Arial' font-size='24' fill='white'%3EE%3C/text%3E%3C/svg%3E",
},
{
value: "growth",
label: "Growth",
description: "Marketing and customer growth",
icon: "icon-[lucide--trending-up]",
color: "#10b981",
avatar:
"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect width='64' height='64' rx='32' fill='%2310b981'/%3E%3Ctext x='32' y='40' text-anchor='middle' font-family='Arial' font-size='24' fill='white'%3EG%3C/text%3E%3C/svg%3E",
},
{
value: "support",
label: "Support",
description: "Customer operations",
icon: "icon-[lucide--life-buoy]",
color: "#f59e0b",
},
];
const remote =
lower.includes("remote") || lower.includes("infinite") || lower.includes("query parameter");
const grouped = lower.includes("grouped");
return {
label: title,
name: `combobox-${variation + 1}`,
value: lower.includes("preselected") ? "engineering" : "",
options: grouped ? [] : options,
groups: grouped
? [
{ label: "Product", options: options.slice(0, 2) },
{ label: "Business", options: options.slice(2) },
]
: [],
placeholder: remote ? "Search remote teams" : "Search teams",
allowCustomValue: lower.includes("custom"),
clearable: true,
disabled: lower.includes("disabled"),
required: lower.includes("validation"),
invalid: false,
validationMessage: "",
minSearchLength: lower.includes("minimum") ? 3 : remote ? 2 : 0,
searchResultLimit: lower.includes("result limit") ? 2 : 0,
searchMode: lower.includes("starts-with") ? "startsWith" : "contains",
optionTemplate: lower.includes("avatar")
? "avatar"
: lower.includes("color")
? "color"
: lower.includes("icon")
? "icon"
: "default",
fixed: lower.includes("fixed"),
remote,
remoteUrl: remote ? "/api/teams" : "",
remoteQueryParam: "q",
remoteAutoLoad: true,
infinite: lower.includes("infinite"),
hasMore: lower.includes("infinite"),
};
}
const pinInputTitles = [
"Default four-digit PIN",
"Different lengths",
"Pre-filled verification code",
"Masked security PIN",
"Alphanumeric regex",
"Grouped code with separator",
"Paste from clipboard",
"Disabled PIN input",
"Read-only code",
"Validation state",
"Supporting help text",
"Large PIN cells",
"Custom placeholder",
"Automatic form submission",
"Completion events",
"JavaScript methods",
];
const pinInputUses = pinInputTitles.map((title, index) => ({
eyebrow: index < 7 ? "Entry behavior" : index < 13 ? "States & design" : "Automation",
title,
description: `Live ${title.toLowerCase()} example with keyboard navigation, filtering, and copyable component code.`,
}));
function pinInputConfiguration(variation) {
const title = pinInputTitles[variation] ?? pinInputTitles[0];
const lower = title.toLowerCase();
const fourDigits = lower.includes("four-digit") || lower.includes("security pin");
return {
label: title,
name: `pin-input-${variation + 1}`,
value: lower.includes("pre-filled") ? "482915" : lower.includes("read-only") ? "739204" : "",
length: fourDigits ? 4 : 6,
pattern: lower.includes("alphanumeric") ? "[A-Z0-9]" : "[0-9]",
inputMode: lower.includes("alphanumeric") ? "text" : "numeric",
placeholder: lower.includes("custom placeholder") ? "•" : "○",
masked: lower.includes("masked"),
disabled: lower.includes("disabled"),
readonly: lower.includes("read-only"),
required: lower.includes("validation"),
invalid: false,
validationMessage: "",
allowPaste: true,
separator: lower.includes("separator") ? "–" : "",
groupSize: lower.includes("separator") ? 3 : 0,
helpText: lower.includes("help text")
? "Enter the code sent to your registered email address."
: "",
autoSubmit: lower.includes("automatic"),
size: lower.includes("large") ? "lg" : "default",
};
}
const strongPasswordTitles = [
"Live strength meter",
"Requirements and hint text",
"Requirements in a popover",
"Custom special characters",
"Optional requirements",
"Disabled password strength",
];
const strongPasswordUses = strongPasswordTitles.map((title, index) => ({
eyebrow: index < 3 ? "Strength feedback" : "Configuration",
title,
description: `Live ${title.toLowerCase()} example with accessible scoring feedback and copyable component code.`,
}));
function strongPasswordConfiguration(variation) {
const title = strongPasswordTitles[variation] ?? strongPasswordTitles[0];
const lower = title.toLowerCase();
return {
label: title,
name: `strong-password-${variation + 1}`,
value: lower.includes("live") ? "Northstar!2026" : "",
placeholder: "Create a strong password",
autocomplete: "new-password",
minLength: 8,
specialCharactersSet: lower.includes("custom") ? "@#_-." : "!@#$%^&*()_+-=[]{}|;:,.<>?",
requireLowercase: true,
requireUppercase: true,
requireNumber: true,
requireSpecialCharacter: !lower.includes("optional"),
showRequirements: !lower.includes("live"),
presentation: lower.includes("popover") ? "popover" : "inline",
hintText: lower.includes("hint")
? "Use a unique password you do not use for another account."
: "",
disabled: lower.includes("disabled"),
readonly: false,
required: false,
invalid: false,
validationMessage: "",
};
}
const togglePasswordTitles = [
"Inline visibility button",
"Checkbox-controlled toggle",
"Synchronized password fields",
"Without visibility toggle",
"Supporting help text",
"Required validation",
"Disabled password",
"Read-only password",
"Large password field",
"Toggle events",
];
const togglePasswordUses = togglePasswordTitles.map((title, index) => ({
eyebrow: index < 4 ? "Visibility" : index < 8 ? "Forms & states" : "Design & events",
title,
description: `Live ${title.toLowerCase()} example with accessible visibility controls and copyable component code.`,
}));
function togglePasswordConfiguration(variation) {
const title = togglePasswordTitles[variation] ?? togglePasswordTitles[0];
const lower = title.toLowerCase();
const checkbox = lower.includes("checkbox");
const synchronized = lower.includes("synchronized");
return {
label: synchronized ? "Passwords" : lower.includes("inline") ? "Password" : title,
name: `toggle-password-${variation + 1}`,
value:
lower.includes("inline") || checkbox || lower.includes("read-only") ? "Northstar!2026" : "",
placeholder: "Enter your password",
autocomplete: "current-password",
minlength: lower.includes("validation") ? "8" : "",
maxlength: "128",
pattern: "(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[^A-Za-z0-9]).{8,}",
fields: synchronized
? [
{
label: "New password",
name: "new-password",
placeholder: "Enter new password",
autocomplete: "new-password",
},
{
label: "Current password",
name: "current-password",
value: "Northstar!2026",
autocomplete: "current-password",
},
]
: [],
visible: false,
toggleable: !lower.includes("without"),
toggleMode: checkbox ? "checkbox" : "button",
checkboxLabel: "Show password",
disabled: lower.includes("disabled"),
readonly: lower.includes("read-only"),
required: lower.includes("validation"),
helpText: lower.includes("help text")
? "Use at least eight characters and avoid passwords used on other sites."
: "",
validationMessage: "",
invalid: false,
size: lower.includes("large") ? "lg" : "default",
};
}
const inputNumberUses = [
{
eyebrow: "Basic usage",
title: "Default controls",
description: "Use standard decrement and increment buttons alongside a numeric input field.",
},
{
eyebrow: "Basic usage",
title: "Labeled input style",
description:
"Add supporting label text and rounded controls for a more form-like number input.",
},
{
eyebrow: "Layout",
title: "Vertical buttons",
description: "Stack the increment and decrement buttons vertically beside the field.",
},
{
eyebrow: "Layout",
title: "Horizontal buttons",
description: "Place the increment and decrement buttons on opposite sides of the value.",
},
{
eyebrow: "Sizing",
title: "Compact size",
description: "Use a smaller compact control when the available space is limited.",
},
{
eyebrow: "Commerce",
title: "Seat quantity selector",
description: "Pair the control with pricing information for seat counts or plan add-ons.",
},
{
eyebrow: "States",
title: "Disabled input",
description: "Disable manual entry while keeping supported quantity controls available.",
},
{
eyebrow: "States",
title: "Disabled buttons",
description:
"Disable increment and decrement controls while leaving the current value visible.",
},
{
eyebrow: "Behaviour",
title: "Custom step size",
description: "Set a custom step so every button press changes the value by two.",
},
{
eyebrow: "Limits",
title: "Negative values",
description: "Use the min option to permit controlled negative values.",
},
{
eyebrow: "Limits",
title: "Maximum limit",
description: "Set an upper limit and disable incrementing when that limit is reached.",
},
{
eyebrow: "Validation",
title: "Validation states",
description: "Communicate whether the current numerical value is valid.",
},
];
const toggleCountUses = [
{
eyebrow: "Pricing",
title: "Monthly and annual pricing",
description: "Compare plan prices with an aligned segmented control and clear selected state.",
},
{
eyebrow: "Pricing",
title: "Switch control",
description: "Use a compact switch between labels while updating every displayed plan value.",
},
{
eyebrow: "Configuration",
title: "Custom billing periods",
description: "Configure custom values, labels, currency, suffixes, and plan descriptions.",
},
{
eyebrow: "States",
title: "Disabled pricing toggle",
description: "Keep the current comparison visible while preventing billing-period changes.",
},
];
function toggleCountConfiguration(variation) {
const sharedItems = [
{ label: "Startup", monthly: 19, annual: 190 },
{ label: "Team", monthly: 89, annual: 890 },
{ label: "Enterprise", monthly: 129, annual: 1290 },
];
const configurations = [
{
name: "pricing-cycle",
value: "annual",
variant: "segmented",
align: "end",
items: sharedItems,
},
{
name: "pricing-switch",
value: "monthly",
variant: "switch",
align: "center",
items: sharedItems,
},
{
name: "contract-length",
value: "quarterly",
firstValue: "quarterly",
firstLabel: "Quarterly",
secondValue: "yearly",
secondLabel: "Yearly",
firstValueKey: "quarterly",
secondValueKey: "yearly",
currency: "€",
suffix: "/seat",
items: [
{
label: "Core",
description: "For small product teams",
quarterly: 29,
yearly: 22,
},
{
label: "Scale",
description: "For growing organizations",
quarterly: 79,
yearly: 62,
},
],
},
{
name: "disabled-pricing-cycle",
value: "annual",
variant: "segmented",
align: "end",
items: sharedItems,
disabled: true,
},
];
return {
color: "primary",
size: "default",
...configurations[variation],
};
}
const basicFormComponentNames = new Set([
"Checkbox",
"ColorPicker",
"FileInput",
"Input",
"InputGroup",
"Radio",
"RangeSlider",
"Select",
"Switch",
"Textarea",
"TimePicker",
]);
const basicFormUses = [
{
eyebrow: "Basic usage",
title: "Default field",
description: "Use a labelled field with a value, placeholder, and supporting helper text.",
},
{
eyebrow: "Appearance",
title: "Outlined with start icon",
description: "Combine an outlined treatment with a leading icon.",
},
{
eyebrow: "Appearance",
title: "Floating label",
description: "Move the label into the control while preserving an accessible field name.",
},
{
eyebrow: "Appearance",
title: "Pill and compact size",
description: "Use pill styling and compact sizing in dense interfaces.",
},
{
eyebrow: "States",
title: "Read only",
description: "Display a submitted value without allowing it to be changed.",
},
{
eyebrow: "States",
title: "Disabled",
description: "Prevent interaction and communicate that the field is unavailable.",
},
{
eyebrow: "Layout",
title: "Inline helper text",
description: "Place the label, control, and supporting text on one responsive line.",
},
{
eyebrow: "Layout",
title: "Field with corner hint",
description: "Place optional context opposite the label and keep the control on the next line.",
},
{
eyebrow: "Validation",
title: "Validated field",
description:
"Surface validation through @wrnexus/validation-compatible error targets and accessible state.",
},
];
const radioUses = [
{
eyebrow: "Basic usage",
title: "Default radio",
description: "Use a single radio control for one selectable option.",
},
{
eyebrow: "States",
title: "Disabled",
description: "Disable an option when it is unavailable.",
},
{
eyebrow: "Groups",
title: "Inline radio group",
description: "Arrange compact radio options on one horizontal line.",
},
{
eyebrow: "Groups",
title: "Vertical radio group",
description: "Stack radio options for easy scanning.",
},
{
eyebrow: "Content",
title: "Radios with descriptions",
description: "Add supporting copy to clarify each choice.",
},
{
eyebrow: "Cards",
title: "Radio cards",
description: "Present options as selectable horizontal cards.",
},
{
eyebrow: "Cards",
title: "Vertical radio cards",
description: "Stack selectable cards in a vertical layout.",
},
{
eyebrow: "Alignment",
title: "Right-aligned radio",
description: "Place controls at the far edge of their labels.",
},
{
eyebrow: "Lists",
title: "Radio list group",
description: "Separate options with list dividers.",
},
{
eyebrow: "Lists",
title: "Horizontal radio list group",
description: "Combine list styling with a horizontal layout.",
},
{
eyebrow: "Validation",
title: "Validation states",
description: "Validate the selected group with @wrnexus/validation.",
},
];
const checkboxUses = [
{
eyebrow: "Basic usage",
title: "Default checkbox",
description: "Use a checkbox for an independent yes-or-no choice.",
},
{
eyebrow: "States",
title: "Indeterminate",
description: "Represent a partially selected group with the mixed state.",
},
{
eyebrow: "States",
title: "Disabled",
description: "Disable a checkbox when its choice is unavailable.",
},
{
eyebrow: "Groups",
title: "Checkbox group options",
description: "Render passed options as a named checkbox group.",
},
{
eyebrow: "Content",
title: "Checkboxes with descriptions",
description: "Add supporting copy to clarify each choice.",
},
{
eyebrow: "Cards",
title: "Checkbox cards",
description: "Present multiple choices as selectable horizontal cards.",
},
{
eyebrow: "Cards",
title: "Vertical checkbox cards",
description: "Stack selectable checkbox cards vertically.",
},
{
eyebrow: "Alignment",
title: "Right-aligned checkbox",
description: "Place checkbox controls at the far edge of their labels.",
},
{
eyebrow: "Lists",
title: "Checkbox list group",
description: "Separate checkbox options with list dividers.",
},
{
eyebrow: "Lists",
title: "Horizontal checkbox list group",
description: "Combine list styling with a horizontal layout.",
},
{
eyebrow: "Validation",
title: "Validation states",
description: "Validate required checkbox choices with @wrnexus/validation.",
},
];
const fileUploadProgressUses = [
{
eyebrow: "Basic usage",
title: "Uploading file",
description: "Show an active upload with file metadata and a cancel action.",
},
{
eyebrow: "Progress",
title: "Starting upload",
description: "Communicate that a newly queued file has begun uploading.",
},
{
eyebrow: "Progress",
title: "Nearly complete",
description: "Keep progress and transferred size readable near completion.",
},
{
eyebrow: "States",
title: "Upload complete",
description: "Confirm a successful upload with a clear completion state.",
},
{
eyebrow: "States",
title: "Upload failed",
description: "Explain failure and provide a retry action.",
},
{
eyebrow: "States",
title: "Upload cancelled",
description: "Preserve file context and allow a cancelled upload to restart.",
},
];
function fileUploadProgressConfiguration(variation) {
const configurations = [
{
fileName: "design-system.zip",
label: "Uploading archive",
uploadedSize: "8.4 MB",
fileSize: "20 MB",
value: 42,
max: 100,
status: "uploading",
},
{
fileName: "product-images.tar",
label: "Preparing upload",
uploadedSize: "640 KB",
fileSize: "48 MB",
value: 2,
max: 100,
status: "uploading",
},
{
fileName: "launch-video.mp4",
label: "Uploading video",
uploadedSize: "184 MB",
fileSize: "200 MB",
value: 92,
max: 100,
status: "uploading",
},
{
fileName: "quarterly-report.pdf",
label: "Uploaded successfully",
uploadedSize: "4.8 MB",
fileSize: "4.8 MB",
value: 100,
max: 100,
status: "complete",
},
{
fileName: "customer-export.csv",
label: "Network connection lost",
uploadedSize: "12 MB",
fileSize: "36 MB",
value: 33,
max: 100,
status: "error",
},
{
fileName: "brand-assets.fig",
label: "Upload cancelled",
uploadedSize: "16 MB",
fileSize: "72 MB",
value: 22,
max: 100,
status: "cancelled",
},
];
return configurations[variation] || configurations[0];
}
function basicFormConfiguration(componentName, variation) {
const validationVariation = componentName === "Radio" || componentName === "Checkbox" ? 10 : 8;
const common = {
id: `${baseSlugOf(componentName)}-${variation + 1}`,
name:
variation === validationVariation ? "validatedField" : `${baseSlugOf(componentName)}Field`,
label: displayName(componentName),
placeholder: `Enter ${displayName(componentName).toLowerCase()}`,
helperText:
variation === 0 || variation === 6
? variation === 6
? "We'll never share your details."
: "Helpful context for this field."
: "",
cornerHint: variation === 7 ? "Optional" : "",
variant:
variation === 1
? "outlined"
: variation === 2
? "floating"
: variation === 3
? "pill"
: "normal",
icon:
variation === 1
? "icon-[lucide--sparkles]"
: variation === 2
? "icon-[lucide--circle-user-round]"
: "",
iconPosition: variation === 2 ? "end" : "start",
size: variation === 3 ? "sm" : "default",
readonly: variation === 4,
disabled:
componentName === "Radio"
? variation === 1
: componentName === "Checkbox"
? variation === 2
: variation === 5,
inline: variation === 6,
required: variation === validationVariation,
error: "",
color: "primary",
};
const byComponent = {
Checkbox: {
value: "terms",
values: variation >= 3 ? ["product"] : [],
checked: variation === 0,
indeterminate: variation === 1,
label: variation < 3 ? "Accept notifications" : "Notification preferences",
options:
variation >= 3
? [
{
label: "Product updates",
value: "product",
description: variation >= 4 ? "News about features and improvements." : "",
},
{
label: "Account activity",
value: "account",
description: variation >= 4 ? "Important security and billing alerts." : "",
},
{
label: "Partner offers",
value: "partners",
description: variation >= 4 ? "Relevant offers from trusted partners." : "",
},
]
: [],
orientation: [3, 5, 9].includes(variation) ? "horizontal" : "vertical",
card: variation === 5 || variation === 6,
rightAligned: variation === 7,
list: variation === 8 || variation === 9,
helperText: variation === 10 ? "Select at least one notification type." : "",
},
ColorPicker: { value: "#2563eb", label: "Brand color" },
FileInput: {
accept: "image/png,image/jpeg",
label: "Profile image",
placeholder: "Choose an image",
},
Input: {
value: variation === 4 ? "readonly@example.com" : "",
label: "Email address",
type: "email",
autocomplete: "email",
},
InputGroup: {
value: variation === 4 ? "wirejs" : "",
label: "Workspace",
startText: "https://",
endText: ".app",
actionLabel: variation === 0 ? "Check" : "",
},
Radio: {
value: variation === 0 ? "standard" : "",
checked: variation === 0,
label: variation === 0 ? "Standard plan" : "Choose a plan",
options:
variation >= 2
? [
{
label: "Starter",
value: "starter",
description: variation >= 4 ? "For individuals and small projects." : "",
},
{
label: "Professional",
value: "professional",
description: variation >= 4 ? "For growing teams and products." : "",
},
{
label: "Enterprise",
value: "enterprise",
description: variation >= 4 ? "For organizations needing advanced controls." : "",
},
]
: [],
orientation: [2, 5, 9].includes(variation) ? "horizontal" : "vertical",
card: variation === 5 || variation === 6,
rightAligned: variation === 7,
list: variation === 8 || variation === 9,
helperText: variation === 10 ? "Select one plan to continue." : "",
},
RangeSlider: {
value: 60,
min: 0,
max: 100,
step: 5,
showBounds: true,
showSteps: variation === 1,
marks:
variation === 1
? [
{ value: 0, label: "0" },
{ value: 25, label: "25" },
{ value: 50, label: "50" },
{ value: 75, label: "75" },
{ value: 100, label: "100" },
]
: [],
label: "Volume",
},
Select: {
value: variation === 4 ? "design" : "",
label: "Team",
options: [
{ label: "Engineering", value: "engineering" },
{ label: "Design", value: "design" },
{ label: "Marketing", value: "marketing" },
],
},
Switch: { value: "enabled", checked: variation === 4, label: "Email notifications" },
Textarea: {
value: variation === 4 ? "This content is read only." : "",
label: "Description",
rows: 4,
},
TimePicker: {
value: variation === 4 ? "09:30" : "",
label: "Start time",
min: "08:00",
max: "18:00",
},
};
return { ...common, ...byComponent[componentName] };
}
function inputNumberConfiguration(variation) {
const configurations = [
{
name: "default-quantity",
value: 1,
min: 0,
max: 99,
step: 1,
variant: "default",
},
{
name: "labeled-quantity",
label: "Select quantity",
value: 1,
min: 1,
max: 25,
step: 1,
variant: "labeled",
},
{
name: "vertical-quantity",
label: "Select quantity",
value: 1,
min: 0,
max: 20,
variant: "vertical",
},
{
name: "horizontal-quantity",
value: 1,
min: 0,
max: 20,
variant: "horizontal",
},
{
name: "compact-quantity",
value: 0,
min: 0,
max: 10,
variant: "compact",
size: "sm",
},
{
name: "additional-seats",
label: "Additional seats",
description: "$39 monthly",
value: 0,
min: 0,
max: 50,
variant: "seat",
},
{
name: "disabled-input",
value: 10,
inputDisabled: true,
},
{
name: "disabled-buttons",
value: 10,
buttonsDisabled: true,
},
{
name: "custom-step",
value: 0,
min: 0,
max: 20,
step: 2,
},
{
name: "negative-values",
value: -10,
min: -100,
max: 100,
},
{
name: "maximum-limit",
value: 10,
min: 0,
max: 10,
help: "The maximum value is 10.",
},
{
name: "invalid-quantity",
value: 10,
min: 0,
max: 5,
error: "Out of limit",
},
];
return {
color: "primary",
size: "md",
...configurations[variation],
};
}
function demoUses(component) {
if (component.name === "FileUploadProgress") return fileUploadProgressUses;
if (component.name === "Checkbox") return checkboxUses;
if (component.name === "Radio") return radioUses;
if (basicFormComponentNames.has(component.name)) return basicFormUses;
switch (component.name) {
case "Accordion":
return accordionUses;
case "Alert":
return alertUses;
case "Avatar":
return avatarUses;
case "AvatarGroup":
return avatarGroupUses;
case "Badge":
return badgeUses;
case "Button":
return buttonUses;
case "ButtonGroup":
return buttonGroupUses;
case "Card":
return cardUses;
case "Carousel":
return carouselUses;
case "ChatBubble":
return chatBubbleUses;
case "Collapse":
return collapseUses;
case "AdvancedSelect":
return advancedSelectUses;
case "ComboBox":
return comboboxUses;
case "InputNumber":
return inputNumberUses;
case "ToggleCount":
return toggleCountUses;
case "PinInput":
return pinInputUses;
case "StrongPassword":
return strongPasswordUses;
case "TogglePassword":
return togglePasswordUses;
default:
return defaultUses;
}
}
function eventDocumentation(component) {
const descriptions = {
action: "Fires when the component's action is selected.",
avatarClick: "Fires when a message avatar is selected.",
add: "Fires when an item is added.",
blur: "Fires when focus leaves the component.",
cancel: "Fires when the current operation is cancelled.",
change: "Fires when the component value or selection changes.",
clear: "Fires after the current value or collection is cleared.",
click: "Fires when the component is activated by pointer or keyboard.",
close: "Fires when the component closes.",
complete: "Fires when the component's operation completes.",
error: "Fires when the component encounters an error.",
focus: "Fires when the component receives focus.",
input: "Fires immediately as the component value changes.",
load: "Fires when component data or media finishes loading.",
linkClick: "Fires when a link inside a message is selected.",
messageClick: "Fires when a message bubble is selected.",
open: "Fires when the component opens.",
progress: "Fires as an operation reports progress.",
search: "Fires when the search query changes or is submitted.",
select: "Fires when an item or option is selected.",
success: "Fires when the component operation succeeds.",
submit: "Fires when the component submits its value.",
toggle: "Fires when the component toggles between states.",
};
const events = component.events.map((name) => [
name,
descriptions[name] ?? `Fires when the component emits the ${displayName(name)} event.`,
]);
const emptyState =
' No public component events.
';
const eventsList = events.length
? `${events.map(([name, description]) => `
@${name}${description}
`).join("")}
`
: emptyState;
const listenerExample = events.length
? `${escapeCode(`const component = document.querySelector('[data-wrn-events]')
component.addEventListener('${events[0][0]}', (event) => {
console.log(event.detail)
})`)} `
: "";
return `Component events Events Public events declared by this component. Custom events bubble from the component root and expose state through event.detail.
${eventsList}${listenerExample}
`;
}
function detailUseCaseLinks(component, uses) {
const link = (index) =>
`${escapeText(uses[index].title)} `;
if (component.name === "ButtonGroup") {
return `Basic usage ${link(0)}Sizes ${link(1)}${link(2)}${link(3)}${link(4)}${link(5)}${link(6)}`;
}
if (component.name === "Card") {
return `Basic usage ${link(0)}Content ${link(1)}${link(2)}Structure ${link(3)}${link(4)}Sizes ${link(5)}${link(6)}${link(7)}Navigation ${link(8)}${link(9)}Images ${link(10)}${link(11)}${link(12)}Animations ${link(13)}${link(14)}Card layout ${link(15)}${link(16)}Panels ${link(17)}${link(18)}${link(19)}${link(20)}${link(21)}${link(22)}`;
}
if (component.name === "Carousel") {
return `Basic usage ${link(0)}${link(1)}Playback ${link(2)}${link(3)}Direction ${link(4)}Layouts ${link(5)}${link(6)}${link(7)}${link(8)}${link(9)}Status ${link(10)}Thumbnails ${link(11)}${link(12)}`;
}
return uses
.map(
(useCase, variation) =>
`${escapeText(useCase.title)} `,
)
.join("");
}
function detailPage(component, categoryComponents) {
const index = categoryComponents.findIndex((item) => item.name === component.name);
const previous = categoryComponents[index - 1];
const next = categoryComponents[index + 1];
const uses = demoUses(component);
const demos = uses
.map((useCase, variation) => {
const isPinLengthDemo = component.name === "PinInput" && variation === 1;
const pinLengths = [3, 5, 7];
const mount = isPinLengthDemo
? `${pinLengths
.map((length) =>
componentMount(component, variation, {
label: `${length}-digit code`,
name: `pin-length-${length}`,
length,
}),
)
.join("")}
`
: componentMount(component, variation);
const snippet = isPinLengthDemo
? pinLengths
.map((length) =>
sourceSnippet(component, variation, {
label: `${length}-digit code`,
name: `pinLength${length}`,
length,
}),
)
.join("\n\n")
: sourceSnippet(component, variation);
const validationSchema =
basicFormComponentNames.has(component.name) &&
variation === (component.name === "Radio" || component.name === "Checkbox" ? 10 : 8)
? "basic-form-validation"
: component.name === "AdvancedSelect" && variation === 25
? "advanced-select-validation"
: component.name === "AdvancedSelect" && variation === 26
? "advanced-select-dynamic-validation"
: component.name === "ComboBox" && variation === 14
? "combobox-validation"
: component.name === "PinInput" && variation === 9
? "pin-input-validation"
: component.name === "TogglePassword" && variation === 5
? "toggle-password-validation"
: "";
const preview = validationSchema
? ``
: mount;
return `
`;
})
.join("");
return `// Generated by scripts/generate-showcase.mjs. Do not edit directly.
page ${component.name.replace(/[^A-Za-z0-9_]/g, "")}Detail {
layout = "showcase"
${playgroundStates(component)}
seo {
title = "${escapeAttribute(displayName(component.name))}"
description = "${escapeAttribute(component.purpose)}"
}
view {
Components / ${titleCase(component.category)} / ${escapeText(displayName(component.name))}
${playgroundSection(component)}
Live examples Designed for real product surfaces Compare configurations and resize the browser to check responsive behavior.
${demos}
${eventDocumentation(component)}
Component API Props and configuration All content and behavior shown above is supplied through these props and slots.
${propsTable(component)}
}
}
`;
}
function categoryPage(category, components) {
const liveDemoCount = components.reduce(
(total, component) => total + demoUses(component).length,
0,
);
const cards = components
.map(
(component) => `
${componentMount(component, 0)}
${titleCase(category)} ${escapeText(displayName(component.name))} ${escapeText(component.purpose)}
`,
)
.join("");
return `// Generated by scripts/generate-showcase.mjs. Do not edit directly.
page ${identifier(category)}Showcase {
layout = "showcase"
seo {
title = "${titleCase(category)} components"
description = "${components.length} ${category} components from @wrnexus/ui."
}
view {
Component category
${titleCase(category)}
${components.length} unique, responsive, theme-aware components. Open a component to inspect multiple live configurations and its complete props API.
${components.length} components Multiple use cases ${liveDemoCount} live configurations
${cards}
}
}
`;
}
const categories = Object.groupBy(reference.components, (component) => component.category);
const categoryEntries = Object.entries(categories).sort(([left], [right]) =>
left.localeCompare(right),
);
const totalDemoCount = reference.components.reduce(
(total, component) => total + demoUses(component).length,
0,
);
const completedComponents = new Set([
"Accordion",
"Alert",
"Avatar",
"AvatarGroup",
"Badge",
"Blockquote",
"Button",
"ButtonGroup",
"Card",
"Carousel",
"ChatBubble",
"Collapse",
"Checkbox",
"DatePicker",
"DeviceFrame",
"FileUploadProgress",
"ColorPicker",
"FileInput",
"Input",
"InputGroup",
"Radio",
"RangeSlider",
"Select",
"Switch",
"Textarea",
"TimePicker",
"AdvancedSelect",
"ComboBox",
"InputNumber",
"PinInput",
"StrongPassword",
"ToggleCount",
"TogglePassword",
]);
const workingComponents = new Set([]);
function componentStatus(componentName) {
if (completedComponents.has(componentName)) return "complete";
if (workingComponents.has(componentName)) return "working";
return "pending";
}
function showcaseLayout() {
const directory = categoryEntries
.map(
([category, components]) => `
`,
)
.join("");
return `// Generated by scripts/generate-showcase.mjs. Do not edit directly.
layout Showcase {
view {
${directory}
No components found.
}
}
`;
}
function documentLayout() {
return `// Generated by scripts/generate-showcase.mjs. Do not edit directly.
layout Document {
props {
cookies = {}
theme = "light"
language = "en"
url = ""
pathname = "/"
}
view {
}
}
`;
}
mkdirSync(pagesDir, { recursive: true });
mkdirSync(detailPagesDir, { recursive: true });
mkdirSync(layoutsDir, { recursive: true });
writeIfChanged(join(layoutsDir, "showcase.wrn"), showcaseLayout());
writeIfChanged(join(layoutsDir, "document.wrn"), documentLayout());
const generatedPages = new Set(["index.wrn", "docs.wrn", "blocks.wrn", "templates.wrn"]);
const generatedDetailPages = new Set();
for (const [category, components] of categoryEntries) {
generatedPages.add(`${category}.wrn`);
writeIfChanged(join(pagesDir, `${category}.wrn`), categoryPage(category, components));
for (const component of components) {
const detailFile = `${slugOf(component.name)}.wrn`;
generatedDetailPages.add(detailFile);
writeIfChanged(join(detailPagesDir, detailFile), detailPage(component, components));
}
}
const categoryCards = categoryEntries
.map(
([category, components], index) =>
`0${index + 1} ${titleCase(category)} ${components.length} components · ${components.reduce((total, component) => total + demoUses(component).length, 0)} live demos
`,
)
.join("");
writeIfChanged(
join(pagesDir, "index.wrn"),
`// Generated by scripts/generate-showcase.mjs. Do not edit directly.
page ComponentShowcase {
layout = "showcase"
seo {
title = "WRNexus component system"
description = "Explore every unique @wrnexus/ui component through live, production-quality examples."
}
view {
The UI foundation for WRNexus
Build modern productsat framework speed.
A complete, accessible component system with ${reference.count} primitives, ready-made blocks, page templates, responsive behavior, and deeply configurable design tokens.
$ bun add @wrnexus/ui
${reference.count} Unique components
${totalDemoCount} Live configurations
0 Duplicate implementations
${categoryEntries.length} Focused categories
Explore the system
Everything your product needs ${categoryCards}
}
}
`,
"utf8",
);
const docsSections = [
[
"installation",
"Installation",
"Install the package and import the shared stylesheet once in your application.",
"bun add @wrnexus/ui",
],
[
"framework-guides",
"Framework guides",
"WRNexus components use native HTML semantics and framework component mounts.",
],
[
"accessibility",
"Accessibility",
"Keyboard interaction, visible focus states, labels, and reduced-motion support ship by default.",
],
[
"dark-mode",
"Dark mode",
"Choose light, dark, or system mode. The selection persists and updates every preview.",
],
[
"themes",
"Themes",
"Theme presets control radius, surface treatment, borders, shadows, and motion.",
],
[
"colors",
"Colors",
"Semantic primary, secondary, success, warning, danger, and info tokens remain available in every palette.",
],
[
"fonts",
"Fonts",
"Select a font family globally while components continue to inherit typography correctly.",
],
[
"sizing",
"Sizing",
"Compact through large scales adjust the complete component system consistently.",
],
[
"resources",
"Resources",
"Use the component API pages, blocks, and templates as production starting points.",
],
];
writeIfChanged(
join(pagesDir, "docs.wrn"),
`page Documentation {
layout = "showcase"
seo { title = "Documentation" description = "Install, configure, and use WRNexus UI." }
view {
Explore the documentation Every Getting Started and Customization topic now has its own page. Choose a guide from the sidebar to continue.
}
}
`,
"utf8",
);
const customizationGuides = new Set(["dark-mode", "themes", "colors", "fonts", "sizing"]);
for (const [slug, title, description, code] of docsSections) {
generatedPages.add(`${slug}.wrn`);
const section = customizationGuides.has(slug) ? "Customization" : "Getting Started";
const examples =
slug === "colors"
? [
["Brand colors", "Primary and secondary tokens communicate brand hierarchy."],
[
"Status colors",
"Success, warning, danger, and info preserve their semantic meaning in every palette.",
],
[
"Component override",
'Pass color="success" or another semantic color to any component.',
],
]
: slug === "sizing"
? [
["Component size", "Every component accepts xs, sm, default, md, lg, and xl."],
[
"Global density",
"Compact, default, comfortable, and large scale the complete interface.",
],
["Responsive sizing", "Component sizing cooperates with responsive breakpoints."],
]
: slug === "themes"
? [
["Default", "Balanced surfaces, borders, elevation, and radius."],
["Soft and sharp", "Choose friendly rounded surfaces or dense operational styling."],
["Glass", "Use translucent layered surfaces while retaining semantic tokens."],
]
: [
["Overview", description],
["Configuration", "Use the design switcher in the header to test this setting live."],
[
"Component behavior",
"The setting is inherited by components, blocks, templates, and playground previews.",
],
];
writeIfChanged(
join(pagesDir, `${slug}.wrn`),
`page ${identifier(slug)}Guide {
layout = "showcase"
seo {
title = "${escapeAttribute(title)}"
description = "${escapeAttribute(description)}"
}
view {
Docs / ${escapeText(title)}
${examples.map(([heading, copy]) => `${escapeText(heading)} ${escapeText(copy)}
`).join("")}
${code ? `Quick start ${escapeCode(code)} ` : ""}
Explore components → Browse blocks →
}
}
`,
"utf8",
);
}
const blockCards = [
[
"Hero sections",
"Marketing",
"layout-template",
"Hero, actions, product proof, and visual stage.",
],
[
"Feature grids",
"Marketing",
"grid-2x2",
"Responsive benefits and product capability sections.",
],
["Pricing sections", "Marketing", "badge-dollar-sign", "Tier cards, comparison grids, and CTAs."],
[
"Application shells",
"Application UI",
"panel-left",
"Headers, sidebars, content, and command areas.",
],
[
"Dashboard stats",
"Application UI",
"chart-no-axes-combined",
"Metrics, trends, filters, and summaries.",
],
["Checkout panels", "Ecommerce", "shopping-cart", "Cart, address, payment, and order summary."],
[
"Article layouts",
"Blog & Articles",
"newspaper",
"Editorial headers, content, author, and sharing.",
],
[
"Authentication",
"Application UI",
"shield-check",
"Sign in, registration, recovery, and verification.",
],
];
writeIfChanged(
join(pagesDir, "blocks.wrn"),
`page Blocks {
layout = "showcase"
seo { title = "UI blocks" description = "Complete sections composed from WRNexus UI components." }
view {
All blocks Marketing Application UI Ecommerce Blog & Articles
${blockCards.map(([title, category, icon, description], index) => `
`).join("")}
}
}
`,
"utf8",
);
const templates = [
[
"Admin dashboard",
"Dashboard & Admin",
"31 pages",
"Analytics, teams, settings, billing, and operational workflows.",
],
[
"Commerce storefront",
"Ecommerce",
"18 pages",
"Catalog, product, cart, checkout, and customer account pages.",
],
[
"SaaS website",
"Websites",
"12 pages",
"Landing, pricing, about, blog, contact, and legal pages.",
],
[
"Support center",
"Support",
"9 pages",
"Knowledge base, ticketing, status, and customer help experiences.",
],
[
"Finance workspace",
"Finance & Payments",
"14 pages",
"Accounts, transactions, reporting, transfers, and controls.",
],
];
writeIfChanged(
join(pagesDir, "templates.wrn"),
`page Templates {
layout = "showcase"
seo { title = "Page templates" description = "Complete pages assembled from WRNexus UI blocks." }
view {
All templates Dashboards & Admin Ecommerce Websites Support Finance
${templates.map(([title, category, count], index) => `
${count} ${title} ${category} template with consistent navigation, content, forms, and responsive behavior.
Preview →
`).join("")}
}
}
`,
"utf8",
);
for (const file of readdirSync(pagesDir).filter((entry) => entry.endsWith(".wrn"))) {
if (!generatedPages.has(file)) unlinkSync(join(pagesDir, file));
}
for (const file of readdirSync(detailPagesDir).filter((entry) => entry.endsWith(".wrn"))) {
if (!generatedDetailPages.has(file)) unlinkSync(join(detailPagesDir, file));
}
process.stdout.write(
`Generated ${reference.count} component detail pages, ${totalDemoCount} live demos, and ${categoryEntries.length} category pages.\n`,
);