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:
@@ -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,
|
||||
},
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user