From 6a2477656c88826f1112c1aea4e69b0aa954e6bc Mon Sep 17 00:00:00 2001 From: Chris Testa Date: Sat, 7 Feb 2026 15:49:52 +0000 Subject: [PATCH 01/16] Add GTM planning and marketing website First step on the go-to-market path: GTM strategy doc and Vercel-based marketing site with Next.js, shadcn/ui, and GitHub Pages deployment workflow. Co-Authored-By: Claude Opus 4.6 --- planning/gtm.md | 226 + website/.github/workflows/deploy-pages.yml | 62 + website/.gitignore | 12 + .../specialists-dilemma/page.tsx | 219 + website/app/book/layout.tsx | 83 + website/app/globals.css | 94 + website/app/layout.tsx | 35 + website/app/page.tsx | 350 ++ website/components.json | 21 + website/components/book-nav.tsx | 147 + website/components/email-signup.tsx | 56 + website/components/mermaid-diagram.tsx | 70 + website/components/theme-provider.tsx | 11 + website/components/theme-toggle.tsx | 34 + website/components/ui/accordion.tsx | 58 + website/components/ui/alert-dialog.tsx | 141 + website/components/ui/alert.tsx | 59 + website/components/ui/aspect-ratio.tsx | 7 + website/components/ui/avatar.tsx | 50 + website/components/ui/badge.tsx | 37 + website/components/ui/breadcrumb.tsx | 115 + website/components/ui/button.tsx | 57 + website/components/ui/calendar.tsx | 66 + website/components/ui/card.tsx | 79 + website/components/ui/carousel.tsx | 262 + website/components/ui/chart.tsx | 365 ++ website/components/ui/checkbox.tsx | 30 + website/components/ui/collapsible.tsx | 11 + website/components/ui/command.tsx | 153 + website/components/ui/context-menu.tsx | 200 + website/components/ui/dialog.tsx | 122 + website/components/ui/drawer.tsx | 118 + website/components/ui/dropdown-menu.tsx | 200 + website/components/ui/form.tsx | 178 + website/components/ui/hover-card.tsx | 29 + website/components/ui/input-otp.tsx | 71 + website/components/ui/input.tsx | 22 + website/components/ui/label.tsx | 26 + website/components/ui/menubar.tsx | 236 + website/components/ui/navigation-menu.tsx | 128 + website/components/ui/pagination.tsx | 117 + website/components/ui/popover.tsx | 31 + website/components/ui/progress.tsx | 28 + website/components/ui/radio-group.tsx | 44 + website/components/ui/resizable.tsx | 45 + website/components/ui/scroll-area.tsx | 48 + website/components/ui/select.tsx | 160 + website/components/ui/separator.tsx | 31 + website/components/ui/sheet.tsx | 141 + website/components/ui/sidebar.tsx | 771 +++ website/components/ui/skeleton.tsx | 15 + website/components/ui/slider.tsx | 28 + website/components/ui/sonner.tsx | 31 + website/components/ui/switch.tsx | 29 + website/components/ui/table.tsx | 117 + website/components/ui/tabs.tsx | 55 + website/components/ui/textarea.tsx | 22 + website/components/ui/toast.tsx | 129 + website/components/ui/toaster.tsx | 35 + website/components/ui/toggle-group.tsx | 61 + website/components/ui/toggle.tsx | 45 + website/components/ui/tooltip.tsx | 30 + website/components/ui/use-mobile.tsx | 19 + website/components/ui/use-toast.ts | 191 + website/hooks/use-mobile.tsx | 19 + website/hooks/use-toast.ts | 191 + website/lib/book-data.ts | 112 + website/lib/utils.ts | 6 + website/next.config.mjs | 13 + website/package.json | 72 + website/pnpm-lock.yaml | 4709 +++++++++++++++++ website/postcss.config.mjs | 8 + website/public/placeholder-logo.svg | 1 + website/public/placeholder.svg | 1 + website/styles/globals.css | 94 + website/tailwind.config.ts | 96 + website/tsconfig.json | 33 + 77 files changed, 11818 insertions(+) create mode 100644 planning/gtm.md create mode 100644 website/.github/workflows/deploy-pages.yml create mode 100644 website/.gitignore create mode 100644 website/app/book/foundations/renaissance-developer/specialists-dilemma/page.tsx create mode 100644 website/app/book/layout.tsx create mode 100644 website/app/globals.css create mode 100644 website/app/layout.tsx create mode 100644 website/app/page.tsx create mode 100644 website/components.json create mode 100644 website/components/book-nav.tsx create mode 100644 website/components/email-signup.tsx create mode 100644 website/components/mermaid-diagram.tsx create mode 100644 website/components/theme-provider.tsx create mode 100644 website/components/theme-toggle.tsx create mode 100644 website/components/ui/accordion.tsx create mode 100644 website/components/ui/alert-dialog.tsx create mode 100644 website/components/ui/alert.tsx create mode 100644 website/components/ui/aspect-ratio.tsx create mode 100644 website/components/ui/avatar.tsx create mode 100644 website/components/ui/badge.tsx create mode 100644 website/components/ui/breadcrumb.tsx create mode 100644 website/components/ui/button.tsx create mode 100644 website/components/ui/calendar.tsx create mode 100644 website/components/ui/card.tsx create mode 100644 website/components/ui/carousel.tsx create mode 100644 website/components/ui/chart.tsx create mode 100644 website/components/ui/checkbox.tsx create mode 100644 website/components/ui/collapsible.tsx create mode 100644 website/components/ui/command.tsx create mode 100644 website/components/ui/context-menu.tsx create mode 100644 website/components/ui/dialog.tsx create mode 100644 website/components/ui/drawer.tsx create mode 100644 website/components/ui/dropdown-menu.tsx create mode 100644 website/components/ui/form.tsx create mode 100644 website/components/ui/hover-card.tsx create mode 100644 website/components/ui/input-otp.tsx create mode 100644 website/components/ui/input.tsx create mode 100644 website/components/ui/label.tsx create mode 100644 website/components/ui/menubar.tsx create mode 100644 website/components/ui/navigation-menu.tsx create mode 100644 website/components/ui/pagination.tsx create mode 100644 website/components/ui/popover.tsx create mode 100644 website/components/ui/progress.tsx create mode 100644 website/components/ui/radio-group.tsx create mode 100644 website/components/ui/resizable.tsx create mode 100644 website/components/ui/scroll-area.tsx create mode 100644 website/components/ui/select.tsx create mode 100644 website/components/ui/separator.tsx create mode 100644 website/components/ui/sheet.tsx create mode 100644 website/components/ui/sidebar.tsx create mode 100644 website/components/ui/skeleton.tsx create mode 100644 website/components/ui/slider.tsx create mode 100644 website/components/ui/sonner.tsx create mode 100644 website/components/ui/switch.tsx create mode 100644 website/components/ui/table.tsx create mode 100644 website/components/ui/tabs.tsx create mode 100644 website/components/ui/textarea.tsx create mode 100644 website/components/ui/toast.tsx create mode 100644 website/components/ui/toaster.tsx create mode 100644 website/components/ui/toggle-group.tsx create mode 100644 website/components/ui/toggle.tsx create mode 100644 website/components/ui/tooltip.tsx create mode 100644 website/components/ui/use-mobile.tsx create mode 100644 website/components/ui/use-toast.ts create mode 100644 website/hooks/use-mobile.tsx create mode 100644 website/hooks/use-toast.ts create mode 100644 website/lib/book-data.ts create mode 100644 website/lib/utils.ts create mode 100644 website/next.config.mjs create mode 100644 website/package.json create mode 100644 website/pnpm-lock.yaml create mode 100644 website/postcss.config.mjs create mode 100644 website/public/placeholder-logo.svg create mode 100644 website/public/placeholder.svg create mode 100644 website/styles/globals.css create mode 100644 website/tailwind.config.ts create mode 100644 website/tsconfig.json diff --git a/planning/gtm.md b/planning/gtm.md new file mode 100644 index 0000000..24f97f2 --- /dev/null +++ b/planning/gtm.md @@ -0,0 +1,226 @@ +# Go-to-Market Strategy: Agentic Coding Playbook + +**Created**: 2026-02-06 +**Author**: Chris Testa +**Goal**: Establish credibility and thought leadership in agentic coding by shipping Part 1: Foundations as a polished, free release — then build an audience for Parts 2-4. + +--- + +## Brand Identity + +**Brand Name**: Agentic Coding Playbook +**Positioning**: The definitive guide to building software products with AI agents — written by a CTO who's done it. +**Voice**: Authoritative but accessible. Opinionated but practical. Talks to vibecoders and CTOs in the same sentence without condescending to either. +**Core Message**: The bottleneck isn't code anymore. It's knowing what to build. + +### Key Concepts to Lead With + +These are the book's most original, shareable ideas — they become the brand's intellectual property: + +1. **The Renaissance Developer** — Breadth over depth. The new competitive advantage is being good enough at everything. +2. **The Acceleration Paradox** — When coding gets 5-10x faster, five new bottlenecks emerge that nobody talks about. +3. **The Digestibility Principle** — Human working memory and AI context windows have the same constraints. Design for both. +4. **Specification-Driven Development** — The quality of your descriptions determines the quality of your product. +5. **The "Good Enough at Everything" Principle** — Why 70% competency across 5 domains beats 95% in one. + +--- + +## Launch Strategy + +### Phase 1: Polish the Product + +Ship Part 1 as a professional, polished reading experience — not a repo README. + +#### Website + +- [ ] Export v0 design and adapt for GitHub Pages deployment +- [ ] Build a landing page that sells the book's ideas (not just lists chapters) + - Hero: "The bottleneck isn't code anymore" + - Social proof section (once available) + - Email capture: "Get notified when Part 2 drops" + - Clear CTA to start reading Part 1 +- [ ] Polish the reading experience — clean typography, chapter navigation, mobile-friendly +- [ ] Add proper meta tags (OpenGraph, Twitter cards) so links look good when shared +- [ ] Set up a custom domain (optional but more professional than testaco.github.io) + +#### Content Cleanup + +- [ ] Update author to "Chris Testa" across all frontmatter (50+ files) +- [ ] Change `status: draft` to `status: published` in all Part 1 files +- [ ] Fix placeholder text in Chapter 1 Further Reading (6 instances) +- [ ] Clean up TABLE_OF_CONTENTS.md — remove Part 2-4 stub entries +- [ ] Update navigation.yml — remove broken Part 2-4 chapter links +- [ ] Update preface.md — frame as Part 1 release, Parts 2-4 coming +- [ ] Update index.md — status table shows Part 1 as Published +- [ ] Update README.md — author, status, links + +#### PDF + +- [ ] Generate a clean PDF of Part 1 +- [ ] Add a cover page / title page +- [ ] Verify formatting, code blocks, and diagrams render properly +- [ ] Create a GitHub Release with the PDF attached + +#### Email Capture + +- [ ] Set up an email list (Buttondown, Substack, ConvertKit, or similar) +- [ ] Embed signup form on the landing page +- [ ] Create a welcome email: "Here's Part 1 + what's coming in Parts 2-4" + +--- + +### Phase 2: Seed the Brand + +Before the "launch day," establish the brand presence and start putting ideas out there. This builds an audience that's primed when the site goes live. + +#### Brand Accounts + +- [ ] Create X/Twitter account for Agentic Coding Playbook (or use personal + brand hashtag) +- [ ] Create LinkedIn page or start posting from personal with brand framing +- [ ] Set up Substack or newsletter (doubles as blog + email list) +- [ ] Create GitHub org or ensure repo is public and polished + +#### Content Seeding (Pre-Launch) + +The book has 70K words of content. Extract the best ideas as standalone posts: + +- [ ] **Thread 1: The Renaissance Developer** — "The most valuable developer in 2026 isn't a specialist. Here's why..." Pull from Chapter 1. +- [ ] **Thread 2: The 5 New Bottlenecks** — "When AI makes coding 5-10x faster, 5 new bottlenecks emerge that nobody talks about." Pull from Chapter 5. +- [ ] **Thread 3: The Digestibility Principle** — "Your brain and GPT-4 have the same architectural constraint. Here's how to design for both." Pull from Chapter 4. +- [ ] **Thread 4: What Agentic Coding Actually Is** — "Everyone's talking about AI coding tools. Almost nobody understands what 'agentic coding' actually means." Pull from Chapter 2. +- [ ] **Thread 5: Architecture for the AI Era** — "Most codebases are designed for humans to write. Here's how to design them for AI to write and humans to review." Pull from Chapter 3. +- [ ] **Blog post**: Expand one thread into a long-form article (best candidate: Renaissance Developer or 5 New Bottlenecks) + +#### Community Presence + +- [ ] Join and lurk in relevant communities before posting (authenticity matters): + - Indie Hackers + - r/SideProject, r/programming, r/artificial + - Hacker News + - Dev.to + - Claude/Anthropic Discord or community spaces +- [ ] Contribute value first — answer questions about agentic coding, share insights without linking the book +- [ ] Build a list of 10-20 relevant community threads where the book's ideas would genuinely add value + +--- + +### Phase 3: Launch + +Coordinate a launch window where the site goes live and content hits multiple channels simultaneously. + +#### Launch Day Checklist + +- [ ] Site is live, polished, and tested on mobile +- [ ] PDF is downloadable from GitHub Releases +- [ ] Email capture is working +- [ ] OpenGraph/Twitter card previews tested and looking good +- [ ] All social accounts are set up and have some prior content (not brand new empty accounts) + +#### Launch Posts + +- [ ] **Hacker News**: "Show HN: The Agentic Coding Playbook — free book on building products with AI agents" + - Best if posted mid-morning US time, Tuesday-Thursday + - Title should emphasize "free" and "playbook" — HN values substance + - Have a top comment ready explaining the meta story (book built with its own methodology) +- [ ] **Reddit**: Post to r/programming, r/SideProject, r/ChatGPTCoding, r/ClaudeAI + - Different framing for each subreddit + - Focus on value, not promotion +- [ ] **Indie Hackers**: Launch post with the story — CTO writing a book using AI about using AI +- [ ] **X/Twitter**: Launch thread from brand account + personal amplification +- [ ] **LinkedIn**: Article or post targeting engineering leaders and CTOs +- [ ] **Dev.to**: Publish the best blog post (Renaissance Developer or New Bottlenecks) with link to full book +- [ ] **Substack/Newsletter**: Send launch announcement to any early subscribers + +#### The Meta Story (Your Secret Weapon) + +The most compelling marketing angle: **this book was built using its own methodology**. The planning docs are visible. The git history shows the process. The AI assistance is transparent. This is the proof that the approach works — and it's a story that communities love. + +Lead with: "I'm a CTO. I wrote a book about building products with AI. The book itself was built using the workflow it teaches. Here's Part 1 — free." + +--- + +### Phase 4: Sustain and Build to Parts 2-4 + +After launch, keep momentum while writing the rest. + +#### Ongoing Content + +- [ ] Weekly or biweekly posts pulling ideas from Part 1 (you have months of content to extract) +- [ ] "Behind the scenes" posts about writing Parts 2-4 using the methodology +- [ ] Respond to comments and discussions — thought leadership is built in conversations, not broadcasts +- [ ] Guest posts or podcast appearances if opportunities arise (don't chase these, let them come from visibility) + +#### Audience Building + +- [ ] Track email signups — this is the core metric for Part 2 launch readiness +- [ ] Engage with people who share or comment on the content +- [ ] Collect feedback on Part 1 — what resonated, what's missing, what should Part 2 prioritize + +#### Parts 2-4 Teasers + +- [ ] Monthly update to email list: "Here's what I'm working on in Part 2" +- [ ] Share draft excerpts from Part 2 as they're written — builds anticipation and gets feedback +- [ ] When Part 2 is ready, repeat Phase 3 launch process with the built-in audience + +--- + +## Success Metrics + +### Launch (First 30 Days) + +| Metric | Target | How to Measure | +|--------|--------|----------------| +| Unique site visitors | 5,000+ | GitHub Pages analytics or added analytics | +| Email signups | 500+ | Email platform dashboard | +| GitHub stars | 200+ | Repository stats | +| Social impressions | 50,000+ | Platform analytics | +| Community engagement | 3+ substantive discussions | HN comments, Reddit threads, etc. | + +### Credibility Indicators (First 90 Days) + +| Indicator | What It Looks Like | +|-----------|-------------------| +| Thought leadership | People reference "Renaissance Developer" or "Digestibility Principle" unprompted | +| Inbound interest | Speaking invitations, podcast requests, collaboration offers | +| Community recognition | Recognized name in agentic coding discussions | +| Content resonance | Posts get shared and discussed organically | + +--- + +## Timeline + +| Week | Focus | Key Deliverable | +|------|-------|-----------------| +| 1 | Polish product | Site live with v0 design, content cleaned up | +| 1 | Brand setup | Accounts created, first 2 seed posts published | +| 2 | Content seeding | 3-5 threads/posts published, community presence established | +| 3 | Launch | Coordinated launch across all channels | +| 4+ | Sustain | Weekly content, audience engagement, start Part 2 | + +--- + +## Anti-Patterns to Avoid + +**Don't**: Post "check out my book" with a link and nothing else. Communities hate this. +**Do**: Share a genuinely useful insight, and mention the book as context. + +**Don't**: Post the same content everywhere simultaneously. It looks like spam. +**Do**: Adapt the message for each channel's culture and audience. + +**Don't**: Obsess over metrics in the first week. Launches are noisy. +**Do**: Focus on whether the ideas resonate in conversations. + +**Don't**: Wait until everything is perfect. Part 1 is good. Ship it. +**Do**: Iterate the site and marketing based on what you learn post-launch. + +**Don't**: Burn out trying to be everywhere. Consistency beats intensity. +**Do**: Pick 2 channels that feel natural and go deep there. Expand later. + +--- + +## Open Questions + +- [ ] Custom domain? (e.g., agenticcodingplaybook.com) +- [ ] Monetization for Parts 2-4? (Free, paid, freemium with Part 1 free?) +- [ ] Analytics setup? (Simple analytics, Plausible, or just GitHub traffic stats?) +- [ ] Will Parts 2-4 also be open source / CC BY 4.0, or shift to paid? diff --git a/website/.github/workflows/deploy-pages.yml b/website/.github/workflows/deploy-pages.yml new file mode 100644 index 0000000..c335112 --- /dev/null +++ b/website/.github/workflows/deploy-pages.yml @@ -0,0 +1,62 @@ +name: Deploy Website to GitHub Pages + +on: + push: + branches: [main] + paths: + - "website/**" + - ".github/workflows/deploy-pages.yml" + workflow_dispatch: + +permissions: + contents: read + pages: write + id-token: write + +concurrency: + group: "pages" + cancel-in-progress: false + +jobs: + build: + runs-on: ubuntu-latest + defaults: + run: + working-directory: ./website + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: "20" + cache: "npm" + cache-dependency-path: ./website/package-lock.json + + - name: Setup Pages + uses: actions/configure-pages@v5 + + - name: Install dependencies + run: npm ci + + - name: Build + run: npx next build + env: + NEXT_PUBLIC_BASE_PATH: "" + + - name: Upload artifact + uses: actions/upload-pages-artifact@v3 + with: + path: ./website/out + + deploy: + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + needs: build + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 diff --git a/website/.gitignore b/website/.gitignore new file mode 100644 index 0000000..fe447fb --- /dev/null +++ b/website/.gitignore @@ -0,0 +1,12 @@ +# v0 runtime files (should only exist in preview, not in git) +__v0_runtime_loader.js +__v0_devtools.tsx +__v0_jsx-dev-runtime.ts +instrumentation-client.js +instrumentation-client.ts + +# Common ignores +node_modules/ +.next/ +.env*.local +.DS_Store \ No newline at end of file diff --git a/website/app/book/foundations/renaissance-developer/specialists-dilemma/page.tsx b/website/app/book/foundations/renaissance-developer/specialists-dilemma/page.tsx new file mode 100644 index 0000000..ca067fe --- /dev/null +++ b/website/app/book/foundations/renaissance-developer/specialists-dilemma/page.tsx @@ -0,0 +1,219 @@ +import { MermaidDiagram } from "@/components/mermaid-diagram" +import { ChevronLeft, ChevronRight } from "lucide-react" +import Link from "next/link" +import { Button } from "@/components/ui/button" +import { getFlatSections } from "@/lib/book-data" + +export const metadata = { + title: "The Specialist's Dilemma | The Renaissance Developer | Agentic Coding Playbook", + description: + "When AI agents can handle implementation, deep specialization in coding becomes less valuable. Learn about the shift from coding to knowing what to build.", +} + +const mermaidChart = `graph TB + subgraph "Traditional Development (Pre-AI)" + T1[Product Idea] --> T2[Write Code
⏱️ BOTTLENECK] + T2 --> T3[Debug & Test
⏱️ Time-consuming] + T3 --> T4[Deploy] + end + + subgraph "AI-Era Development (2024+)" + A1[Product Idea] --> A2[Define What to Build
⏱️ BOTTLENECK] + A2 --> A3[AI Implements Code
⚡ Minutes] + A3 --> A4[Review & Validate] + A4 --> A5[Deploy] + end + + style T2 fill:#ff6b6b,stroke:#c92a2a,color:#fff + style A2 fill:#ff6b6b,stroke:#c92a2a,color:#fff + style A3 fill:#51cf66,stroke:#2f9e44,color:#fff` + +export default function SpecialistsDilemmaPage() { + const flat = getFlatSections() + const currentHref = "/book/foundations/renaissance-developer/specialists-dilemma" + const currentIdx = flat.findIndex((s) => s.href === currentHref) + const prev = currentIdx > 0 ? flat[currentIdx - 1] : null + const next = currentIdx < flat.length - 1 ? flat[currentIdx + 1] : null + + return ( +
+ {/* Breadcrumb */} +
+ + Home + + / + Part 1: Foundations + / + Chapter 1 +
+ + {/* Header */} +
+

Section 1.1.1

+

+ The Specialist's Dilemma +

+

+ Chapter 1: The Renaissance Developer +

+
+ + {/* Content */} +
+

+ You've spent five years becoming an expert React developer. You know the framework + inside and out—hooks, context, performance optimization, the works. You can debug the + gnarliest state management issues and architect scalable component hierarchies in your + sleep. You're valuable because you're deep. +

+ +

+ But something changed in 2024. +

+ +

+ You ask Claude Code to "build a React component for user authentication with social + login," and it generates production-ready code in 30 seconds. Code that would have + taken you an hour. Code that's clean, well-structured, and follows best practices. + Sometimes it's better than what you would have written. +

+ +

So what's your role now?

+ +

+ This is the specialist's dilemma:{" "} + + when AI agents can handle implementation, deep specialization in coding becomes less + valuable + + . The bottleneck in software development is shifting from "writing code" to + "knowing what to build and why." The Renaissance Developer recognizes this shift + and adapts accordingly. +

+ +

Why Specialization Made Sense

+ +

+ For the past two decades, the software industry rewarded depth. Companies needed backend + experts who could optimize database queries, frontend specialists who mastered browser + quirks, DevOps engineers who understood infrastructure at scale. This made perfect sense + when: +

+ +
    +
  • + Coding was the bottleneck: Writing and + debugging code consumed most project time +
  • +
  • + Tools were primitive: No AI assistance, + minimal automation, steep learning curves +
  • +
  • + Teams were siloed: Backend, frontend, + design, and product were separate departments +
  • +
  • + Time-to-market was measured in months or years + : Speed wasn't the primary competitive advantage +
  • +
+ +

+ In that world, being a 10x engineer meant writing code 10x faster, knowing your domain 10x + deeper, debugging 10x more efficiently. Specialization delivered real value. +

+ +

But the world changed.

+ +

The New Reality

+ +

Today, AI agents can:

+ +
    +
  • Generate boilerplate code instantly
  • +
  • Implement APIs from specifications in minutes
  • +
  • Write tests automatically
  • +
  • Refactor codebases while maintaining behavior
  • +
  • Translate designs into working UIs
  • +
  • Debug issues by analyzing stack traces and suggesting fixes
  • +
+ +

+ What used to take a specialized engineer hours or days now takes minutes.{" "} + + The bottleneck is no longer coding—it's knowing what to code + + . +

+ + + +

+ This creates a paradox for specialists: your expertise in how to code is + increasingly commoditized, while your expertise in what to build becomes more + valuable. But traditional specialization focused on the former, not the latter. +

+ +

+ + The question isn't "can you write React better than Claude?" + {" "} + (you probably can't, and that's fine).{" "} + + The question is "can you decide what to build, architect the system, and validate + that the AI-generated code actually solves the right problem?" + +

+ +

+ This is where the Renaissance Developer emerges—not as someone who codes less, but as + someone who builds more by orchestrating AI agents to handle implementation while + you focus on the thinking that AI can't (yet) do: product vision, architectural + decisions, user experience, and strategic tradeoffs. +

+ +

+ The rest of this chapter introduces the Renaissance Developer model and explains how to + thrive in this new paradigm. +

+
+ + {/* Navigation */} + +
+ ) +} diff --git a/website/app/book/layout.tsx b/website/app/book/layout.tsx new file mode 100644 index 0000000..a5545e1 --- /dev/null +++ b/website/app/book/layout.tsx @@ -0,0 +1,83 @@ +"use client" + +import React, { useState } from "react" +import Link from "next/link" +import { Terminal, Github, Home, Menu } from "lucide-react" +import { Button } from "@/components/ui/button" +import { BookNav } from "@/components/book-nav" +import { ThemeToggle } from "@/components/theme-toggle" +import { + Sheet, + SheetContent, + SheetHeader, + SheetTitle, + SheetTrigger, +} from "@/components/ui/sheet" + +export default function BookLayout({ children }: { children: React.ReactNode }) { + const [mobileOpen, setMobileOpen] = useState(false) + + return ( +
+ {/* Header */} +
+
+
+ {/* Mobile TOC toggle */} + + + + + + + + Agentic Coding Playbook + + +
+ setMobileOpen(false)} /> +
+
+
+ + + + agentic-coding + + / + Book +
+
+ + + +
+
+
+ +
+ {/* Desktop Sidebar */} + + + {/* Main Content */} +
{children}
+
+
+ ) +} diff --git a/website/app/globals.css b/website/app/globals.css new file mode 100644 index 0000000..dcc92f2 --- /dev/null +++ b/website/app/globals.css @@ -0,0 +1,94 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +body { + font-family: Arial, Helvetica, sans-serif; +} + +@layer utilities { + .text-balance { + text-wrap: balance; + } +} + +@layer base { + :root { + --background: 60 9% 98%; + --foreground: 220 20% 8%; + --card: 0 0% 100%; + --card-foreground: 220 20% 8%; + --popover: 0 0% 100%; + --popover-foreground: 220 20% 8%; + --primary: 142 71% 35%; + --primary-foreground: 0 0% 100%; + --secondary: 220 10% 93%; + --secondary-foreground: 220 20% 8%; + --muted: 220 10% 93%; + --muted-foreground: 220 10% 40%; + --accent: 142 71% 35%; + --accent-foreground: 0 0% 100%; + --destructive: 0 84.2% 60.2%; + --destructive-foreground: 0 0% 98%; + --border: 220 10% 88%; + --input: 220 10% 88%; + --ring: 142 71% 35%; + --chart-1: 142 71% 35%; + --chart-2: 173 58% 39%; + --chart-3: 197 37% 24%; + --chart-4: 43 74% 66%; + --chart-5: 27 87% 67%; + --radius: 0.5rem; + --sidebar-background: 60 9% 97%; + --sidebar-foreground: 220 20% 8%; + --sidebar-primary: 142 71% 35%; + --sidebar-primary-foreground: 0 0% 100%; + --sidebar-accent: 220 10% 93%; + --sidebar-accent-foreground: 220 20% 8%; + --sidebar-border: 220 10% 88%; + --sidebar-ring: 142 71% 35%; + } + .dark { + --background: 220 20% 4%; + --foreground: 60 9% 98%; + --card: 220 18% 8%; + --card-foreground: 60 9% 98%; + --popover: 220 18% 8%; + --popover-foreground: 60 9% 98%; + --primary: 142 71% 45%; + --primary-foreground: 220 20% 4%; + --secondary: 220 15% 15%; + --secondary-foreground: 60 9% 98%; + --muted: 220 15% 12%; + --muted-foreground: 220 10% 55%; + --accent: 142 71% 45%; + --accent-foreground: 220 20% 4%; + --destructive: 0 84.2% 60.2%; + --destructive-foreground: 0 0% 98%; + --border: 220 15% 18%; + --input: 220 15% 18%; + --ring: 142 71% 45%; + --chart-1: 142 71% 45%; + --chart-2: 173 58% 39%; + --chart-3: 197 37% 24%; + --chart-4: 43 74% 66%; + --chart-5: 27 87% 67%; + --sidebar-background: 220 18% 6%; + --sidebar-foreground: 60 9% 98%; + --sidebar-primary: 142 71% 45%; + --sidebar-primary-foreground: 220 20% 4%; + --sidebar-accent: 220 15% 12%; + --sidebar-accent-foreground: 60 9% 98%; + --sidebar-border: 220 15% 18%; + --sidebar-ring: 142 71% 45%; + } +} + +@layer base { + * { + @apply border-border; + } + body { + @apply bg-background text-foreground; + } +} diff --git a/website/app/layout.tsx b/website/app/layout.tsx new file mode 100644 index 0000000..48895d2 --- /dev/null +++ b/website/app/layout.tsx @@ -0,0 +1,35 @@ +import React from "react" +import type { Metadata } from 'next' +import { Geist, Geist_Mono } from 'next/font/google' +import { ThemeProvider } from '@/components/theme-provider' + +import './globals.css' + +const _geist = Geist({ subsets: ['latin'] }) +const _geistMono = Geist_Mono({ subsets: ['latin'] }) + +export const metadata: Metadata = { + title: 'The Agentic Coding Playbook', + description: 'From Idea to Production in 6 Weeks. The definitive playbook for building software products with AI agents.', +} + +export default function RootLayout({ + children, +}: Readonly<{ + children: React.ReactNode +}>) { + return ( + + + + {children} + + + + ) +} diff --git a/website/app/page.tsx b/website/app/page.tsx new file mode 100644 index 0000000..85a1dfb --- /dev/null +++ b/website/app/page.tsx @@ -0,0 +1,350 @@ +import { ArrowRight, Github, Terminal, BookOpen } from "lucide-react" +import { Button } from "@/components/ui/button" +import { Badge } from "@/components/ui/badge" +import { EmailSignup } from "@/components/email-signup" +import { ThemeToggle } from "@/components/theme-toggle" + +const KEY_IDEAS = [ + { + title: "The Renaissance Developer", + description: + "Breadth over depth. The new competitive advantage is being good enough at everything.", + }, + { + title: "The Acceleration Paradox", + description: + "When coding gets 5-10x faster, five new bottlenecks emerge that nobody talks about.", + }, + { + title: "The Digestibility Principle", + description: + "Human working memory and AI context windows have the same constraints. Design for both.", + }, + { + title: "Specification-Driven Development", + description: + "The quality of your descriptions determines the quality of your product.", + }, + { + title: 'The "Good Enough at Everything" Principle', + description: + "Why 70% competency across 5 domains beats 95% in one.", + }, +] + +const TESTIMONIALS = [ + { + quote: + "Finally, a book that treats AI coding as a real engineering discipline, not a party trick.", + author: "Coming soon", + }, + { + quote: + "The Renaissance Developer chapter changed how I think about my role as a senior engineer.", + author: "Coming soon", + }, + { + quote: + "Practical, opinionated, and exactly the playbook my team needed to adopt agentic workflows.", + author: "Coming soon", + }, +] + +export default function Home() { + return ( +
+ {/* Header */} +
+
+ + + agentic-coding + + +
+
+ +
+ {/* Hero */} +
+ {/* Book cover faded in background */} +
+
+
+ +
+

+ The +

+

+ Agentic Coding Playbook +

+
+
+

+ From Idea to Production +
+ in 6 Weeks +

+
+

Chris Testa

+
+
+ +
+ + Part 1: Foundations -- available now + + +

+ The bottleneck isn't{" "} + code anymore. +

+ +

+ It's knowing what to build. +

+ +

+ The definitive playbook for building software products with AI + agents — written by a CTO who's done it. For vibecoders + and CTOs alike. +

+ + + +

+ Free to read. Creative Commons (CC BY). +

+
+
+ + {/* Big Ideas */} +
+
+

+ What you'll learn +

+

+ Five ideas that will change how you build software +

+

+ These aren't AI hype takes. They're frameworks forged + from shipping real products with agentic workflows. +

+ +
+ {KEY_IDEAS.map((idea, i) => ( +
+ + {String(i + 1).padStart(2, "0")} + +
+

+ {idea.title} +

+

+ {idea.description} +

+
+
+ ))} +
+
+
+ + {/* Social Proof */} +
+
+

+ What readers are saying +

+
+ {TESTIMONIALS.map((item) => ( +
+
+ “{item.quote}” +
+
+ — {item.author} +
+
+ ))} +
+
+
+ + {/* Table of Contents */} +
+
+

+ Table of Contents +

+

+ Four parts. One playbook. Idea to production in six weeks. +

+ +
+ {/* Part 1 - Available */} + + + 01 + +
+
+

+ Foundations +

+ + Available + +
+

+ The Renaissance Developer mindset, what agentic coding + really means, and first principles of software architecture. +

+
+ +
+ + {/* Parts 2-4 - Coming Soon */} + {[ + { + num: "02", + title: "The Playbook", + desc: "The complete 6-week journey: idea, brainstorm, brief, requirements, design, implementation, testing, and deployment.", + }, + { + num: "03", + title: "Patterns & Tools", + desc: "Architecture patterns, interface design, testing strategies, and the agent-friendly toolchain.", + }, + { + num: "04", + title: "Complete Example", + desc: "A real, non-trivial product built using the entire playbook -- proving the 6-week promise.", + }, + ].map((part) => ( +
+ + {part.num} + +
+
+

{part.title}

+ + Soon + +
+

{part.desc}

+
+
+ ))} +
+
+
+ + {/* Email Capture */} +
+
+

+ Get notified when Part 2 drops +

+

+ No spam. Just a single email when new chapters go live. +

+
+ +
+
+
+ + {/* Final CTA */} +
+
+

+ AI can write code faster than you can type. The question + isn't whether to use it — it's whether you know + how to direct it. +

+ +
+
+
+ + {/* Footer */} +
+
+

+ By Chris Testa. Creative Commons Attribution (CC BY). +

+
+ + GitHub + + The Agentic Coding Playbook +
+
+
+
+ ) +} diff --git a/website/components.json b/website/components.json new file mode 100644 index 0000000..13f24bf --- /dev/null +++ b/website/components.json @@ -0,0 +1,21 @@ +{ + "$schema": "https://ui.shadcn.com/schema.json", + "style": "default", + "rsc": true, + "tsx": true, + "tailwind": { + "config": "tailwind.config.ts", + "css": "app/globals.css", + "baseColor": "neutral", + "cssVariables": true, + "prefix": "" + }, + "aliases": { + "components": "@/components", + "utils": "@/lib/utils", + "ui": "@/components/ui", + "lib": "@/lib", + "hooks": "@/hooks" + }, + "iconLibrary": "lucide" +} diff --git a/website/components/book-nav.tsx b/website/components/book-nav.tsx new file mode 100644 index 0000000..14bf11f --- /dev/null +++ b/website/components/book-nav.tsx @@ -0,0 +1,147 @@ +"use client" + +import Link from "next/link" +import { usePathname } from "next/navigation" +import { ChevronDown, ChevronRight } from "lucide-react" +import { useState, useEffect } from "react" +import { cn } from "@/lib/utils" +import { bookStructure } from "@/lib/book-data" + +export function BookNav({ onNavigate }: { onNavigate?: () => void }) { + const pathname = usePathname() + + const getInitialExpanded = () => { + const parts: string[] = [] + const chapters: string[] = [] + for (const part of bookStructure) { + for (const chapter of part.chapters) { + for (const section of chapter.sections) { + if (pathname === `/book/${part.slug}/${chapter.slug}/${section.slug}`) { + parts.push(part.slug) + chapters.push(chapter.slug) + } + } + } + } + return { + parts: parts.length > 0 ? parts : ["foundations"], + chapters: chapters.length > 0 ? chapters : [], + } + } + + const initial = getInitialExpanded() + const [expandedParts, setExpandedParts] = useState(initial.parts) + const [expandedChapters, setExpandedChapters] = useState(initial.chapters) + + useEffect(() => { + const updated = getInitialExpanded() + setExpandedParts((prev) => Array.from(new Set([...prev, ...updated.parts]))) + setExpandedChapters((prev) => Array.from(new Set([...prev, ...updated.chapters]))) + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [pathname]) + + const togglePart = (slug: string) => { + setExpandedParts((prev) => + prev.includes(slug) ? prev.filter((s) => s !== slug) : [...prev, slug], + ) + } + + const toggleChapter = (slug: string) => { + setExpandedChapters((prev) => + prev.includes(slug) ? prev.filter((s) => s !== slug) : [...prev, slug], + ) + } + + return ( + + ) +} diff --git a/website/components/email-signup.tsx b/website/components/email-signup.tsx new file mode 100644 index 0000000..e03fc1a --- /dev/null +++ b/website/components/email-signup.tsx @@ -0,0 +1,56 @@ +"use client" + +import React from "react" + +import { useState } from "react" +import { Button } from "@/components/ui/button" +import { Input } from "@/components/ui/input" +import { ArrowRight, Check, Loader2 } from "lucide-react" + +export function EmailSignup() { + const [email, setEmail] = useState("") + const [status, setStatus] = useState<"idle" | "loading" | "success">("idle") + + function handleSubmit(e: React.FormEvent) { + e.preventDefault() + if (!email) return + setStatus("loading") + // Simulate submission — replace with real endpoint + setTimeout(() => { + setStatus("success") + }, 1000) + } + + if (status === "success") { + return ( +
+ + You're on the list. We'll be in touch. +
+ ) + } + + return ( +
+ setEmail(e.target.value)} + required + className="bg-card" + aria-label="Email address" + /> + +
+ ) +} diff --git a/website/components/mermaid-diagram.tsx b/website/components/mermaid-diagram.tsx new file mode 100644 index 0000000..5656871 --- /dev/null +++ b/website/components/mermaid-diagram.tsx @@ -0,0 +1,70 @@ +"use client" + +import { useEffect, useRef } from "react" +import mermaid from "mermaid" + +mermaid.initialize({ + startOnLoad: false, + theme: "dark", + themeVariables: { + primaryColor: "#22c55e", + primaryTextColor: "#fafaf9", + primaryBorderColor: "#22c55e", + lineColor: "#3f3f46", + secondaryColor: "#18181b", + tertiaryColor: "#27272a", + background: "#0c0d10", + mainBkg: "#18181b", + nodeBorder: "#3f3f46", + clusterBkg: "#18181b", + clusterBorder: "#3f3f46", + titleColor: "#fafaf9", + edgeLabelBackground: "#18181b", + }, + flowchart: { + curve: "basis", + padding: 20, + }, +}) + +interface MermaidDiagramProps { + chart: string + caption?: string +} + +export function MermaidDiagram({ chart, caption }: MermaidDiagramProps) { + const containerRef = useRef(null) + + useEffect(() => { + const renderDiagram = async () => { + if (containerRef.current) { + containerRef.current.innerHTML = "" + try { + const { svg } = await mermaid.render( + `mermaid-${Math.random().toString(36).substr(2, 9)}`, + chart + ) + containerRef.current.innerHTML = svg + } catch (error) { + console.error("Mermaid rendering error:", error) + containerRef.current.innerHTML = `
${chart}
` + } + } + } + renderDiagram() + }, [chart]) + + return ( +
+
+ {caption && ( +
+ {caption} +
+ )} +
+ ) +} diff --git a/website/components/theme-provider.tsx b/website/components/theme-provider.tsx new file mode 100644 index 0000000..55c2f6e --- /dev/null +++ b/website/components/theme-provider.tsx @@ -0,0 +1,11 @@ +'use client' + +import * as React from 'react' +import { + ThemeProvider as NextThemesProvider, + type ThemeProviderProps, +} from 'next-themes' + +export function ThemeProvider({ children, ...props }: ThemeProviderProps) { + return {children} +} diff --git a/website/components/theme-toggle.tsx b/website/components/theme-toggle.tsx new file mode 100644 index 0000000..c967559 --- /dev/null +++ b/website/components/theme-toggle.tsx @@ -0,0 +1,34 @@ +"use client" + +import { useTheme } from "next-themes" +import { useEffect, useState } from "react" +import { Moon, Sun } from "lucide-react" +import { Button } from "@/components/ui/button" + +export function ThemeToggle() { + const { resolvedTheme, setTheme } = useTheme() + const [mounted, setMounted] = useState(false) + + useEffect(() => setMounted(true), []) + + if (!mounted) { + return ( + + ) + } + + const isDark = resolvedTheme === "dark" || !resolvedTheme + + return ( + + ) +} diff --git a/website/components/ui/accordion.tsx b/website/components/ui/accordion.tsx new file mode 100644 index 0000000..b69428b --- /dev/null +++ b/website/components/ui/accordion.tsx @@ -0,0 +1,58 @@ +'use client' + +import * as React from 'react' +import * as AccordionPrimitive from '@radix-ui/react-accordion' +import { ChevronDown } from 'lucide-react' + +import { cn } from '@/lib/utils' + +const Accordion = AccordionPrimitive.Root + +const AccordionItem = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AccordionItem.displayName = 'AccordionItem' + +const AccordionTrigger = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + + svg]:rotate-180', + className, + )} + {...props} + > + {children} + + + +)) +AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName + +const AccordionContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + +
{children}
+
+)) + +AccordionContent.displayName = AccordionPrimitive.Content.displayName + +export { Accordion, AccordionItem, AccordionTrigger, AccordionContent } diff --git a/website/components/ui/alert-dialog.tsx b/website/components/ui/alert-dialog.tsx new file mode 100644 index 0000000..e58f90a --- /dev/null +++ b/website/components/ui/alert-dialog.tsx @@ -0,0 +1,141 @@ +'use client' + +import * as React from 'react' +import * as AlertDialogPrimitive from '@radix-ui/react-alert-dialog' + +import { cn } from '@/lib/utils' +import { buttonVariants } from '@/components/ui/button' + +const AlertDialog = AlertDialogPrimitive.Root + +const AlertDialogTrigger = AlertDialogPrimitive.Trigger + +const AlertDialogPortal = AlertDialogPrimitive.Portal + +const AlertDialogOverlay = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName + +const AlertDialogContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + + + + +)) +AlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName + +const AlertDialogHeader = ({ + className, + ...props +}: React.HTMLAttributes) => ( +
+) +AlertDialogHeader.displayName = 'AlertDialogHeader' + +const AlertDialogFooter = ({ + className, + ...props +}: React.HTMLAttributes) => ( +
+) +AlertDialogFooter.displayName = 'AlertDialogFooter' + +const AlertDialogTitle = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName + +const AlertDialogDescription = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AlertDialogDescription.displayName = + AlertDialogPrimitive.Description.displayName + +const AlertDialogAction = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName + +const AlertDialogCancel = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName + +export { + AlertDialog, + AlertDialogPortal, + AlertDialogOverlay, + AlertDialogTrigger, + AlertDialogContent, + AlertDialogHeader, + AlertDialogFooter, + AlertDialogTitle, + AlertDialogDescription, + AlertDialogAction, + AlertDialogCancel, +} diff --git a/website/components/ui/alert.tsx b/website/components/ui/alert.tsx new file mode 100644 index 0000000..2b2ced8 --- /dev/null +++ b/website/components/ui/alert.tsx @@ -0,0 +1,59 @@ +import * as React from 'react' +import { cva, type VariantProps } from 'class-variance-authority' + +import { cn } from '@/lib/utils' + +const alertVariants = cva( + 'relative w-full rounded-lg border p-4 [&>svg~*]:pl-7 [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground', + { + variants: { + variant: { + default: 'bg-background text-foreground', + destructive: + 'border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive', + }, + }, + defaultVariants: { + variant: 'default', + }, + }, +) + +const Alert = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes & VariantProps +>(({ className, variant, ...props }, ref) => ( +
+)) +Alert.displayName = 'Alert' + +const AlertTitle = React.forwardRef< + HTMLParagraphElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +AlertTitle.displayName = 'AlertTitle' + +const AlertDescription = React.forwardRef< + HTMLParagraphElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +AlertDescription.displayName = 'AlertDescription' + +export { Alert, AlertTitle, AlertDescription } diff --git a/website/components/ui/aspect-ratio.tsx b/website/components/ui/aspect-ratio.tsx new file mode 100644 index 0000000..794c6f4 --- /dev/null +++ b/website/components/ui/aspect-ratio.tsx @@ -0,0 +1,7 @@ +'use client' + +import * as AspectRatioPrimitive from '@radix-ui/react-aspect-ratio' + +const AspectRatio = AspectRatioPrimitive.Root + +export { AspectRatio } diff --git a/website/components/ui/avatar.tsx b/website/components/ui/avatar.tsx new file mode 100644 index 0000000..77fde46 --- /dev/null +++ b/website/components/ui/avatar.tsx @@ -0,0 +1,50 @@ +'use client' + +import * as React from 'react' +import * as AvatarPrimitive from '@radix-ui/react-avatar' + +import { cn } from '@/lib/utils' + +const Avatar = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +Avatar.displayName = AvatarPrimitive.Root.displayName + +const AvatarImage = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AvatarImage.displayName = AvatarPrimitive.Image.displayName + +const AvatarFallback = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName + +export { Avatar, AvatarImage, AvatarFallback } diff --git a/website/components/ui/badge.tsx b/website/components/ui/badge.tsx new file mode 100644 index 0000000..eb4ccad --- /dev/null +++ b/website/components/ui/badge.tsx @@ -0,0 +1,37 @@ +import * as React from 'react' +import { cva, type VariantProps } from 'class-variance-authority' + +import { cn } from '@/lib/utils' + +const badgeVariants = cva( + 'inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2', + { + variants: { + variant: { + default: + 'border-transparent bg-primary text-primary-foreground hover:bg-primary/80', + secondary: + 'border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80', + destructive: + 'border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80', + outline: 'text-foreground', + }, + }, + defaultVariants: { + variant: 'default', + }, + }, +) + +export interface BadgeProps + extends + React.HTMLAttributes, + VariantProps {} + +function Badge({ className, variant, ...props }: BadgeProps) { + return ( +
+ ) +} + +export { Badge, badgeVariants } diff --git a/website/components/ui/breadcrumb.tsx b/website/components/ui/breadcrumb.tsx new file mode 100644 index 0000000..d731202 --- /dev/null +++ b/website/components/ui/breadcrumb.tsx @@ -0,0 +1,115 @@ +import * as React from 'react' +import { Slot } from '@radix-ui/react-slot' +import { ChevronRight, MoreHorizontal } from 'lucide-react' + +import { cn } from '@/lib/utils' + +const Breadcrumb = React.forwardRef< + HTMLElement, + React.ComponentPropsWithoutRef<'nav'> & { + separator?: React.ReactNode + } +>(({ ...props }, ref) =>