From 9fc643f9b6b6ce5ed79da6c00723f443bf9b3dce Mon Sep 17 00:00:00 2001 From: Mark Henderson Date: Mon, 2 Mar 2026 16:44:37 -0800 Subject: [PATCH] Restyle design page to match Stackless aesthetic - Replace all Tailwind/shadcn classes with inline styles - Use warm beige palette, Outfit/Newsreader fonts, Stackless card pattern - Replace Lucide icons with simple Unicode glyphs - Surface picker and left nav match the sidebar/activity visual language - Controls bar (library tabs, swatches, lock-in) restyled to match Made-with: Cursor --- .../project/[projectId]/design/page.tsx | 586 +++++++----------- 1 file changed, 218 insertions(+), 368 deletions(-) diff --git a/app/[workspace]/project/[projectId]/design/page.tsx b/app/[workspace]/project/[projectId]/design/page.tsx index e452f44..d2e4184 100644 --- a/app/[workspace]/project/[projectId]/design/page.tsx +++ b/app/[workspace]/project/[projectId]/design/page.tsx @@ -1,14 +1,7 @@ "use client"; import { use, useState, useEffect } from "react"; -import { Badge } from "@/components/ui/badge"; -import { Button } from "@/components/ui/button"; import { toast } from "sonner"; -import { cn } from "@/lib/utils"; -import { - Monitor, Globe, Settings, Smartphone, Mail, BookOpen, - Lock, CheckCircle2, Loader2, ChevronRight, Pencil, -} from "lucide-react"; import { SCAFFOLD_REGISTRY, THEME_REGISTRY, type ThemeColor } from "@/components/design-scaffolds"; // --------------------------------------------------------------------------- @@ -19,7 +12,7 @@ interface Surface { id: string; name: string; description: string; - icon: React.ElementType; + icon: string; themes: Theme[]; } @@ -29,269 +22,77 @@ interface Theme { description: string; tags: string[]; url: string; - preview: React.ReactNode; } -// Mini UI mockups — each styled to feel like the library -const ShadcnPreview = () => ( -
-
-
-
-
-
-
-
-
-
-
- Save - Cancel -
-
-); - -const MantiinePreview = () => ( -
-
-
- New -
-
-
-
-
-
- {[1,2].map(i => ( -
-
-
-
- ))} -
-
- Apply -
-
-); - -const HeroUIPreview = () => ( -
-
-
-
-
-
-
-
-
- - Get started → - -
-); - -const DaisyPreview = () => ( -
-
-
-
-
-
-
-
-
-
-
- Primary - Success -
-
-); - -const AcernityPreview = () => ( -
-
-
-
-
-
-
- Get started -
-
-); - -const TailwindPreview = () => ( -
-
className="flex…"
-
-
-
-
-
-
-
- Custom → -
-
-); - -const TremorPreview = () => ( -
-
-
-
-
-
- {[60, 80, 45, 90, 70, 55].map((h, i) => ( -
-
-
- ))} -
-
-); - -const NativewindPreview = () => ( -
-
-
-
-
-
-
-
-
-
-
- Button -
-
-
-
-); - -const GluestackPreview = () => ( -
-
-
-
-
-
-
-
-
- Submit -
-
-
-
-); - -const ReactEmailPreview = () => ( -
-
-
-
-
-
-
-
-
-
-
-
-
- Open App → -
-
-); - -const NextraPreview = () => ( -
-
- {['Getting started', 'Installation', 'API', 'Examples'].map(l => ( -
- ))} -
-
-
-
-
-
-
-); - const ALL_SURFACES: Surface[] = [ { id: "web-app", name: "Web App", - description: "The core product your users log into — dashboards, features, settings", - icon: Monitor, + icon: "⬡", + description: "The core product users log into — dashboards, features, settings", themes: [ - { id: "shadcn", name: "shadcn/ui", description: "Copy-paste components on Radix primitives. You own the code, fully customisable.", tags: ["Tailwind", "Radix", "Copy-paste"], url: "https://ui.shadcn.com", preview: }, - { id: "mantine", name: "Mantine", description: "100+ components with hooks, forms, charts. Best for data-heavy apps.", tags: ["React", "Charts", "Forms"], url: "https://mantine.dev", preview: }, - { id: "hero-ui", name: "HeroUI", description: "Beautiful, accessible components with smooth animations and dark mode.", tags: ["Tailwind", "Animations", "Accessible"], url: "https://heroui.com", preview: }, - { id: "tremor", name: "Tremor", description: "Dashboard components — charts, KPIs, tables — designed for analytics UIs.", tags: ["Charts", "Dashboard", "Analytics"], url: "https://tremor.so", preview: }, + { id: "shadcn", name: "shadcn/ui", description: "Copy-paste components on Radix primitives. You own the code, fully customisable.", tags: ["Tailwind", "Radix", "Copy-paste"], url: "https://ui.shadcn.com" }, + { id: "mantine", name: "Mantine", description: "100+ components with hooks, forms, charts. Best for data-heavy apps.", tags: ["React", "Charts", "Forms"], url: "https://mantine.dev" }, + { id: "hero-ui", name: "HeroUI", description: "Beautiful, accessible components with smooth animations and dark mode.", tags: ["Tailwind", "Animations", "Accessible"], url: "https://heroui.com" }, + { id: "tremor", name: "Tremor", description: "Dashboard components — charts, KPIs, tables — designed for analytics UIs.", tags: ["Charts", "Dashboard", "Analytics"], url: "https://tremor.so" }, ], }, { id: "marketing", name: "Marketing Site", + icon: "◎", description: "Public-facing landing page, blog, pricing — brand expression and conversion", - icon: Globe, themes: [ - { id: "daisy-ui", name: "DaisyUI", description: "Tailwind plugin with 48 built-in themes. Fastest path to a beautiful site.", tags: ["Tailwind", "Themes", "Plugin"], url: "https://daisyui.com", preview: }, - { id: "hero-ui", name: "HeroUI", description: "Beautiful components with gradients and smooth animations.", tags: ["Tailwind", "Animations", "Modern"], url: "https://heroui.com", preview: }, - { id: "aceternity", name: "Aceternity UI", description: "Animated, visually striking components for premium landing pages.", tags: ["Animations", "Dark", "Premium"], url: "https://ui.aceternity.com", preview: }, - { id: "tailwind-only", name: "Tailwind only", description: "No component library — full creative control with pure Tailwind CSS.", tags: ["Custom", "Flexible", "Minimal"], url: "https://tailwindcss.com", preview: }, + { id: "daisy-ui", name: "DaisyUI", description: "Tailwind plugin with 48 built-in themes. Fastest path to a beautiful site.", tags: ["Tailwind", "Themes", "Plugin"], url: "https://daisyui.com" }, + { id: "hero-ui", name: "HeroUI", description: "Beautiful components with gradients and smooth animations.", tags: ["Tailwind", "Animations", "Modern"], url: "https://heroui.com" }, + { id: "aceternity", name: "Aceternity UI", description: "Animated, visually striking components for premium landing pages.", tags: ["Animations", "Dark", "Premium"], url: "https://ui.aceternity.com" }, + { id: "tailwind-only", name: "Tailwind only", description: "No component library — full creative control with pure Tailwind CSS.", tags: ["Custom", "Flexible", "Minimal"], url: "https://tailwindcss.com" }, ], }, { id: "admin", name: "Admin Panel", + icon: "◫", description: "Internal tool for managing your business — users, support, billing, analytics", - icon: Settings, themes: [ - { id: "mantine", name: "Mantine", description: "The best choice for admin — comprehensive tables, forms, and data components.", tags: ["Tables", "Forms", "Charts"], url: "https://mantine.dev", preview: }, - { id: "shadcn", name: "shadcn/ui", description: "Clean, neutral components. Great if you want the admin to match the main app.", tags: ["Tailwind", "Consistent", "Clean"], url: "https://ui.shadcn.com", preview: }, - { id: "tremor", name: "Tremor", description: "Analytics-first — built for KPI dashboards, charts, and data tables.", tags: ["Analytics", "Charts", "KPIs"], url: "https://tremor.so", preview: }, + { id: "mantine", name: "Mantine", description: "The best choice for admin — comprehensive tables, forms, and data components.", tags: ["Tables", "Forms", "Charts"], url: "https://mantine.dev" }, + { id: "shadcn", name: "shadcn/ui", description: "Clean, neutral components. Great if you want the admin to match the main app.", tags: ["Tailwind", "Consistent", "Clean"], url: "https://ui.shadcn.com" }, + { id: "tremor", name: "Tremor", description: "Analytics-first — built for KPI dashboards, charts, and data tables.", tags: ["Analytics", "Charts", "KPIs"], url: "https://tremor.so" }, ], }, { id: "mobile", name: "Mobile App", + icon: "▢", description: "iOS and Android companion app — touch-first, native feel", - icon: Smartphone, themes: [ - { id: "nativewind", name: "NativeWind", description: "Use Tailwind CSS in React Native. Consistent style across web and mobile.", tags: ["Tailwind", "React Native", "Expo"], url: "https://nativewind.dev", preview: }, - { id: "gluestack", name: "Gluestack UI", description: "Universal components for React Native — accessible, well-tested, comprehensive.", tags: ["Universal", "Accessible", "Expo"], url: "https://gluestack.io", preview: }, + { id: "nativewind", name: "NativeWind", description: "Use Tailwind CSS in React Native. Consistent style across web and mobile.", tags: ["Tailwind", "React Native", "Expo"], url: "https://nativewind.dev" }, + { id: "gluestack", name: "Gluestack UI", description: "Universal components for React Native — accessible, well-tested, comprehensive.", tags: ["Universal", "Accessible", "Expo"], url: "https://gluestack.io" }, ], }, { id: "email", name: "Email", + icon: "✉", description: "Transactional and marketing emails — welcome, billing, notifications", - icon: Mail, themes: [ - { id: "react-email", name: "React Email", description: "Build emails with React components. Works with any email provider.", tags: ["React", "Resend", "Cross-client"], url: "https://react.email", preview: }, + { id: "react-email", name: "React Email", description: "Build emails with React components. Works with any email provider.", tags: ["React", "Resend", "Cross-client"], url: "https://react.email" }, ], }, { id: "docs", name: "Docs / Content", + icon: "☰", description: "Documentation, knowledge base, or blog for your product", - icon: BookOpen, themes: [ - { id: "nextra", name: "Nextra", description: "Next.js-based docs site. Markdown-first, fast, with great search.", tags: ["Next.js", "Markdown", "Search"], url: "https://nextra.site", preview: }, - { id: "shadcn", name: "shadcn/ui + custom", description: "Build a fully custom docs site that matches your product exactly.", tags: ["Custom", "Tailwind", "Flexible"], url: "https://ui.shadcn.com", preview: }, + { id: "nextra", name: "Nextra", description: "Next.js-based docs site. Markdown-first, fast, with great search.", tags: ["Next.js", "Markdown", "Search"], url: "https://nextra.site" }, + { id: "shadcn", name: "shadcn/ui + custom", description: "Build a fully custom docs site that matches your product exactly.", tags: ["Custom", "Tailwind", "Flexible"], url: "https://ui.shadcn.com" }, ], }, ]; // --------------------------------------------------------------------------- -// Surface section — tab toggle + scaffold preview + lock in +// Surface section // --------------------------------------------------------------------------- function SurfaceSection({ @@ -311,38 +112,54 @@ function SurfaceSection({ onUnlock: () => void; saving: boolean; }) { - // Active preview tab — if locked show that, otherwise the selected/first const previewId = lockedThemeId ?? selectedThemeId ?? surface.themes[0]?.id ?? null; const activeTheme = surface.themes.find(t => t.id === previewId); const ScaffoldComponent = previewId ? SCAFFOLD_REGISTRY[surface.id]?.[previewId] : null; - // Theme color variants for the active library (e.g. shadcn has 8 color themes) const availableColorThemes: ThemeColor[] = previewId ? (THEME_REGISTRY[surface.id]?.[previewId] ?? []) : []; const [selectedColorTheme, setSelectedColorTheme] = useState(null); const activeColorTheme = selectedColorTheme ?? availableColorThemes[0] ?? null; - return ( -
+ const isLocked = !!lockedThemeId; - {/* Scaffold preview — browser chrome frame */} -
-
-
-
-
-
- - {activeTheme ? `/${surface.id} — ${activeTheme.name}${activeColorTheme ? ` / ${activeColorTheme.label}` : ""}` : ""} + return ( +
+ + {/* Browser chrome + scaffold */} +
+ {/* Chrome bar */} +
+ {["#d0ccc4", "#d0ccc4", "#d0ccc4"].map((c, i) => ( +
+ ))} +
+ + {activeTheme ? `/${surface.id} · ${activeTheme.name}${activeColorTheme ? ` · ${activeColorTheme.label}` : ""}` : ""}
-
+ + {/* Scaffold */} +
{ScaffoldComponent ? : ( -
+
Select a library below to preview
) @@ -350,89 +167,123 @@ function SurfaceSection({
- {/* Controls bar — all below the render */} -
+ {/* Controls below render */} +
- {/* Row 1: library tabs */} -
+ {/* Library tabs */} +
{surface.themes.map(theme => { const isActive = theme.id === previewId; - const isLocked = theme.id === lockedThemeId; + const isThisLocked = theme.id === lockedThemeId; return ( ); })}
- {/* Row 2: color theme swatches (only if this library has color variants) */} + {/* Color swatches */} {availableColorThemes.length > 0 && ( -
- Theme -
+
+ Theme +
{availableColorThemes.map(ct => (
)} - {/* Row 3: description + tags + docs + lock in */} -
+ {/* Description + tags + docs + lock-in */} +
{activeTheme && ( <> -

{activeTheme.description}

-
+

+ {activeTheme.description} +

+
{activeTheme.tags.map(t => ( - {t} + + {t} + ))}
- + (e.currentTarget.style.color = "#1a1a1a")} + onMouseLeave={e => (e.currentTarget.style.color = "#a09a90")} + > Docs ↗ )} - {lockedThemeId ? ( - + {isLocked ? ( + ) : ( - + )}
@@ -444,13 +295,7 @@ function SurfaceSection({ // Phase 1 — Surface picker // --------------------------------------------------------------------------- -function SurfacePicker({ - onConfirm, - saving, -}: { - onConfirm: (ids: string[]) => void; - saving: boolean; -}) { +function SurfacePicker({ onConfirm, saving }: { onConfirm: (ids: string[]) => void; saving: boolean }) { const [selected, setSelected] = useState>(new Set()); const toggle = (id: string) => { @@ -462,60 +307,73 @@ function SurfacePicker({ }; return ( -
-
-

Design surfaces

-

- Which surfaces does your product need? Select all that apply — you can always add more later. -

-
+
+

+ Design surfaces +

+

+ Which surfaces does your product need? Select all that apply. +

-
+
{ALL_SURFACES.map(surface => { - const Icon = surface.icon; const isSelected = selected.has(surface.id); return ( ); })}
-
- - {selected.size === 0 && ( -

Select at least one surface to continue

- )} -
+ + {selected.size === 0 && ( +

+ Select at least one surface to continue +

+ )}
); } @@ -524,11 +382,7 @@ function SurfacePicker({ // Page // --------------------------------------------------------------------------- -export default function DesignPage({ - params, -}: { - params: Promise<{ workspace: string; projectId: string }>; -}) { +export default function DesignPage({ params }: { params: Promise<{ workspace: string; projectId: string }> }) { const { projectId } = use(params); const [surfaces, setSurfaces] = useState([]); @@ -595,79 +449,75 @@ export default function DesignPage({ }; const handleUnlock = (surfaceId: string) => { - setSurfaceThemes(prev => { - const next = { ...prev }; - delete next[surfaceId]; - return next; - }); + setSurfaceThemes(prev => { const next = { ...prev }; delete next[surfaceId]; return next; }); }; if (loading) { return ( -
- +
+
+
); } - // Phase 1 — no surfaces set yet if (surfaces.length === 0) { - return ( -
- -
- ); + return ; } - // Phase 2 — left nav + main content const activeSurfaces = ALL_SURFACES.filter(s => surfaces.includes(s.id)); const currentSurface = activeSurfaces.find(s => s.id === activeSurfaceId) ?? activeSurfaces[0]; const lockedCount = Object.keys(surfaceThemes).length; return ( -
+
+ {/* Left nav */} -
-
-

Surfaces

+
+
+
Surfaces
-