feat: color theme swatches inside web app scaffolds
Each web app scaffold (shadcn, Mantine, HeroUI, Tremor) now shows a row of color swatches in the header. Clicking a swatch updates the primary color across the entire scaffold — sidebar active state, buttons, bar chart, badges, toggles, and status indicators all update live. shadcn has 8 themes (Neutral/Blue/Green/Orange/Red/Rose/Violet/ Yellow), Mantine has 6, HeroUI has 5, Tremor has 5. Made-with: Cursor
This commit is contained in:
@@ -22,13 +22,87 @@ const TABLE_ROWS = [
|
||||
];
|
||||
|
||||
type Page = "Dashboard" | "Users" | "Settings";
|
||||
const PAGES: Page[] = ["Dashboard", "Users", "Settings"];
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Theme color definitions
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export interface ThemeColor {
|
||||
id: string;
|
||||
label: string;
|
||||
primary: string; // button bg, active dot, toggle on
|
||||
primaryFg: string; // text on primary bg
|
||||
activeBg: string; // sidebar active item bg
|
||||
activeFg: string; // sidebar active item text
|
||||
ring: string; // focus ring / subtle tint
|
||||
}
|
||||
|
||||
export const SHADCN_THEMES: ThemeColor[] = [
|
||||
{ id: "neutral", label: "Neutral", primary: "#18181b", primaryFg: "#fff", activeBg: "#f4f4f5", activeFg: "#18181b", ring: "#e4e4e7" },
|
||||
{ id: "blue", label: "Blue", primary: "#3b82f6", primaryFg: "#fff", activeBg: "#eff6ff", activeFg: "#1d4ed8", ring: "#bfdbfe" },
|
||||
{ id: "green", label: "Green", primary: "#22c55e", primaryFg: "#fff", activeBg: "#f0fdf4", activeFg: "#15803d", ring: "#bbf7d0" },
|
||||
{ id: "orange", label: "Orange", primary: "#f97316", primaryFg: "#fff", activeBg: "#fff7ed", activeFg: "#c2410c", ring: "#fed7aa" },
|
||||
{ id: "red", label: "Red", primary: "#ef4444", primaryFg: "#fff", activeBg: "#fef2f2", activeFg: "#b91c1c", ring: "#fecaca" },
|
||||
{ id: "rose", label: "Rose", primary: "#f43f5e", primaryFg: "#fff", activeBg: "#fff1f2", activeFg: "#be123c", ring: "#fecdd3" },
|
||||
{ id: "violet", label: "Violet", primary: "#8b5cf6", primaryFg: "#fff", activeBg: "#f5f3ff", activeFg: "#6d28d9", ring: "#ddd6fe" },
|
||||
{ id: "yellow", label: "Yellow", primary: "#eab308", primaryFg: "#000", activeBg: "#fefce8", activeFg: "#a16207", ring: "#fef08a" },
|
||||
];
|
||||
|
||||
export const MANTINE_THEMES: ThemeColor[] = [
|
||||
{ id: "blue", label: "Blue", primary: "#228be6", primaryFg: "#fff", activeBg: "#e7f5ff", activeFg: "#1971c2", ring: "#a5d8ff" },
|
||||
{ id: "teal", label: "Teal", primary: "#12b886", primaryFg: "#fff", activeBg: "#e6fcf5", activeFg: "#087f5b", ring: "#96f2d7" },
|
||||
{ id: "violet", label: "Violet", primary: "#7950f2", primaryFg: "#fff", activeBg: "#f3f0ff", activeFg: "#5f3dc4", ring: "#d0bfff" },
|
||||
{ id: "red", label: "Red", primary: "#fa5252", primaryFg: "#fff", activeBg: "#fff5f5", activeFg: "#c92a2a", ring: "#ffc9c9" },
|
||||
{ id: "orange", label: "Orange", primary: "#fd7e14", primaryFg: "#fff", activeBg: "#fff4e6", activeFg: "#d9480f", ring: "#ffd8a8" },
|
||||
{ id: "green", label: "Green", primary: "#40c057", primaryFg: "#fff", activeBg: "#ebfbee", activeFg: "#2f9e44", ring: "#b2f2bb" },
|
||||
];
|
||||
|
||||
export const HEROUI_THEMES: ThemeColor[] = [
|
||||
{ id: "violet", label: "Violet", primary: "#7c3aed", primaryFg: "#fff", activeBg: "rgba(124,58,237,0.3)", activeFg: "#c084fc", ring: "rgba(168,85,247,0.3)" },
|
||||
{ id: "blue", label: "Blue", primary: "#2563eb", primaryFg: "#fff", activeBg: "rgba(37,99,235,0.3)", activeFg: "#93c5fd", ring: "rgba(59,130,246,0.3)" },
|
||||
{ id: "rose", label: "Rose", primary: "#e11d48", primaryFg: "#fff", activeBg: "rgba(225,29,72,0.3)", activeFg: "#fda4af", ring: "rgba(244,63,94,0.3)" },
|
||||
{ id: "green", label: "Green", primary: "#16a34a", primaryFg: "#fff", activeBg: "rgba(22,163,74,0.3)", activeFg: "#86efac", ring: "rgba(34,197,94,0.3)" },
|
||||
{ id: "orange", label: "Orange", primary: "#ea580c", primaryFg: "#fff", activeBg: "rgba(234,88,12,0.3)", activeFg: "#fdba74", ring: "rgba(249,115,22,0.3)" },
|
||||
];
|
||||
|
||||
export const TREMOR_THEMES: ThemeColor[] = [
|
||||
{ id: "blue", label: "Blue", primary: "#2563eb", primaryFg: "#fff", activeBg: "#eff6ff", activeFg: "#1d4ed8", ring: "#bfdbfe" },
|
||||
{ id: "indigo", label: "Indigo", primary: "#4f46e5", primaryFg: "#fff", activeBg: "#eef2ff", activeFg: "#3730a3", ring: "#c7d2fe" },
|
||||
{ id: "teal", label: "Teal", primary: "#0d9488", primaryFg: "#fff", activeBg: "#f0fdfa", activeFg: "#0f766e", ring: "#99f6e4" },
|
||||
{ id: "rose", label: "Rose", primary: "#e11d48", primaryFg: "#fff", activeBg: "#fff1f2", activeFg: "#be123c", ring: "#fecdd3" },
|
||||
{ id: "amber", label: "Amber", primary: "#d97706", primaryFg: "#fff", activeBg: "#fffbeb", activeFg: "#92400e", ring: "#fde68a" },
|
||||
];
|
||||
|
||||
// Small swatch strip rendered inside each scaffold's header
|
||||
function ThemeSwatches({ themes, selected, onSelect }: {
|
||||
themes: ThemeColor[];
|
||||
selected: ThemeColor;
|
||||
onSelect: (t: ThemeColor) => void;
|
||||
}) {
|
||||
return (
|
||||
<div className="flex items-center gap-1">
|
||||
{themes.map(t => (
|
||||
<button
|
||||
key={t.id}
|
||||
title={t.label}
|
||||
onClick={() => onSelect(t)}
|
||||
className="w-4 h-4 rounded-full transition-transform hover:scale-110"
|
||||
style={{
|
||||
background: t.primary,
|
||||
outline: selected.id === t.id ? `2px solid ${t.primary}` : "none",
|
||||
outlineOffset: 2,
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// WEB APP — shadcn
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
function ShadcnDashboard() {
|
||||
function ShadcnDashboard({ t }: { t: ThemeColor }) {
|
||||
return (
|
||||
<div className="flex-1 p-5 bg-zinc-50 overflow-auto">
|
||||
<div className="grid grid-cols-3 gap-3 mb-4">
|
||||
@@ -41,12 +115,10 @@ function ShadcnDashboard() {
|
||||
))}
|
||||
</div>
|
||||
<div className="bg-white rounded-lg border mb-3">
|
||||
<div className="px-4 py-3 border-b">
|
||||
<span className="text-xs font-semibold text-zinc-700">Revenue</span>
|
||||
</div>
|
||||
<div className="px-4 py-3 border-b"><span className="text-xs font-semibold text-zinc-700">Revenue</span></div>
|
||||
<div className="p-4 flex items-end gap-1.5 h-24">
|
||||
{[40,60,45,75,65,85,70,90,55,80,75,95].map((h,i)=>(
|
||||
<div key={i} className="flex-1 rounded-sm" style={{ height:`${h}%`, background: i===11?"#18181b":"#e4e4e7" }} />
|
||||
<div key={i} className="flex-1 rounded-sm" style={{ height:`${h}%`, background: i===11 ? t.primary : t.ring }} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
@@ -56,7 +128,7 @@ function ShadcnDashboard() {
|
||||
<div key={r.name} className="flex items-center gap-3 px-4 py-2.5 border-b last:border-0">
|
||||
<div className="w-6 h-6 rounded-full bg-zinc-200 shrink-0" />
|
||||
<div className="flex-1"><p className="text-[11px] font-medium text-zinc-800">{r.name}</p><p className="text-[10px] text-zinc-400">{r.email}</p></div>
|
||||
<span className={`px-1.5 py-0.5 rounded text-[9px] font-medium ${r.status==="Active"?"bg-zinc-100 text-zinc-700":"bg-zinc-50 text-zinc-400"}`}>{r.status}</span>
|
||||
<span className="px-1.5 py-0.5 rounded text-[9px] font-medium" style={r.status==="Active"?{background:t.activeBg,color:t.activeFg}:{background:"#f4f4f5",color:"#a1a1aa"}}>{r.status}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
@@ -64,7 +136,7 @@ function ShadcnDashboard() {
|
||||
);
|
||||
}
|
||||
|
||||
function ShadcnUsers() {
|
||||
function ShadcnUsers({ t }: { t: ThemeColor }) {
|
||||
return (
|
||||
<div className="flex-1 p-5 bg-zinc-50 overflow-auto">
|
||||
<div className="bg-white rounded-lg border">
|
||||
@@ -72,7 +144,7 @@ function ShadcnUsers() {
|
||||
<span className="text-xs font-semibold text-zinc-700">Team members</span>
|
||||
<div className="flex gap-2">
|
||||
<input className="h-7 px-2.5 rounded-md border border-zinc-200 text-[11px] outline-none w-32" placeholder="Search..." />
|
||||
<div className="h-7 px-3 rounded-md bg-zinc-900 text-[11px] flex items-center text-white cursor-pointer">Invite</div>
|
||||
<div className="h-7 px-3 rounded-md text-[11px] flex items-center cursor-pointer" style={{ background: t.primary, color: t.primaryFg }}>Invite</div>
|
||||
</div>
|
||||
</div>
|
||||
<table className="w-full text-[11px]">
|
||||
@@ -83,7 +155,7 @@ function ShadcnUsers() {
|
||||
<div className="flex items-center gap-2"><div className="w-5 h-5 rounded-full bg-zinc-200" /><div><p className="font-medium text-zinc-800">{r.name}</p><p className="text-[10px] text-zinc-400">{r.email}</p></div></div>
|
||||
</td>
|
||||
<td className="px-4 py-2.5"><span className="px-1.5 py-0.5 rounded border border-zinc-200 text-[9px] text-zinc-600">{r.role}</span></td>
|
||||
<td className="px-4 py-2.5"><span className={`px-1.5 py-0.5 rounded text-[9px] font-medium ${r.status==="Active"?"bg-zinc-900 text-white":r.status==="Pending"?"bg-zinc-100 text-zinc-600":"bg-zinc-50 text-zinc-400"}`}>{r.status}</span></td>
|
||||
<td className="px-4 py-2.5"><span className="px-1.5 py-0.5 rounded text-[9px] font-medium" style={r.status==="Active"?{background:t.primary,color:t.primaryFg}:r.status==="Pending"?{background:"#f4f4f5",color:"#71717a"}:{background:"#fafafa",color:"#a1a1aa"}}>{r.status}</span></td>
|
||||
<td className="px-4 py-2.5 text-zinc-400">{r.date}</td>
|
||||
<td className="px-4 py-2.5"><div className="h-6 px-2 rounded border border-zinc-200 text-[9px] flex items-center text-zinc-500 cursor-pointer w-fit">···</div></td>
|
||||
</tr>
|
||||
@@ -94,7 +166,7 @@ function ShadcnUsers() {
|
||||
);
|
||||
}
|
||||
|
||||
function ShadcnSettings() {
|
||||
function ShadcnSettings({ t }: { t: ThemeColor }) {
|
||||
return (
|
||||
<div className="flex-1 p-5 bg-zinc-50 overflow-auto">
|
||||
<div className="bg-white rounded-lg border mb-3">
|
||||
@@ -103,10 +175,10 @@ function ShadcnSettings() {
|
||||
{[{l:"Workspace name",v:"Acme Inc"},{l:"Slug",v:"acme-inc"},{l:"Email",v:"admin@acme.com"}].map(f=>(
|
||||
<div key={f.l}>
|
||||
<label className="text-[10px] font-medium text-zinc-600 block mb-1">{f.l}</label>
|
||||
<input defaultValue={f.v} className="w-full h-8 px-3 rounded-md border border-zinc-200 text-[11px] outline-none focus:ring-1 focus:ring-zinc-900" />
|
||||
<input defaultValue={f.v} className="w-full h-8 px-3 rounded-md border border-zinc-200 text-[11px] outline-none" style={{ outlineColor: t.primary }} />
|
||||
</div>
|
||||
))}
|
||||
<div className="pt-1"><div className="h-7 px-3 rounded-md bg-zinc-900 text-[11px] inline-flex items-center text-white cursor-pointer">Save changes</div></div>
|
||||
<div className="pt-1"><div className="h-7 px-3 rounded-md text-[11px] inline-flex items-center cursor-pointer" style={{ background: t.primary, color: t.primaryFg }}>Save changes</div></div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="bg-white rounded-lg border">
|
||||
@@ -115,8 +187,8 @@ function ShadcnSettings() {
|
||||
{["Email digests","Push notifications","Security alerts","Product updates"].map((label,i)=>(
|
||||
<div key={label} className="flex items-center justify-between">
|
||||
<div><p className="text-[11px] font-medium text-zinc-700">{label}</p><p className="text-[10px] text-zinc-400">Receive {label.toLowerCase()}</p></div>
|
||||
<div className={`w-8 h-4 rounded-full relative cursor-pointer transition-colors ${[true,false,true,false][i]?"bg-zinc-900":"bg-zinc-200"}`}>
|
||||
<div className={`absolute top-0.5 w-3 h-3 rounded-full bg-white shadow transition-transform ${[true,false,true,false][i]?"translate-x-4":"translate-x-0.5"}`} />
|
||||
<div className="w-8 h-4 rounded-full relative cursor-pointer" style={{ background: [true,false,true,false][i] ? t.primary : "#e4e4e7" }}>
|
||||
<div className="absolute top-0.5 w-3 h-3 rounded-full bg-white shadow" style={{ transform: [true,false,true,false][i] ? "translateX(16px)" : "translateX(2px)" }} />
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
@@ -128,6 +200,7 @@ function ShadcnSettings() {
|
||||
|
||||
export function WebAppShadcn() {
|
||||
const [page, setPage] = useState<Page>("Dashboard");
|
||||
const [theme, setTheme] = useState<ThemeColor>(SHADCN_THEMES[0]);
|
||||
const NAV_ITEMS: { label: Page; icon: string }[] = [
|
||||
{ label: "Dashboard", icon: "▦" },
|
||||
{ label: "Users", icon: "◎" },
|
||||
@@ -135,35 +208,39 @@ 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 bg-zinc-900" />
|
||||
<div className="w-6 h-6 rounded" style={{ background: theme.primary }} />
|
||||
<span className="font-semibold text-zinc-900 text-xs">Acme Inc</span>
|
||||
</div>
|
||||
{NAV_ITEMS.map(({ label, icon }) => (
|
||||
<button key={label} onClick={() => setPage(label)}
|
||||
className={`flex items-center gap-2 px-2 py-1.5 rounded-md text-xs cursor-pointer w-full text-left ${page === label ? "bg-zinc-100 text-zinc-900 font-medium" : "text-zinc-500 hover:bg-zinc-50"}`}>
|
||||
className="flex items-center gap-2 px-2 py-1.5 rounded-md text-xs cursor-pointer w-full text-left transition-colors"
|
||||
style={page===label ? { background: theme.activeBg, color: theme.activeFg, fontWeight: 600 } : { color: "#71717a" }}>
|
||||
<span className="text-[11px]">{icon}</span>{label}
|
||||
</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-2">
|
||||
<div className="flex items-center gap-3">
|
||||
<ThemeSwatches themes={SHADCN_THEMES} selected={theme} onSelect={setTheme} />
|
||||
<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 bg-zinc-900 text-[11px] flex items-center text-white">+ New</div>
|
||||
<div className="h-7 px-3 rounded-md text-[11px] flex items-center" style={{ background: theme.primary, color: theme.primaryFg }}>+ New</div>
|
||||
</div>
|
||||
</div>
|
||||
{page === "Dashboard" && <ShadcnDashboard />}
|
||||
{page === "Users" && <ShadcnUsers />}
|
||||
{page === "Settings" && <ShadcnSettings />}
|
||||
{page === "Dashboard" && <ShadcnDashboard t={theme} />}
|
||||
{page === "Users" && <ShadcnUsers t={theme} />}
|
||||
{page === "Settings" && <ShadcnSettings t={theme} />}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function MantineDashboard() {
|
||||
function MantineDashboard({ t }: { t: ThemeColor }) {
|
||||
return (
|
||||
<div className="flex-1 p-5 overflow-auto" style={{ background:"#f8f9fa" }}>
|
||||
<div className="grid grid-cols-3 gap-3 mb-4">
|
||||
@@ -197,7 +274,7 @@ function MantineDashboard() {
|
||||
);
|
||||
}
|
||||
|
||||
function MantineUsers() {
|
||||
function MantineUsers({ t }: { t: ThemeColor }) {
|
||||
return (
|
||||
<div className="flex-1 p-5 overflow-auto" style={{ background:"#f8f9fa" }}>
|
||||
<div className="rounded-lg" style={{ background:"#fff", border:"1px solid #e9ecef" }}>
|
||||
@@ -205,15 +282,15 @@ function MantineUsers() {
|
||||
<span className="text-xs font-semibold" style={{ color:"#212529" }}>Team members</span>
|
||||
<div className="flex gap-2">
|
||||
<input className="h-7 px-2.5 text-[11px] rounded" style={{ border:"1px solid #dee2e6", outline:"none", width:130 }} placeholder="Search members..." />
|
||||
<button className="h-7 px-3 rounded text-[11px] font-medium text-white" style={{ background:"#228be6" }}>+ Invite</button>
|
||||
<button className="h-7 px-3 rounded text-[11px] font-medium" style={{ background: t.primary, color: t.primaryFg }}>+ Invite</button>
|
||||
</div>
|
||||
</div>
|
||||
<table className="w-full text-[11px]">
|
||||
<thead><tr style={{ borderBottom:"1px solid #e9ecef", background:"#f8f9fa" }}>{["Member","Role","Status","Joined","Actions"].map(h=><th key={h} className="px-4 py-2 text-left text-[10px] font-semibold" style={{ color:"#868e96" }}>{h}</th>)}</tr></thead>
|
||||
<tbody>{TABLE_ROWS.map(r=>(
|
||||
<tr key={r.name} style={{ borderBottom:"1px solid #f1f3f5" }}>
|
||||
<td className="px-4 py-2.5"><div className="flex items-center gap-2"><div className="w-6 h-6 rounded-full" style={{ background:"#d0ebff" }} /><div><p className="font-semibold" style={{ color:"#212529" }}>{r.name}</p><p style={{ color:"#868e96" }}>{r.email}</p></div></div></td>
|
||||
<td className="px-4 py-2.5"><span className="px-1.5 py-0.5 rounded text-[9px]" style={{ background:"#e7f5ff", color:"#1971c2" }}>{r.role}</span></td>
|
||||
<td className="px-4 py-2.5"><div className="flex items-center gap-2"><div className="w-6 h-6 rounded-full" style={{ background: t.ring }} /><div><p className="font-semibold" style={{ color:"#212529" }}>{r.name}</p><p style={{ color:"#868e96" }}>{r.email}</p></div></div></td>
|
||||
<td className="px-4 py-2.5"><span className="px-1.5 py-0.5 rounded text-[9px]" style={{ background: t.activeBg, color: t.activeFg }}>{r.role}</span></td>
|
||||
<td className="px-4 py-2.5"><span className="px-1.5 py-0.5 rounded text-[9px] font-medium" style={r.status==="Active"?{background:"#d3f9d8",color:"#2f9e44"}:r.status==="Pending"?{background:"#fff3bf",color:"#e67700"}:{background:"#f1f3f5",color:"#868e96"}}>{r.status}</span></td>
|
||||
<td className="px-4 py-2.5" style={{ color:"#adb5bd" }}>{r.date}</td>
|
||||
<td className="px-4 py-2.5"><button className="text-[10px] px-2 py-1 rounded" style={{ border:"1px solid #dee2e6", color:"#495057" }}>Manage</button></td>
|
||||
@@ -225,7 +302,7 @@ function MantineUsers() {
|
||||
);
|
||||
}
|
||||
|
||||
function MantineSettings() {
|
||||
function MantineSettings({ t }: { t: ThemeColor }) {
|
||||
return (
|
||||
<div className="flex-1 p-5 overflow-auto" style={{ background:"#f8f9fa" }}>
|
||||
<div className="rounded-lg mb-3" style={{ background:"#fff", border:"1px solid #e9ecef" }}>
|
||||
@@ -237,7 +314,7 @@ function MantineSettings() {
|
||||
<input defaultValue={f.v} className="w-full h-8 px-3 rounded text-[11px]" style={{ border:"1px solid #dee2e6", outline:"none" }} />
|
||||
</div>
|
||||
))}
|
||||
<button className="h-8 px-4 rounded text-[11px] font-medium text-white" style={{ background:"#228be6" }}>Save</button>
|
||||
<button className="h-8 px-4 rounded text-[11px] font-medium" style={{ background: t.primary, color: t.primaryFg }}>Save</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="rounded-lg" style={{ background:"#fff", border:"1px solid #e9ecef" }}>
|
||||
@@ -246,8 +323,8 @@ function MantineSettings() {
|
||||
{["Email digests","Push notifications","Security alerts","Product updates"].map((label,i)=>(
|
||||
<div key={label} className="flex items-center justify-between">
|
||||
<div><p className="text-[11px] font-medium" style={{ color:"#212529" }}>{label}</p><p className="text-[10px]" style={{ color:"#868e96" }}>Get notified via {label.split(" ")[0].toLowerCase()}</p></div>
|
||||
<div className="w-8 h-4 rounded-full relative cursor-pointer" style={{ background:[true,false,true,false][i]?"#228be6":"#dee2e6" }}>
|
||||
<div className="absolute top-0.5 w-3 h-3 rounded-full bg-white shadow transition-transform" style={{ transform:[true,false,true,false][i]?"translateX(16px)":"translateX(2px)" }} />
|
||||
<div className="w-8 h-4 rounded-full relative cursor-pointer" style={{ background:[true,false,true,false][i] ? t.primary : "#dee2e6" }}>
|
||||
<div className="absolute top-0.5 w-3 h-3 rounded-full bg-white shadow" style={{ transform:[true,false,true,false][i]?"translateX(16px)":"translateX(2px)" }} />
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
@@ -259,6 +336,7 @@ function MantineSettings() {
|
||||
|
||||
export function WebAppMantine() {
|
||||
const [page, setPage] = useState<Page>("Dashboard");
|
||||
const [theme, setTheme] = useState<ThemeColor>(MANTINE_THEMES[0]);
|
||||
const NAV: { label: Page; icon: string }[] = [
|
||||
{ label: "Dashboard", icon: "▦" },
|
||||
{ label: "Users", icon: "◎" },
|
||||
@@ -268,13 +346,13 @@ export function WebAppMantine() {
|
||||
<div className="flex h-full font-sans text-sm" style={{ background: "#f8f9fa" }}>
|
||||
<div className="w-44 flex flex-col py-4 px-3 gap-0.5 shrink-0" style={{ background: "#fff", borderRight: "1px solid #e9ecef" }}>
|
||||
<div className="flex items-center gap-2 px-2 mb-4">
|
||||
<div className="w-6 h-6 rounded" style={{ background: "#228be6" }} />
|
||||
<span className="font-bold text-xs" style={{ color: "#1c7ed6" }}>Acme Inc</span>
|
||||
<div className="w-6 h-6 rounded" style={{ background: theme.primary }} />
|
||||
<span className="font-bold text-xs" style={{ color: theme.activeFg }}>Acme Inc</span>
|
||||
</div>
|
||||
{NAV.map(({ label, icon }) => (
|
||||
<button key={label} onClick={() => setPage(label)}
|
||||
className="flex items-center gap-2 px-2 py-2 rounded text-xs cursor-pointer w-full text-left"
|
||||
style={page===label?{background:"#e7f5ff",color:"#1971c2",fontWeight:600}:{color:"#495057"}}>
|
||||
style={page===label?{background: theme.activeBg, color: theme.activeFg, fontWeight:600}:{color:"#495057"}}>
|
||||
<span>{icon}</span>{label}
|
||||
</button>
|
||||
))}
|
||||
@@ -282,14 +360,15 @@ 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 gap-2">
|
||||
<div className="flex items-center gap-3">
|
||||
<ThemeSwatches themes={MANTINE_THEMES} selected={theme} onSelect={setTheme} />
|
||||
<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 text-white" style={{ background:"#228be6" }}>+ New</button>
|
||||
<button className="h-7 px-3 rounded text-[11px] font-medium" style={{ background: theme.primary, color: theme.primaryFg }}>+ New</button>
|
||||
</div>
|
||||
</div>
|
||||
{page === "Dashboard" && <MantineDashboard />}
|
||||
{page === "Users" && <MantineUsers />}
|
||||
{page === "Settings" && <MantineSettings />}
|
||||
{page === "Dashboard" && <MantineDashboard t={theme} />}
|
||||
{page === "Users" && <MantineUsers t={theme} />}
|
||||
{page === "Settings" && <MantineSettings t={theme} />}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
@@ -391,6 +470,7 @@ function HeroUISettings() {
|
||||
|
||||
export function WebAppHeroUI() {
|
||||
const [page, setPage] = useState<Page>("Dashboard");
|
||||
const [theme, setTheme] = useState<ThemeColor>(HEROUI_THEMES[0]);
|
||||
const NAV: { label: Page; icon: string }[] = [
|
||||
{ label: "Dashboard", icon: "▦" },
|
||||
{ label: "Users", icon: "◎" },
|
||||
@@ -400,23 +480,24 @@ export function WebAppHeroUI() {
|
||||
<div className="flex h-full font-sans text-sm" style={{ background: "#fafafa" }}>
|
||||
<div className="w-44 flex flex-col py-4 px-3 gap-1 shrink-0" style={{ background: "linear-gradient(180deg,#1a1a2e,#16213e)", color: "#fff" }}>
|
||||
<div className="flex items-center gap-2 px-2 mb-4">
|
||||
<div className="w-6 h-6 rounded-full" style={{ background: "linear-gradient(135deg,#7c3aed,#ec4899)" }} />
|
||||
<div className="w-6 h-6 rounded-full" style={{ background: theme.primary }} />
|
||||
<span className="font-bold text-xs text-white">Acme Inc</span>
|
||||
</div>
|
||||
{NAV.map(({ label, icon }) => (
|
||||
<button key={label} onClick={() => setPage(label)}
|
||||
className="flex items-center gap-2 px-2 py-2 rounded-xl text-xs cursor-pointer w-full text-left"
|
||||
style={page===label?{background:"rgba(124,58,237,0.3)",color:"#c084fc",fontWeight:600}:{color:"rgba(255,255,255,0.5)"}}>
|
||||
style={page===label?{background: theme.activeBg, color: theme.activeFg, fontWeight:600}:{color:"rgba(255,255,255,0.5)"}}>
|
||||
<span>{icon}</span>{label}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
<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={{ background:"linear-gradient(90deg,#7c3aed,#ec4899)", WebkitBackgroundClip:"text", WebkitTextFillColor:"transparent" }}>{page}</span>
|
||||
<div className="flex gap-2">
|
||||
<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} />
|
||||
<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 text-white" style={{ background:"linear-gradient(135deg,#7c3aed,#ec4899)" }}>+ New</button>
|
||||
<button className="h-7 px-3 rounded-full text-[11px] font-semibold" style={{ background: theme.primary, color: theme.primaryFg }}>+ New</button>
|
||||
</div>
|
||||
</div>
|
||||
{page === "Dashboard" && <HeroUIDashboard />}
|
||||
@@ -530,6 +611,7 @@ function TremorSettings() {
|
||||
|
||||
export function WebAppTremor() {
|
||||
const [page, setPage] = useState<Page>("Dashboard");
|
||||
const [theme, setTheme] = useState<ThemeColor>(TREMOR_THEMES[0]);
|
||||
const NAV: { label: Page; icon: string }[] = [
|
||||
{ label: "Dashboard", icon: "▦" },
|
||||
{ label: "Users", icon: "◎" },
|
||||
@@ -539,13 +621,13 @@ export function WebAppTremor() {
|
||||
<div className="flex h-full font-sans text-sm bg-white">
|
||||
<div className="w-44 border-r flex flex-col py-4 px-3 gap-1 shrink-0" style={{ background:"#fff" }}>
|
||||
<div className="flex items-center gap-2 px-2 mb-4">
|
||||
<div className="w-6 h-6 rounded" style={{ background:"#3b82f6" }} />
|
||||
<span className="font-bold text-xs" style={{ color:"#1e3a5f" }}>Acme Inc</span>
|
||||
<div className="w-6 h-6 rounded" style={{ background: theme.primary }} />
|
||||
<span className="font-bold text-xs" style={{ color: theme.activeFg }}>Acme Inc</span>
|
||||
</div>
|
||||
{NAV.map(({ label, icon }) => (
|
||||
<button key={label} onClick={() => setPage(label)}
|
||||
className="flex items-center gap-2 px-2 py-2 rounded text-xs cursor-pointer w-full text-left"
|
||||
style={page===label?{background:"#eff6ff",color:"#1d4ed8",fontWeight:600}:{color:"#6b7280"}}>
|
||||
style={page===label?{background: theme.activeBg, color: theme.activeFg, fontWeight:600}:{color:"#6b7280"}}>
|
||||
<span>{icon}</span>{label}
|
||||
</button>
|
||||
))}
|
||||
@@ -553,7 +635,10 @@ 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>
|
||||
<button className="h-7 px-3 rounded-lg text-[11px] font-medium text-white" style={{ background:"#2563eb" }}>+ New</button>
|
||||
<div className="flex items-center gap-3">
|
||||
<ThemeSwatches themes={TREMOR_THEMES} selected={theme} onSelect={setTheme} />
|
||||
<button className="h-7 px-3 rounded-lg text-[11px] font-medium" style={{ background: theme.primary, color: theme.primaryFg }}>+ New</button>
|
||||
</div>
|
||||
</div>
|
||||
{page === "Dashboard" && <TremorDashboard />}
|
||||
{page === "Users" && <TremorUsers />}
|
||||
|
||||
Reference in New Issue
Block a user