refactor: move all design controls below scaffold render

Theme swatches removed from inside scaffold components. Theme state
lifted to SurfaceSection which passes themeColor down as a prop.

Controls bar below the scaffold now has three rows:
  1. Library tabs (shadcn / Mantine / HeroUI / Tremor etc.)
  2. Color theme swatches — only shown when the active library has
     theme variants (shadcn: 8, Mantine: 6, HeroUI: 5, Tremor: 5,
     DaisyUI: 12, HeroUI marketing: 6)
  3. Description + tags + Docs link + Lock in button

Scaffold renders cleanly with no UI chrome inside it.

Made-with: Cursor
This commit is contained in:
2026-03-02 14:06:53 -08:00
parent 16766f587d
commit 7ba3b9563e
2 changed files with 82 additions and 58 deletions

View File

@@ -221,9 +221,9 @@ function ShadcnSettings({ t }: { t: ThemeColor }) {
);
}
export function WebAppShadcn() {
export function WebAppShadcn({ themeColor }: { themeColor?: ThemeColor }) {
const [page, setPage] = useState<Page>("Dashboard");
const [theme, setTheme] = useState<ThemeColor>(SHADCN_THEMES[0]);
const theme = themeColor ?? SHADCN_THEMES[0];
const NAV_ITEMS: { label: Page; icon: string }[] = [
{ label: "Dashboard", icon: "▦" },
{ label: "Users", icon: "◎" },
@@ -231,7 +231,6 @@ export function WebAppShadcn() {
];
return (
<div className="flex h-full bg-white font-sans text-sm">
{/* Sidebar */}
<div className="w-44 border-r flex flex-col py-4 px-3 gap-1 bg-white shrink-0">
<div className="flex items-center gap-2 px-2 mb-4">
<div className="w-6 h-6 rounded" style={{ background: theme.primary }} />
@@ -245,12 +244,10 @@ export function WebAppShadcn() {
</button>
))}
</div>
{/* Main */}
<div className="flex-1 flex flex-col min-w-0">
<div className="h-12 border-b flex items-center justify-between px-5 shrink-0">
<span className="font-semibold text-zinc-900 text-sm">{page}</span>
<div className="flex items-center gap-3">
<ThemeSwatches themes={SHADCN_THEMES} selected={theme} onSelect={setTheme} />
<div className="flex items-center gap-2">
<div className="h-7 px-3 rounded-md border border-zinc-200 text-[11px] flex items-center text-zinc-600">Export</div>
<div className="h-7 px-3 rounded-md text-[11px] flex items-center" style={{ background: theme.primary, color: theme.primaryFg }}>+ New</div>
</div>
@@ -357,9 +354,9 @@ function MantineSettings({ t }: { t: ThemeColor }) {
);
}
export function WebAppMantine() {
export function WebAppMantine({ themeColor }: { themeColor?: ThemeColor }) {
const [page, setPage] = useState<Page>("Dashboard");
const [theme, setTheme] = useState<ThemeColor>(MANTINE_THEMES[0]);
const theme = themeColor ?? MANTINE_THEMES[0];
const NAV: { label: Page; icon: string }[] = [
{ label: "Dashboard", icon: "▦" },
{ label: "Users", icon: "◎" },
@@ -383,8 +380,7 @@ export function WebAppMantine() {
<div className="flex-1 flex flex-col min-w-0">
<div className="h-12 flex items-center justify-between px-5 shrink-0" style={{ background:"#fff", borderBottom:"1px solid #e9ecef" }}>
<span className="font-bold text-sm" style={{ color: "#212529" }}>{page}</span>
<div className="flex items-center gap-3">
<ThemeSwatches themes={MANTINE_THEMES} selected={theme} onSelect={setTheme} />
<div className="flex items-center gap-2">
<button className="h-7 px-3 rounded text-[11px] font-medium" style={{ border:"1px solid #dee2e6", color:"#495057" }}>Export</button>
<button className="h-7 px-3 rounded text-[11px] font-medium" style={{ background: theme.primary, color: theme.primaryFg }}>+ New</button>
</div>
@@ -491,9 +487,9 @@ function HeroUISettings() {
);
}
export function WebAppHeroUI() {
export function WebAppHeroUI({ themeColor }: { themeColor?: ThemeColor }) {
const [page, setPage] = useState<Page>("Dashboard");
const [theme, setTheme] = useState<ThemeColor>(HEROUI_THEMES[0]);
const theme = themeColor ?? HEROUI_THEMES[0];
const NAV: { label: Page; icon: string }[] = [
{ label: "Dashboard", icon: "▦" },
{ label: "Users", icon: "◎" },
@@ -517,8 +513,7 @@ export function WebAppHeroUI() {
<div className="flex-1 flex flex-col min-w-0">
<div className="h-12 flex items-center justify-between px-5 bg-white shrink-0" style={{ borderBottom: "1px solid #f0f0f0" }}>
<span className="font-bold text-sm" style={{ color: theme.primary }}>{page}</span>
<div className="flex items-center gap-3">
<ThemeSwatches themes={HEROUI_THEMES} selected={theme} onSelect={setTheme} />
<div className="flex items-center gap-2">
<button className="h-7 px-3 rounded-full text-[11px] font-medium" style={{ border:"1px solid #e4e4e7", color:"#71717a" }}>Export</button>
<button className="h-7 px-3 rounded-full text-[11px] font-semibold" style={{ background: theme.primary, color: theme.primaryFg }}>+ New</button>
</div>
@@ -632,9 +627,9 @@ function TremorSettings() {
);
}
export function WebAppTremor() {
export function WebAppTremor({ themeColor }: { themeColor?: ThemeColor }) {
const [page, setPage] = useState<Page>("Dashboard");
const [theme, setTheme] = useState<ThemeColor>(TREMOR_THEMES[0]);
const theme = themeColor ?? TREMOR_THEMES[0];
const NAV: { label: Page; icon: string }[] = [
{ label: "Dashboard", icon: "▦" },
{ label: "Users", icon: "◎" },
@@ -658,8 +653,7 @@ export function WebAppTremor() {
<div className="flex-1 flex flex-col min-w-0" style={{ background:"#f9fafb" }}>
<div className="h-12 flex items-center justify-between px-5 bg-white shrink-0" style={{ borderBottom:"1px solid #e5e7eb" }}>
<span className="font-bold text-sm" style={{ color:"#111827" }}>{page}</span>
<div className="flex items-center gap-3">
<ThemeSwatches themes={TREMOR_THEMES} selected={theme} onSelect={setTheme} />
<div className="flex items-center gap-2">
<button className="h-7 px-3 rounded-lg text-[11px] font-medium" style={{ background: theme.primary, color: theme.primaryFg }}>+ New</button>
</div>
</div>
@@ -675,11 +669,8 @@ export function WebAppTremor() {
// MARKETING scaffolds
// ---------------------------------------------------------------------------
export function MarketingDaisy() {
const [theme, setTheme] = useState<ThemeColor>(DAISY_THEMES[0]);
const isDark = !!theme.bg && ["#1d232a","#282a36","#1a103c","#171212","#20150e","#ffee00"].includes(theme.bg) === false
? theme.bg.startsWith("#1") || theme.bg.startsWith("#2") || theme.bg === "#ffee00"
: false;
export function MarketingDaisy({ themeColor }: { themeColor?: ThemeColor }) {
const theme = themeColor ?? DAISY_THEMES[0];
const textColor = theme.textColor ?? "#f8f8f2";
const mutedText = theme.mutedText ?? "rgba(255,255,255,0.5)";
const cardBg = theme.cardBg ?? "rgba(255,255,255,0.05)";
@@ -697,7 +688,6 @@ export function MarketingDaisy() {
{["Features","Pricing","Docs","Blog"].map(i=><span key={i}>{i}</span>)}
</div>
<div className="flex items-center gap-2">
<ThemeSwatches themes={DAISY_THEMES} selected={theme} onSelect={setTheme} />
<button className="h-7 px-3 rounded-full text-[11px] font-bold" style={{ background:`${borderColor}`, color: textColor }}>Login</button>
<button className="h-7 px-3 rounded-full text-[11px] font-bold" style={{ background: theme.primary, color: theme.primaryFg }}>Get started</button>
</div>
@@ -749,8 +739,8 @@ const HEROUI_MARKETING_THEMES: ThemeColor[] = [
{ id: "modern", label: "Modern", primary: "#06b6d4", primaryFg: "#fff", activeBg: "rgba(6,182,212,0.08)", activeFg: "#06b6d4", ring: "rgba(6,182,212,0.15)", bg: "#fff" },
];
export function MarketingHeroUI() {
const [theme, setTheme] = useState<ThemeColor>(HEROUI_MARKETING_THEMES[0]);
export function MarketingHeroUI({ themeColor }: { themeColor?: ThemeColor }) {
const theme = themeColor ?? HEROUI_MARKETING_THEMES[0];
const isDark = theme.id === "dark";
const bg = theme.bg ?? "#fff";
const textColor = theme.textColor ?? "#18181b";
@@ -769,7 +759,6 @@ export function MarketingHeroUI() {
{["Features","Pricing","Docs","Blog"].map(i=><span key={i}>{i}</span>)}
</div>
<div className="flex items-center gap-2">
<ThemeSwatches themes={HEROUI_MARKETING_THEMES} selected={theme} onSelect={setTheme} />
<button className="h-7 px-3 rounded-full text-[11px]" style={{ border:`1px solid ${borderColor}`, color: mutedText }}>Login</button>
<button className="h-7 px-3 rounded-full text-[11px] font-semibold text-white" style={{ background: theme.primary }}>Get started</button>
</div>
@@ -1118,7 +1107,7 @@ export function DocsShadcnCustom() {
// Registry — maps surface+theme to scaffold component
// ---------------------------------------------------------------------------
export const SCAFFOLD_REGISTRY: Record<string, Record<string, React.ComponentType>> = {
export const SCAFFOLD_REGISTRY: Record<string, Record<string, React.ComponentType<{ themeColor?: ThemeColor }>>> = {
"web-app": {
"shadcn": WebAppShadcn,
"mantine": WebAppMantine,
@@ -1148,3 +1137,17 @@ export const SCAFFOLD_REGISTRY: Record<string, Record<string, React.ComponentTyp
"shadcn": DocsShadcnCustom,
},
};
// Maps surface → library → available theme colors for that library
export const THEME_REGISTRY: Record<string, Record<string, ThemeColor[]>> = {
"web-app": {
"shadcn": SHADCN_THEMES,
"mantine": MANTINE_THEMES,
"hero-ui": HEROUI_THEMES,
"tremor": TREMOR_THEMES,
},
"marketing": {
"daisy-ui": DAISY_THEMES,
"hero-ui": HEROUI_MARKETING_THEMES,
},
};