From 520c0761fd290fe7308a08b641e953eaed66c5f6 Mon Sep 17 00:00:00 2001 From: Anton Castro Date: Tue, 21 Jul 2026 00:01:16 -0700 Subject: [PATCH] feat: className pass-through, theming docs, llms.txt + registry index, component template, code of conduct, landing teasers + claim lines --- CODE_OF_CONDUCT.md | 11 +++ CONTRIBUTING.md | 4 + app/how-it-works/page.tsx | 37 ++++++++ app/page.tsx | 90 ++++++++++++------- registry/_template/component-name.tsx | 63 +++++++++++++ registry/agent-inbox/agent-inbox.tsx | 6 +- registry/agent-roster/agent-roster.tsx | 6 +- registry/agent-task-list/agent-task-list.tsx | 6 +- registry/approval-gate/approval-gate.tsx | 5 +- .../confidence-meter/confidence-meter.tsx | 5 +- registry/context-budget/context-budget.tsx | 6 +- registry/interrupt-bar/interrupt-bar.tsx | 5 +- registry/tool-call-card/tool-call-card.tsx | 6 +- scripts/build-registry.mjs | 49 +++++++++- 14 files changed, 252 insertions(+), 47 deletions(-) create mode 100644 CODE_OF_CONDUCT.md create mode 100644 registry/_template/component-name.tsx diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..74f746e --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,11 @@ +# Code of Conduct + +The pit crew works fast because it trusts each other. Same rules here. + +**Expected:** Be direct about the work and generous with the person. Critique components, patterns, and prose as hard as you like; never the human who made them. Assume competence and good faith, especially across the designer/engineer divide this project exists to bridge. Scars and real product stories are the most valued contribution; share yours and respect others'. + +**Not tolerated:** Harassment, discrimination, or personal attacks of any kind; gatekeeping ("that's not real engineering / real design"); sustained disruption of discussions; publishing others' private information. + +**Enforcement:** Report conduct issues to ac.design.px@gmail.com. The maintainer will review and respond within a week. Consequences scale from a warning to a permanent ban from the project's spaces, at the maintainer's judgment. + +Adapted in spirit from the [Contributor Covenant](https://www.contributor-covenant.org/) v2.1. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e98c3e8..fa546e9 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -9,6 +9,10 @@ Thanks for pulling into the pit. This doc is the whole contract: what a contribu - **Fixes** — bugs, typos, broken demos. No proposal needed; just PR. - **Principle chapters** are held to the highest bar: a real claim plus a working component as evidence. Open a discussion before writing one. +## Start from the template + +Copy `registry/_template/component-name.tsx` — it encodes every convention below (tokens, states-in-words, tap targets, className pass-through) so your first five minutes are building, not reverse-engineering. + ## Definition of done (components) Every component PR ships all five, or it isn't done: diff --git a/app/how-it-works/page.tsx b/app/how-it-works/page.tsx index d87adba..2a5bd7a 100644 --- a/app/how-it-works/page.tsx +++ b/app/how-it-works/page.tsx @@ -125,6 +125,43 @@ export default function HowItWorksPage() { product; what happens when a human leans in is what we make excellent.

+

Make it yours

+

+ Every component speaks in semantic tokens, never raw values. That means the + whole system rethemes from one place: define the tokens in your Tailwind theme + with your own values, and every card, gate, and meter follows. No wrapping, no + forking, no component edits. +

+
+ = 4.5:1) */ + /* signals */ + --color-pit: #60a5fa; /* accent: swap for your brand */ + --color-signal: #34d399; /* success */ + --color-caution: #fbbf24; /* attention */ + --color-flag: #f87171; /* danger */ +}`} + /> +
+

+ Past tokens, the path escalates one step at a time: pass{" "} + className{" "} + for layout and overrides, or take the single source file and own it entirely. + Keep the contrast honest when you retheme; the tokens above all clear WCAG AA on + their surfaces. +

+

The name

A pit stop is the moment a fast autonomous thing submits to human hands: twelve diff --git a/app/page.tsx b/app/page.tsx index 846d62b..74fa74f 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -2,24 +2,40 @@ import Link from "next/link"; import { PitFlag } from "@/components/docs/pit-flag"; import { TrackLoop } from "@/components/docs/track-loop"; import { Reveal } from "@/components/docs/reveal"; +import { minis } from "@/app/components/gallery-minis"; const principles = [ - { title: "Legible thinking", href: "/principles/legible-thinking", status: "Live" }, - { title: "Interruptibility", href: "/principles/interruptibility", status: "Live" }, - { title: "Delegation contracts", href: "/principles/delegation-contracts", status: "Live" }, - { title: "Calibrated trust", href: "/principles/calibrated-trust", status: "Live" }, - { title: "Graceful failure", href: "/principles/graceful-failure", status: "Live" }, + { + title: "Legible thinking", + claim: "Agents don't lose trust by being wrong. They lose it by being wrong invisibly.", + href: "/principles/legible-thinking", + }, + { + title: "Interruptibility", + claim: "Users delegate more to agents they can stop.", + href: "/principles/interruptibility", + }, + { + title: "Delegation contracts", + claim: "Delegation fails at the handoff, not at execution.", + href: "/principles/delegation-contracts", + }, + { + title: "Calibrated trust", + claim: "The goal is accurate trust, not more trust.", + href: "/principles/calibrated-trust", + }, + { + title: "Graceful failure", + claim: "Software fails like glass. Agents fail like weather.", + href: "/principles/graceful-failure", + }, ]; -const components = [ - { title: "Tool Call Card", href: "/components/tool-call-card", status: "Live" }, - { title: "Approval Gate", href: "/components/approval-gate", status: "Live" }, - { title: "Agent Task List", href: "/components/agent-task-list", status: "Live" }, - { title: "Interrupt Bar", href: "/components/interrupt-bar", status: "Live" }, - { title: "Confidence Meter", href: "/components/confidence-meter", status: "Live" }, - { title: "Agent Roster", href: "/components/agent-roster", status: "Live" }, - { title: "Agent Inbox", href: "/components/agent-inbox", status: "Live" }, - { title: "Context Budget", href: "/components/context-budget", status: "Live" }, +const teasers = [ + { slug: "tool-call-card", title: "Tool Call Card", category: "Legibility" }, + { slug: "approval-gate", title: "Approval Gate", category: "Control" }, + { slug: "confidence-meter", title: "Confidence Meter", category: "Trust" }, ]; /* two-tone icons in the site accent */ @@ -81,17 +97,13 @@ const heroCards = [ }, ]; -function Row({ title, href, status }: { title: string; href: string | null; status: string }) { +function PrincipleRow({ title, claim, href }: { title: string; claim: string; href: string }) { return ( -

  • - {href ? ( - - {title} - - ) : ( - {title} - )} - {status} +
  • + + {title} + {claim} +
  • ); } @@ -176,27 +188,41 @@ export default function Home() { -
    -
    +
    +

    Components

    - Browse all → + Browse all 8 →
    -
      - {components.map((c) => ( - +
      + {teasers.map((t) => ( + +
      {minis[t.slug]}
      +
      + + {t.title} + + + {t.category} + +
      + ))} -
    +
    diff --git a/registry/_template/component-name.tsx b/registry/_template/component-name.tsx new file mode 100644 index 0000000..4a1f4a4 --- /dev/null +++ b/registry/_template/component-name.tsx @@ -0,0 +1,63 @@ +"use client"; + +/** + * ComponentName — Agent Pit Stop + * One sentence: the sync moment this serves and the stance it takes. + * + * Principles applied: + * - : how this component embodies it, concretely + * - : state is never color alone; failures keep their inputs + * + * Contributor notes (delete before submitting): + * - Single file. Dependencies: React, Tailwind tokens, Motion. Nothing else. + * - Tokens only: bg-asphalt / bg-carbon surfaces, text-chalk / text-smoke / + * text-ash text, border-line, accents pit / signal / caution / flag. + * - Every state gets an icon or label beside its color. + * - Action buttons: min-h-9 + pointer-coarse:min-h-11. + * - Accept className, merged onto the root. + * - Pair with app/components//{demo,page}.tsx, register in lib/nav.ts + * and scripts/build-registry.mjs. See CONTRIBUTING.md for the full + * definition of done. + */ + +import { useState } from "react"; +import { motion } from "motion/react"; + +export type ComponentNameState = "idle" | "active"; + +export interface ComponentNameProps { + /** Document every prop with the concrete example it expects */ + label: string; + state?: ComponentNameState; + /** Extra classes merged onto the root element */ + className?: string; +} + +export function ComponentName({ label, state: controlled, className }: ComponentNameProps) { + const [internal, setInternal] = useState("idle"); + const state = controlled ?? internal; + + return ( +
    +
    + {label} + +
    + {state === "active" && ( + + Active. States are words, never color alone. + + )} +
    + ); +} diff --git a/registry/agent-inbox/agent-inbox.tsx b/registry/agent-inbox/agent-inbox.tsx index 5949636..9711925 100644 --- a/registry/agent-inbox/agent-inbox.tsx +++ b/registry/agent-inbox/agent-inbox.tsx @@ -36,6 +36,8 @@ export interface AgentInboxProps { /** Called when the human opens a run for review */ onOpen?: (id: string) => void; label?: string; + /** Extra classes merged onto the root element */ + className?: string; } const statusMeta: Record = { @@ -45,11 +47,11 @@ const statusMeta: Record i.status === "needs_review").length; return ( -
    +
    {label} diff --git a/registry/agent-roster/agent-roster.tsx b/registry/agent-roster/agent-roster.tsx index 7db48c2..7c085d5 100644 --- a/registry/agent-roster/agent-roster.tsx +++ b/registry/agent-roster/agent-roster.tsx @@ -33,6 +33,8 @@ export interface RosterAgent { export interface AgentRosterProps { agents: RosterAgent[]; label?: string; + /** Extra classes merged onto the root element */ + className?: string; } const stateMeta: Record = { @@ -59,11 +61,11 @@ function StateDot({ state }: { state: AgentState }) { ); } -export function AgentRoster({ agents, label = "Crew" }: AgentRosterProps) { +export function AgentRoster({ agents, label = "Crew", className }: AgentRosterProps) { const working = agents.filter((a) => a.state === "working").length; return ( -
    +
    {label} diff --git a/registry/agent-task-list/agent-task-list.tsx b/registry/agent-task-list/agent-task-list.tsx index 82dfb00..b41d073 100644 --- a/registry/agent-task-list/agent-task-list.tsx +++ b/registry/agent-task-list/agent-task-list.tsx @@ -27,6 +27,8 @@ export interface AgentTaskListProps { /** Short label for the overall plan, e.g. "Booking your trip" */ label?: string; tasks: AgentTask[]; + /** Extra classes merged onto the root element */ + className?: string; } function StatusIcon({ status }: { status: AgentTaskStatus }) { @@ -82,11 +84,11 @@ function StatusIcon({ status }: { status: AgentTaskStatus }) { ); } -export function AgentTaskList({ label, tasks }: AgentTaskListProps) { +export function AgentTaskList({ label, tasks, className }: AgentTaskListProps) { const done = tasks.filter((t) => t.status === "done").length; return ( -
    +
    {label && (
    {label} diff --git a/registry/approval-gate/approval-gate.tsx b/registry/approval-gate/approval-gate.tsx index b812e9e..335abb2 100644 --- a/registry/approval-gate/approval-gate.tsx +++ b/registry/approval-gate/approval-gate.tsx @@ -39,6 +39,8 @@ export interface ApprovalGateProps { onDeny?: () => void; approveLabel?: string; denyLabel?: string; + /** Extra classes merged onto the root element */ + className?: string; } export function ApprovalGate({ @@ -50,6 +52,7 @@ export function ApprovalGate({ onDeny, approveLabel = "Approve", denyLabel = "Deny", + className, }: ApprovalGateProps) { const [internal, setInternal] = useState("awaiting"); const state = controlled ?? internal; @@ -60,7 +63,7 @@ export function ApprovalGate({ }; return ( -
    +
    {state === "awaiting" ? ( void; verifyLabel?: string; + /** Extra classes merged onto the root element */ + className?: string; } export function bandOf(confidence: number): ConfidenceBand { @@ -50,13 +52,14 @@ export function ConfidenceMeter({ threshold = 0.75, onVerify, verifyLabel = "Check this", + className, }: ConfidenceMeterProps) { const band = bandOf(confidence); const meta = bandMeta[band]; const needsVerification = confidence < threshold; return ( -
    +
    {claim}
    diff --git a/registry/context-budget/context-budget.tsx b/registry/context-budget/context-budget.tsx index cbb0463..3cdfaef 100644 --- a/registry/context-budget/context-budget.tsx +++ b/registry/context-budget/context-budget.tsx @@ -30,6 +30,8 @@ export interface ContextBudgetProps { label?: string; /** Called when the human raises a cap after one is hit */ onRaise?: (label: string) => void; + /** Extra classes merged onto the root element */ + className?: string; } function bandFor(pct: number): { word: string; bar: string; text: string } { @@ -38,9 +40,9 @@ function bandFor(pct: number): { word: string; bar: string; text: string } { return { word: "Fine", bar: "bg-pit", text: "text-smoke" }; } -export function ContextBudget({ lines, label = "Run budget", onRaise }: ContextBudgetProps) { +export function ContextBudget({ lines, label = "Run budget", onRaise, className }: ContextBudgetProps) { return ( -
    +
    {label}
    diff --git a/registry/interrupt-bar/interrupt-bar.tsx b/registry/interrupt-bar/interrupt-bar.tsx index 75115fe..7b1dd71 100644 --- a/registry/interrupt-bar/interrupt-bar.tsx +++ b/registry/interrupt-bar/interrupt-bar.tsx @@ -33,6 +33,8 @@ export interface InterruptBarProps { onSteer?: (message: string) => void; /** The agent's acknowledgment of the last steer, shown inline */ acknowledgment?: string; + /** Extra classes merged onto the root element */ + className?: string; } export function InterruptBar({ @@ -43,6 +45,7 @@ export function InterruptBar({ onStop, onSteer, acknowledgment, + className, }: InterruptBarProps) { const [internal, setInternal] = useState("running"); const [draft, setDraft] = useState(""); @@ -61,7 +64,7 @@ export function InterruptBar({ }; return ( -
    +
    {state === "running" ? ( diff --git a/registry/tool-call-card/tool-call-card.tsx b/registry/tool-call-card/tool-call-card.tsx index 369c74d..fff0c1b 100644 --- a/registry/tool-call-card/tool-call-card.tsx +++ b/registry/tool-call-card/tool-call-card.tsx @@ -27,6 +27,8 @@ export interface ToolCallCardProps { detail?: string; /** Seconds elapsed while running (optional) */ elapsed?: number; + /** Extra classes merged onto the root element */ + className?: string; } const stateMeta: Record = { @@ -36,13 +38,13 @@ const stateMeta: Record +