# Local Cal Redesign Implementation Plan > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. **Goal:** Rebuild `local-cal` as a Vercel-inspired product console with aligned desktop AI/timeline hierarchy, capture-first mobile behavior, redesigned primitives, and inline event validation states. **Architecture:** Start at the token layer in `src/app/globals.css` and the shell contract in `src/lib/ui-shell-contract.ts`, then update shared `ui/*` primitives so pages compose the new visual system instead of layering overrides onto the old glass design. After the primitives are stable, reshape `src/app/page.tsx`, `src/components/ai-toolbar.tsx`, `src/components/event-card.tsx`, `src/components/events-list.tsx`, `src/components/event-dialog.tsx`, and `src/components/settings-panel.tsx` to match the approved desktop/mobile hierarchy and validation behavior. **Tech Stack:** Next.js 15 App Router, React 19, Tailwind CSS v4, Bun test, Radix UI, Framer Motion --- ## File Structure Map ### Core styling and shell - Modify: `src/app/globals.css` - Replace glass-heavy tokens/utilities with the approved neutral product-console token system. - Modify: `src/lib/ui-shell-contract.ts` - Re-encode shell surface contracts for the new infrastructure bar, workspace surfaces, and utility controls. ### Shared primitives - Modify: `src/components/ui/button.tsx` - Update button variants toward Vercel-like surface, spacing, and focus rules. - Modify: `src/components/ui/card.tsx` - Replace default bordered card treatment with shadow-as-border primitives. - Modify: `src/components/ui/input.tsx` - Update input treatment to the new console field styling. - Modify: `src/components/ui/badge.tsx` - Convert badges toward functional metadata accents instead of generic pills everywhere. - Modify: `src/components/ui/dialog.tsx` - Move modal framing from generic bordered surfaces to console-style sheets. - Modify: `src/components/ui/dropdown-menu.tsx` - Align menu surfaces with the new token system so `More` and event actions match the redesign. ### Main app surfaces - Modify: `src/app/page.tsx` - Rebuild the page hierarchy into a thin infrastructure bar plus aligned top-row AI/timeline workspace. - Modify: `src/components/ai-toolbar.tsx` - Make text input and attachments co-equal and adapt desktop/mobile hierarchy. - Modify: `src/components/events-list.tsx` - Update empty state and list rhythm to match the new shell. - Modify: `src/components/event-card.tsx` - Replace workflow tags with functional metadata emphasis and inline warning treatment. - Modify: `src/components/event-dialog.tsx` - Redesign the dialog as a precise console form. - Modify: `src/components/settings-panel.tsx` - Demote settings to a secondary admin-like utility surface. ### Validation helpers - Modify: `src/lib/event-form.ts` - Expose a reusable event-level validation result that timeline cards can consume. - Modify: `src/lib/types.ts` - Add any minimal derived UI type needed for validation summaries if existing types are insufficient. ### Tests - Modify: `tests/ui-shell-contract.test.ts` - Lock down the new shell contract classes. - Modify: `tests/ai-toolbar.test.ts` - Update AI surface layout contracts around equal text/attachment importance. - Modify: `tests/event-card.test.ts` - Add timeline metadata and inline warning assertions. - Modify: `tests/event-dialog.test.tsx` - Add dialog contract checks for the redesigned grouping/surface. - Create: `tests/home-page-layout.test.ts` - Lock down the approved desktop/mobile hierarchy at the composition level. --- ### Task 1: Replace global tokens and shell contracts **Files:** - Modify: `src/app/globals.css` - Modify: `src/lib/ui-shell-contract.ts` - Modify: `tests/ui-shell-contract.test.ts` - [ ] **Step 1: Write the failing shell contract test** ```ts import { describe, expect, test } from "bun:test"; import { APP_ACTION_BAR_CLASSES, APP_HEADER_SURFACE_CLASSES, APP_NAV_SURFACE_CLASSES, APP_SECTION_SURFACE_CLASSES, } from "@/lib/ui-shell-contract"; describe("ui shell contract", () => { test("header surface is a thin structural bar instead of a glass panel", () => { expect(APP_HEADER_SURFACE_CLASSES).toContain("min-h-14"); expect(APP_HEADER_SURFACE_CLASSES).toContain("border-b"); expect(APP_HEADER_SURFACE_CLASSES).not.toContain("glass-surface"); }); test("section and action surfaces use tokenized shell classes instead of glass helpers", () => { expect(APP_SECTION_SURFACE_CLASSES).not.toContain("glass-panel"); expect(APP_ACTION_BAR_CLASSES).not.toContain("glass-subtle"); expect(APP_NAV_SURFACE_CLASSES).not.toContain("glass-surface"); }); }); ``` - [ ] **Step 2: Run test to verify it fails** Run: `bun test tests/ui-shell-contract.test.ts` Expected: FAIL because the shell contract still contains `glass-*` classes and lacks the new structural bar classes. - [ ] **Step 3: Replace the shell classes with the approved product-console contract** ```ts import { cn } from "@/lib/utils"; export const APP_HEADER_SURFACE_CLASSES = "mb-6 flex min-h-14 items-center justify-between gap-3 border-b border-foreground/10 bg-background/95 px-4 py-3 sm:px-6"; export const APP_SECTION_SURFACE_CLASSES = "rounded-[10px] bg-card px-4 py-4 shadow-[0_0_0_1px_rgba(0,0,0,0.08),0_2px_2px_rgba(0,0,0,0.04),0_0_0_1px_#fafafa] sm:px-5"; export const APP_ACTION_BAR_CLASSES = "rounded-[10px] bg-card px-3 py-3 shadow-[0_0_0_1px_rgba(0,0,0,0.08)]"; export const APP_NAV_SURFACE_CLASSES = "fixed inset-x-4 bottom-4 mx-auto flex max-w-3xl items-center justify-between rounded-[10px] bg-background/95 px-3 py-2 shadow-[0_0_0_1px_rgba(0,0,0,0.08),0_8px_24px_rgba(0,0,0,0.08)] sm:inset-x-6 lg:hidden"; const CONNECTION_BADGE_BASE_CLASSES = "gap-1.5 rounded-full px-2.5 py-1 text-xs font-medium shadow-[0_0_0_1px_rgba(0,0,0,0.08)]"; export const getConnectionBadgeClasses = (isOnline: boolean) => cn( CONNECTION_BADGE_BASE_CLASSES, isOnline ? "bg-[#ebf5ff] text-[#0068d6]" : "bg-muted text-muted-foreground", ); ``` - [ ] **Step 4: Replace glass-centric global tokens with the approved light/dark console tokens** ```css :root { --background: #ffffff; --foreground: #171717; --card: #ffffff; --card-foreground: #171717; --popover: #ffffff; --popover-foreground: #171717; --primary: #171717; --primary-foreground: #ffffff; --secondary: #fafafa; --secondary-foreground: #171717; --muted: #fafafa; --muted-foreground: #666666; --accent: #f5f5f5; --accent-foreground: #171717; --destructive: #ff5b4f; --destructive-foreground: #ffffff; --border: #ebebeb; --input: #ebebeb; --ring: hsla(212, 100%, 48%, 1); --radius: 0.625rem; --shadow-shell: 0 0 0 1px rgba(0, 0, 0, 0.08); --shadow-card: 0 0 0 1px rgba(0, 0, 0, 0.08), 0 2px 2px rgba(0, 0, 0, 0.04), 0 8px 8px -8px rgba(0, 0, 0, 0.04), 0 0 0 1px #fafafa; } .dark { --background: #111111; --foreground: #f5f5f5; --card: #171717; --card-foreground: #f5f5f5; --popover: #171717; --popover-foreground: #f5f5f5; --primary: #f5f5f5; --primary-foreground: #171717; --secondary: #1f1f1f; --secondary-foreground: #f5f5f5; --muted: #1a1a1a; --muted-foreground: #a1a1a1; --accent: #1f1f1f; --accent-foreground: #f5f5f5; --border: rgba(255, 255, 255, 0.1); --input: rgba(255, 255, 255, 0.12); } ``` - [ ] **Step 5: Run test to verify it passes** Run: `bun test tests/ui-shell-contract.test.ts` Expected: PASS with the new shell contract assertions satisfied. - [ ] **Step 6: Commit** ```bash git add src/app/globals.css src/lib/ui-shell-contract.ts tests/ui-shell-contract.test.ts git commit -m "feat: add product console shell tokens" ``` ### Task 2: Redesign shared UI primitives **Files:** - Modify: `src/components/ui/button.tsx` - Modify: `src/components/ui/card.tsx` - Modify: `src/components/ui/input.tsx` - Modify: `src/components/ui/badge.tsx` - Modify: `src/components/ui/dialog.tsx` - Modify: `src/components/ui/dropdown-menu.tsx` - Test: `tests/event-dialog.test.tsx` - [ ] **Step 1: Write the failing dialog and primitive contract test** ```ts import { describe, expect, test } from "bun:test"; import { renderToStaticMarkup } from "react-dom/server"; import { DialogContent, DialogDescription, DialogHeader, DialogTitle, } from "@/components/ui/dialog"; describe("dialog primitive redesign", () => { test("dialog content uses console surface classes instead of generic border+shadow", () => { const markup = renderToStaticMarkup( New Event Console form , ); expect(markup).toContain("rounded-[10px]"); expect(markup).toContain("shadow-[0_0_0_1px_rgba(0,0,0,0.08)"); expect(markup).not.toContain("rounded-lg border p-6 shadow-lg"); }); }); ``` - [ ] **Step 2: Run test to verify it fails** Run: `bun test tests/event-dialog.test.tsx` Expected: FAIL because the dialog still renders the old generic bordered modal classes. - [ ] **Step 3: Update buttons, cards, inputs, badges, and dialog primitives to the new system** ```ts const buttonVariants = cva( "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-[6px] text-sm font-medium transition-[background-color,color,box-shadow] disabled:pointer-events-none disabled:opacity-50 focus-visible:outline-none focus-visible:ring-[3px] focus-visible:ring-ring/30", { variants: { variant: { default: "bg-primary text-primary-foreground hover:opacity-92", outline: "bg-background text-foreground shadow-[0_0_0_1px_rgba(0,0,0,0.08)] hover:bg-accent", ghost: "text-muted-foreground hover:bg-accent hover:text-foreground", link: "text-[#0072f5] underline-offset-4 hover:underline", }, }, }, ); function Card({ className, ...props }: React.ComponentProps<"div">) { return (
); } function Input({ className, type, ...props }: React.ComponentProps<"input">) { return ( ); } ``` - [ ] **Step 4: Run test to verify it passes** Run: `bun test tests/event-dialog.test.tsx` Expected: PASS with the redesigned dialog surface contract in the markup. - [ ] **Step 5: Commit** ```bash git add src/components/ui/button.tsx src/components/ui/card.tsx src/components/ui/input.tsx src/components/ui/badge.tsx src/components/ui/dialog.tsx src/components/ui/dropdown-menu.tsx tests/event-dialog.test.tsx git commit -m "feat: redesign shared ui primitives" ``` ### Task 3: Rebuild the home page hierarchy **Files:** - Modify: `src/app/page.tsx` - Create: `tests/home-page-layout.test.ts` - [ ] **Step 1: Write the failing home page hierarchy test** ```ts import { describe, expect, test } from "bun:test"; import { readFileSync } from "node:fs"; describe("home page hierarchy", () => { test("desktop layout defines aligned AI and timeline top-row sections", () => { const source = readFileSync("src/app/page.tsx", "utf8"); expect(source).toContain("lg:grid-cols-[minmax(0,0.75fr)_minmax(0,1.25fr)]"); expect(source).toContain("AI capture"); expect(source).toContain("Event timeline"); }); test("manual create is routed through a More menu instead of a primary mobile action", () => { const source = readFileSync("src/app/page.tsx", "utf8"); expect(source).toContain("More"); expect(source).not.toContain("New Event"); }); }); ``` - [ ] **Step 2: Run test to verify it fails** Run: `bun test tests/home-page-layout.test.ts` Expected: FAIL because the page still uses the old stacked shell and direct nav buttons. - [ ] **Step 3: Reshape `page.tsx` to the approved infrastructure bar plus aligned workspace** ```tsx const APP_FRAME_CLASSES = "mx-auto flex min-h-screen w-full max-w-6xl flex-col px-4 pb-20 pt-4 sm:px-6 lg:px-8";

Local Calendar

Event timeline

{isOnline ? "Online" : "Offline"} {/* More: Import, Manual create, Settings */}
{/* timeline */}
``` - [ ] **Step 4: Run test to verify it passes** Run: `bun test tests/home-page-layout.test.ts` Expected: PASS with the aligned desktop workspace and secondary `More` path visible in source. - [ ] **Step 5: Commit** ```bash git add src/app/page.tsx tests/home-page-layout.test.ts git commit -m "feat: rebuild home page hierarchy" ``` ### Task 4: Redesign AI capture as a first-class dual-input surface **Files:** - Modify: `src/components/ai-toolbar.tsx` - Modify: `tests/ai-toolbar.test.ts` - [ ] **Step 1: Write the failing AI capture contract test** ```ts import { describe, expect, test } from "bun:test"; import { cn } from "@/lib/utils"; const COMPOSER_LAYOUT_CLASSES = "grid gap-3 lg:grid-cols-[minmax(0,1fr)_minmax(0,1fr)]"; const ATTACHMENTS_PANEL_CLASSES = "rounded-[10px] bg-card p-3 shadow-[0_0_0_1px_rgba(0,0,0,0.08)]"; describe("AI capture redesign", () => { test("desktop composer treats prompt and attachments as peer panels", () => { expect(cn(COMPOSER_LAYOUT_CLASSES)).toContain("lg:grid-cols-[minmax(0,1fr)_minmax(0,1fr)]"); }); test("attachments panel is a first-class surfaced region, not an inline footer affordance", () => { expect(cn(ATTACHMENTS_PANEL_CLASSES)).toContain("rounded-[10px]"); expect(cn(ATTACHMENTS_PANEL_CLASSES)).toContain("shadow-[0_0_0_1px_rgba(0,0,0,0.08)]"); }); }); ``` - [ ] **Step 2: Run test to verify it fails** Run: `bun test tests/ai-toolbar.test.ts` Expected: FAIL because the current toolbar still treats attachments as subordinate controls beneath the composer. - [ ] **Step 3: Refactor the toolbar layout around peer prompt and attachment regions** ```tsx

AI capture

Describe or attach event details