This repository has been archived on 2026-06-07. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
master-ai/vibn-frontend/components/project/design-kit-panels.tsx

671 lines
24 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"use client";
/**
* Visual panels for the design kit explorer — all driven by ResolvedKitTokens
* so previews stay in sync with the users starter kit + overrides.
*/
import type { CSSProperties, FC, ReactNode } from "react";
import type { ResolvedKitTokens } from "@/lib/design-kits/resolve";
export interface KitSectionItemMeta {
id: string;
title: string;
subtitle?: string;
panelKey: string;
}
export interface KitSectionMeta {
title: string;
items: KitSectionItemMeta[];
}
export const DESIGN_KIT_SECTIONS: KitSectionMeta[] = [
{
title: "Type",
items: [
{
id: "t-fw",
title: "Font Weights & Colors",
subtitle: "Weights + semantic text roles (starter font)",
panelKey: "fontWeights",
},
{
id: "t-scale",
title: "Type Scale",
subtitle: "Pixel/rem steps matched to kit density",
panelKey: "typeScale",
},
],
},
{
title: "Colors",
items: [
{
id: "c-accent",
title: "Accent scale",
subtitle: "12-step ramp from your accent · CTAs, focus, links",
panelKey: "accentScale",
},
{
id: "c-gray",
title: "Gray Scale",
subtitle: "12-step neutral — surfaces, borders, text",
panelKey: "grayScale",
},
{
id: "c-semantic",
title: "Semantic Colors",
subtitle: "Status & tag hues (reference palette)",
panelKey: "semanticColors",
},
],
},
{
title: "Spacing",
items: [
{ id: "s-radius", title: "Border Radius", subtitle: "Derived from radius token", panelKey: "radius" },
{ id: "s-shadow", title: "Box Shadows", subtitle: "Elevation system + focus line", panelKey: "shadows" },
{ id: "s-space", title: "Spacing Tokens", subtitle: "4px base unit spacing scale", panelKey: "spacing" },
],
},
{
title: "Components",
items: [
{ id: "x-btn", title: "Buttons", subtitle: "Primary · accent · danger · ghost", panelKey: "buttons" },
{ id: "x-input", title: "Input Fields", subtitle: "Default · focus · error · disabled", panelKey: "inputs" },
{ id: "x-nav", title: "Navigation Items", subtitle: "Sidebar density preview", panelKey: "nav" },
{ id: "x-tag", title: "Tag Colors", subtitle: "Status chips", panelKey: "tags" },
],
},
{
title: "Brand",
items: [
{ id: "b-icon", title: "Illustration Icons", subtitle: "Placeholder grid · swap with assets", panelKey: "illustrations" },
{ id: "b-logo", title: "Logo & Brand", subtitle: "Mark preview uses accent-on-neutral", panelKey: "logo" },
],
},
];
const codeSm: CSSProperties = {
fontFamily: "var(--font-ibm-plex-mono), ui-monospace, monospace",
fontSize: "0.68rem",
color: "#52525b",
};
const panelMiniTitle: CSSProperties = {
fontSize: "0.65rem",
fontWeight: 700,
letterSpacing: "0.06em",
color: "#a1a1aa",
marginBottom: 10,
};
const inputBase: CSSProperties = {
width: "100%",
boxSizing: "border-box",
padding: "10px 12px",
borderRadius: 8,
border: "1px solid #e4e4e7",
fontSize: "0.82rem",
fontFamily: "inherit",
background: "#fff",
};
function densityPad(tokens: ResolvedKitTokens, base: number): number {
return tokens.density === "compact" ? Math.round(base * 0.88) : base;
}
function SwatchRow({ colors, labels }: { colors: string[]; labels?: Record<number, string> }) {
return (
<div style={{ display: "flex", flexWrap: "wrap", gap: 6, alignItems: "flex-end" }}>
{colors.map((c, i) => (
<div key={i} style={{ textAlign: "center", width: 52 }}>
<div style={{ height: 40, borderRadius: 6, background: c, border: "1px solid #e4e4e7" }} />
<div style={{ fontSize: "0.62rem", color: "#71717a", marginTop: 4 }}>{labels?.[i] ?? i + 1}</div>
</div>
))}
</div>
);
}
export function FontWeightsPanel({ tokens }: { tokens: ResolvedKitTokens }) {
return (
<div style={{ fontFamily: tokens.fontFamily, display: "grid", gridTemplateColumns: "1fr 1fr", gap: 20 }}>
<div>
<div style={panelMiniTitle}>Font weights</div>
{[
["400", "Regular — body, field values"],
["500", "Medium — buttons, nav, labels"],
["600", "Semibold — headings, CTAs"],
].map(([w, desc]) => (
<div key={w} style={{ marginBottom: densityPad(tokens, 12) }}>
<div style={{ fontWeight: Number(w), fontSize: "0.95rem", color: "#18181b" }}>Aa {w}</div>
<div style={{ fontSize: "0.72rem", color: "#71717a" }}>{desc}</div>
</div>
))}
</div>
<div>
<div style={panelMiniTitle}>Semantic text colors</div>
{[
["primary", "#18181b", "Headings, record names"],
["secondary", "#52525b", "Body, descriptions"],
["tertiary", "#a1a1aa", "Labels, hints"],
["accent", tokens.accentHex, "Links & emphasis"],
["danger", "#dc2626", "Errors"],
].map(([role, hex, desc]) => (
<div key={role} style={{ marginBottom: 10, display: "flex", alignItems: "baseline", gap: 10 }}>
<span style={{ fontSize: "0.72rem", width: 72, color: "#71717a" }}>{role}</span>
<span style={{ fontSize: "0.88rem", color: hex, fontWeight: 500 }}>Sample</span>
<span style={{ fontSize: "0.7rem", color: "#a1a1aa", flex: 1 }}>{desc}</span>
</div>
))}
</div>
</div>
);
}
export function TypeScalePanel({ tokens }: { tokens: ResolvedKitTokens }) {
const scale =
tokens.density === "compact"
? [
{ token: "--font-size-xxl", rem: "1.65rem", sample: "Hero title", note: "Hero" },
{ token: "--font-size-xl", rem: "1.38rem", sample: "Page title", note: "Page" },
{ token: "--font-size-lg", rem: "1.12rem", sample: "Section heading", note: "Section" },
{ token: "--font-size-md", rem: "0.94rem", sample: "Body — primary interface size", note: "Body" },
{ token: "--font-size-sm", rem: "0.85rem", sample: "Secondary label", note: "Meta" },
{ token: "--font-size-xs", rem: "0.78rem", sample: "Caption · nav", note: "Dense" },
]
: [
{ token: "--font-size-xxl", rem: "1.85rem", sample: "The Open-Source CRM", note: "Hero titles" },
{ token: "--font-size-xl", rem: "1.54rem", sample: "People & Companies", note: "Section titles" },
{ token: "--font-size-lg", rem: "1.23rem", sample: "Section heading", note: "Card headers" },
{ token: "--font-size-md", rem: "1rem", sample: "Body text — primary interface size", note: "Default UI copy" },
{ token: "--font-size-sm", rem: "0.92rem", sample: "Secondary label · timestamp", note: "Meta" },
{ token: "--font-size-xs", rem: "0.85rem", sample: "Caption · nav item", note: "Dense UI" },
];
const rowPad = densityPad(tokens, 12);
return (
<div style={{ fontFamily: tokens.fontFamily, display: "flex", flexDirection: "column", gap: 0 }}>
{scale.map((r) => (
<div
key={r.token}
style={{
display: "grid",
gridTemplateColumns: "160px 1fr",
gap: 16,
padding: `${rowPad}px 0`,
borderBottom: "1px solid #f4f4f5",
}}
>
<div style={{ display: "flex", flexDirection: "column", gap: 4 }}>
<code style={codeSm}>{r.token}</code>
<span style={{ color: "#a1a1aa", fontSize: "0.7rem" }}>{r.rem}</span>
</div>
<div style={{ display: "flex", flexDirection: "column", justifyContent: "center" }}>
<span style={{ fontSize: r.rem, fontWeight: 500, color: "#18181b" }}>{r.sample}</span>
<span style={{ fontSize: "0.7rem", color: "#a1a1aa", marginTop: 4 }}>{r.note}</span>
</div>
</div>
))}
</div>
);
}
export function AccentScalePanel({ tokens }: { tokens: ResolvedKitTokens }) {
const a = tokens.accentScale;
const md = tokens.radiusMdPx;
return (
<div style={{ fontFamily: tokens.fontFamily }}>
<SwatchRow colors={a} labels={{ 4: "primary", 8: "brand", 10: "text" }} />
<div style={{ display: "flex", flexWrap: "wrap", gap: 10, marginTop: 16 }}>
<button
type="button"
style={{
padding: "8px 14px",
borderRadius: md,
background: a[8] ?? tokens.accentHex,
color: "#fff",
border: "none",
fontSize: "0.8rem",
fontWeight: 500,
}}
>
Primary CTA
</button>
<button
type="button"
style={{
padding: "8px 14px",
borderRadius: md,
background: a[2],
color: a[10],
border: `1px solid ${a[6]}`,
fontSize: "0.8rem",
fontWeight: 500,
}}
>
Secondary
</button>
<span style={{ fontSize: "0.8rem", color: a[8], textDecoration: "underline", alignSelf: "center" }}>
Link · accent {tokens.accentHex}
</span>
</div>
</div>
);
}
export function GrayScalePanel({ tokens }: { tokens: ResolvedKitTokens }) {
return (
<div style={{ fontFamily: tokens.fontFamily }}>
<SwatchRow colors={tokens.grayScale} />
<p style={{ fontSize: "0.72rem", color: "#71717a", marginTop: 12, lineHeight: 1.55 }}>
Neutrals for surfaces & typography pair with your accent ramp above.
</p>
</div>
);
}
export function SemanticColorsPanel({ tokens }: { tokens: ResolvedKitTokens }) {
const named = [
"red",
"orange",
"yellow",
"grass",
"green",
"cyan",
"blue",
"purple",
"crimson",
"amber",
"lime",
"jade",
"mint",
"iris",
"plum",
"pink",
];
const hues = [
"#ef4444",
"#f97316",
"#eab308",
"#84cc16",
"#22c55e",
"#06b6d4",
"#3b82f6",
"#a855f7",
"#e11d48",
"#f59e0b",
"#65a30d",
"#059669",
"#14b8a6",
"#6366f1",
"#9333ea",
"#ec4899",
];
return (
<div style={{ fontFamily: tokens.fontFamily }}>
<div style={{ display: "grid", gridTemplateColumns: "repeat(8, 1fr)", gap: 8 }}>
{named.map((n, i) => (
<div key={n} style={{ textAlign: "center" }}>
<div style={{ height: 36, borderRadius: tokens.radiusSm, background: hues[i] }} />
<div style={{ fontSize: "0.62rem", color: "#71717a", marginTop: 4 }}>{n}</div>
</div>
))}
</div>
<div style={{ display: "flex", gap: 8, marginTop: 14, flexWrap: "wrap" }}>
{[
["Danger", "#fef2f2", "#dc2626"],
["Success", "#f0fdf4", "#16a34a"],
["Warning", "#fffbeb", "#ca8a04"],
["Info", "#eff6ff", "#2563eb"],
].map(([label, bg, fg]) => (
<span
key={label}
style={{
padding: "4px 10px",
borderRadius: 999,
background: bg as string,
color: fg as string,
fontSize: "0.72rem",
fontWeight: 600,
}}
>
{label}
</span>
))}
</div>
<p style={{ fontSize: "0.7rem", color: "#a1a1aa", marginTop: 12 }}>Reference hues for tags & status (not overridden by accent).</p>
</div>
);
}
export function RadiusPanel({ tokens }: { tokens: ResolvedKitTokens }) {
const rows = [
{ id: "radius-xs", px: tokens.radiusXs, use: "micro chips" },
{ id: "radius-sm", px: tokens.radiusSm, use: "inputs, buttons" },
{ id: "radius-md", px: tokens.radiusMdPx, use: "cards, dropdowns" },
{ id: "radius-xl", px: tokens.radiusXl, use: "large panels" },
{ id: "radius-xxl", px: tokens.radiusXxl, use: "hero surfaces" },
{ id: "radius-pill", px: 999, use: "tags" },
{ id: "radius-full", px: 100, use: "avatars" },
];
const fill = tokens.accentScale[3] ?? "#eceeff";
return (
<div style={{ display: "flex", flexWrap: "wrap", gap: 14, alignItems: "flex-end", fontFamily: tokens.fontFamily }}>
{rows.map((x) => (
<div key={x.id} style={{ textAlign: "center", width: 72 }}>
<div
style={{
width: 56,
height: 40,
margin: "0 auto",
background: fill,
borderRadius: x.px >= 100 ? "50%" : x.px,
}}
/>
<div style={{ fontSize: "0.62rem", fontWeight: 600, marginTop: 6, color: "#3f3f46" }}>{x.id}</div>
<div style={{ fontSize: "0.62rem", color: "#a1a1aa" }}>{x.use}</div>
</div>
))}
</div>
);
}
export function ShadowsPanel({ tokens }: { tokens: ResolvedKitTokens }) {
const focus = tokens.accentHex;
const cards = [
{ id: "shadow-light", desc: "Buttons, rows", shadow: "0 1px 2px rgba(0,0,0,0.05)" },
{ id: "shadow-strong", desc: "Dropdowns", shadow: "0 4px 16px rgba(0,0,0,0.08)" },
{ id: "shadow-super-heavy", desc: "Modals", shadow: "0 12px 48px rgba(0,0,0,0.12)" },
{ id: "shadow-underline", desc: "Focus line", shadow: `inset 0 -2px 0 ${focus}` },
];
return (
<div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fill, minmax(140px, 1fr))", gap: 12, fontFamily: tokens.fontFamily }}>
{cards.map((c) => (
<div key={c.id} style={{ padding: 14, borderRadius: tokens.radiusMdPx, background: "#fff", boxShadow: c.shadow, border: "1px solid #f4f4f5" }}>
<div style={{ fontSize: "0.72rem", fontWeight: 600, color: "#18181b" }}>{c.id}</div>
<div style={{ fontSize: "0.65rem", color: "#71717a", marginTop: 6 }}>{c.desc}</div>
</div>
))}
</div>
);
}
export function SpacingPanel({ tokens }: { tokens: ResolvedKitTokens }) {
const bar = tokens.accentScale[5] ?? "#9aa6ff";
const steps = [
[1, 4, "sibling gap min"],
[2, 8, "button padding"],
[3, 12, "sidebar gap"],
[4, 16, "section padding"],
[6, 24, "card padding"],
[8, 32, "large gap"],
[12, 48, "table row"],
[16, 64, "section spacer"],
];
return (
<div style={{ fontFamily: tokens.fontFamily }}>
<div style={{ fontSize: "0.65rem", fontWeight: 700, letterSpacing: "0.06em", color: "#a1a1aa", marginBottom: 12 }}>
SPACING 4PX BASE · space(n) = n × 4px
</div>
{steps.map(([n, px, note]) => (
<div key={n} style={{ display: "flex", alignItems: "center", gap: 12, marginBottom: 8 }}>
<code style={{ ...codeSm, width: 72 }}>space({n})</code>
<span style={{ fontSize: "0.72rem", color: "#71717a", width: 36 }}>{px}px</span>
<div style={{ flex: 1, height: 8, background: "#f4f4f5", borderRadius: 4, overflow: "hidden" }}>
<div style={{ width: Math.min((px as number) * 2, 200), height: "100%", background: bar }} />
</div>
<span style={{ fontSize: "0.72rem", color: "#71717a", flex: 1 }}>{note}</span>
</div>
))}
</div>
);
}
export function ButtonsPanel({ tokens }: { tokens: ResolvedKitTokens }) {
const a = tokens.accentScale;
const md = tokens.radiusMdPx;
const accent = a[8] ?? tokens.accentHex;
return (
<div style={{ display: "flex", flexDirection: "column", gap: 14, fontFamily: tokens.fontFamily }}>
<div style={panelMiniTitle}>Primary</div>
<div style={{ display: "flex", flexWrap: "wrap", gap: 8 }}>
<button type="button" style={{ padding: "8px 16px", borderRadius: md, background: "#18181b", color: "#fff", border: "none", fontSize: "0.8rem" }}>
Continue
</button>
<button type="button" style={{ padding: "8px 16px", borderRadius: md, background: accent, color: "#fff", border: "none", fontSize: "0.8rem" }}>
Accent
</button>
<button type="button" style={{ padding: "8px 16px", borderRadius: md, background: "#dc2626", color: "#fff", border: "none", fontSize: "0.8rem" }}>
Danger
</button>
<button type="button" disabled style={{ padding: "8px 16px", borderRadius: md, background: "#e4e4e7", color: "#a1a1aa", border: "none", fontSize: "0.8rem" }}>
Disabled
</button>
</div>
<div style={panelMiniTitle}>Secondary · outlined</div>
<div style={{ display: "flex", flexWrap: "wrap", gap: 8 }}>
<button type="button" style={{ padding: "8px 16px", borderRadius: md, background: "#fff", border: "1px solid #e4e4e7", fontSize: "0.8rem" }}>
Default
</button>
<button type="button" style={{ padding: "8px 16px", borderRadius: md, background: "#fff", border: `1px solid ${accent}`, color: accent, fontSize: "0.8rem" }}>
Accent outline
</button>
</div>
<div style={panelMiniTitle}>Tertiary · text</div>
<div style={{ display: "flex", gap: 16 }}>
<button type="button" style={{ background: "none", border: "none", fontSize: "0.8rem", color: "#18181b", cursor: "pointer" }}>
Cancel
</button>
<button type="button" style={{ background: "none", border: "none", fontSize: "0.8rem", color: accent, cursor: "pointer" }}>
Save changes
</button>
</div>
</div>
);
}
function Field({ label, children }: { label: string; children: ReactNode }) {
return (
<div>
<div style={{ fontSize: "0.68rem", fontWeight: 500, color: "#71717a", marginBottom: 6 }}>{label}</div>
{children}
</div>
);
}
export function InputsPanel({ tokens }: { tokens: ResolvedKitTokens }) {
const accent = tokens.accentHex;
const md = tokens.radiusMdPx;
return (
<div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fill, minmax(168px, 1fr))", gap: 14, fontFamily: tokens.fontFamily }}>
<Field label="Default">
<input placeholder="Type something…" style={{ ...inputBase, borderRadius: md }} />
</Field>
<Field label="Focused">
<input defaultValue="Acme Corporation" style={{ ...inputBase, borderRadius: md, outline: `2px solid ${accent}`, borderColor: accent }} />
</Field>
<Field label="Error">
<input defaultValue="invalid@" style={{ ...inputBase, borderRadius: md, borderColor: "#fca5a5", boxShadow: "0 0 0 3px rgba(252,165,165,0.35)" }} />
</Field>
<Field label="Disabled">
<input defaultValue="Read only" disabled style={{ ...inputBase, borderRadius: md, opacity: 0.55, background: "#fafafa" }} />
</Field>
<Field label="Search">
<input placeholder="Search…" style={{ ...inputBase, borderRadius: md }} />
</Field>
<Field label="Multiline">
<textarea placeholder="Add a note…" rows={3} style={{ ...inputBase, borderRadius: md, resize: "vertical" }} />
</Field>
</div>
);
}
export function NavPanel({ tokens }: { tokens: ResolvedKitTokens }) {
const rs = tokens.radiusSm;
const pad = tokens.density === "compact" ? 6 : 8;
const Item = ({ active, children }: { active?: boolean; children: ReactNode }) => (
<div
style={{
padding: `${pad}px 10px`,
borderRadius: rs,
background: active ? "#ebebeb" : "transparent",
fontSize: tokens.density === "compact" ? "0.76rem" : "0.8rem",
color: active ? "#18181b" : "#52525b",
fontWeight: active ? 600 : 400,
fontFamily: tokens.fontFamily,
}}
>
{children}
</div>
);
return (
<div style={{ display: "grid", gridTemplateColumns: "220px 1fr", gap: 24 }}>
<div
style={{
background: tokens.grayScale[2],
borderRadius: tokens.radiusMdPx,
border: `1px solid ${tokens.grayScale[4]}`,
padding: 10,
fontFamily: tokens.fontFamily,
}}
>
<div style={{ fontSize: "0.62rem", fontWeight: 700, color: "#a1a1aa", letterSpacing: "0.06em", margin: "4px 8px 8px" }}>MAIN</div>
<Item active>People</Item>
<Item>Companies</Item>
<Item>
Opportunities{" "}
<span style={{ float: "right", fontSize: "0.65rem", background: "#e4e4e7", padding: "1px 6px", borderRadius: 999 }}>12</span>
</Item>
<Item>Inbox</Item>
<div style={{ fontSize: "0.62rem", fontWeight: 700, color: "#a1a1aa", letterSpacing: "0.06em", margin: "14px 8px 8px" }}>WORKSPACE</div>
<Item>Workflows</Item>
<Item>Tasks</Item>
</div>
<div style={{ fontSize: "0.72rem", color: "#71717a", lineHeight: 1.65, fontFamily: tokens.fontFamily }}>
<div>Drawer preview uses kit neutrals + radius tokens.</div>
<div>Accent {tokens.accentHex} applies to links & focus off-nav.</div>
</div>
</div>
);
}
export function TagsPanel({ tokens }: { tokens: ResolvedKitTokens }) {
const pills = [
["Active", "#dcfce7", "#166534"],
["Closed", "#fee2e2", "#991b1b"],
["In Progress", "#fef9c3", "#854d0e"],
["Draft", tokens.accentScale[2] ?? "#e0e7ff", tokens.accentScale[10] ?? "#3730a3"],
];
return (
<div style={{ fontFamily: tokens.fontFamily }}>
<div style={{ fontSize: "0.72rem", color: "#71717a", marginBottom: 10 }}>Pill tags · radius-full</div>
<div style={{ display: "flex", flexWrap: "wrap", gap: 8 }}>
{pills.map(([label, bg, fg]) => (
<span
key={label}
style={{
padding: "4px 12px",
borderRadius: 999,
background: bg as string,
color: fg as string,
fontSize: "0.75rem",
fontWeight: 600,
}}
>
{label}
</span>
))}
</div>
</div>
);
}
export function IllustrationsPanel({ tokens }: { tokens: ResolvedKitTokens }) {
const icons = ["user", "mail", "calendar", "file", "link", "tag", "settings", "numbers"];
const r = tokens.radiusMdPx;
return (
<div style={{ fontFamily: tokens.fontFamily }}>
<div style={{ display: "flex", flexWrap: "wrap", gap: 10 }}>
{icons.map((name) => (
<div
key={name}
style={{
width: 52,
height: 52,
borderRadius: r,
border: "1px solid #e4e4e7",
display: "flex",
alignItems: "center",
justifyContent: "center",
fontSize: "0.62rem",
color: "#52525b",
background: "#fafafa",
}}
>
{name}
</div>
))}
</div>
<p style={{ fontSize: "0.7rem", color: "#a1a1aa", marginTop: 12 }}>
Replace with project SVGs · <code style={codeSm}>assets/illustrations/</code>
</p>
</div>
);
}
export function LogoPanel({ tokens }: { tokens: ResolvedKitTokens }) {
const r = tokens.radiusMdPx;
return (
<div style={{ display: "flex", flexWrap: "wrap", gap: 14, fontFamily: tokens.fontFamily }}>
{["Logo light", "Logo dark", "Icon mark", "Workspace"].map((label) => (
<div key={label} style={{ padding: 16, borderRadius: r, border: "1px solid #e4e4e7", background: "#fafafa", minWidth: 120 }}>
<div style={{ fontSize: "0.62rem", color: "#71717a", marginBottom: 10 }}>{label}</div>
<div
style={{
width: 40,
height: 40,
borderRadius: tokens.radiusSm,
background: `linear-gradient(135deg, ${tokens.accentHex}, ${tokens.accentScale[10] ?? "#1a1a1a"})`,
color: "#fff",
display: "flex",
alignItems: "center",
justifyContent: "center",
fontWeight: 700,
fontSize: "0.75rem",
}}
>
V
</div>
</div>
))}
</div>
);
}
const PANEL_COMPONENTS: Record<string, FC<{ tokens: ResolvedKitTokens }>> = {
fontWeights: FontWeightsPanel,
typeScale: TypeScalePanel,
accentScale: AccentScalePanel,
grayScale: GrayScalePanel,
semanticColors: SemanticColorsPanel,
radius: RadiusPanel,
shadows: ShadowsPanel,
spacing: SpacingPanel,
buttons: ButtonsPanel,
inputs: InputsPanel,
nav: NavPanel,
tags: TagsPanel,
illustrations: IllustrationsPanel,
logo: LogoPanel,
};
export function renderKitPanel(panelKey: string, tokens: ResolvedKitTokens): ReactNode {
const C = PANEL_COMPONENTS[panelKey];
if (!C) return <p style={{ color: "#71717a", fontSize: "0.82rem" }}>Unknown panel: {panelKey}</p>;
return <C tokens={tokens} />;
}