Skip to content

Commit fe83e95

Browse files
committed
feat(site): redesign landing page with bento grid and feature list components
Replace FeatureGrid with two new components: BentoGrid showcases four core pillars with animated visualizations (typing, icon orbit, pulse nodes, beam connector), while FeatureList details six additional capabilities with rich descriptions. Add three new magic animation components (AnimatedBeam, IconOrbit, PulseNodes) to support interactive feature cards. Update Landing route to render both grids sequentially. Add .tmp to gitignore and extend globals.css with orbit and pulse-node keyframe animations to support reduced-motion preferences. This redesign provides richer visual storytelling for product capabilities while maintaining accessibility through motion-safe/motion-reduce CSS patterns.
1 parent 6eece42 commit fe83e95

9 files changed

Lines changed: 555 additions & 119 deletions

File tree

site/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ dist
33
.env
44
.env.local
55
.vercel
6+
.tmp
67
coverage
78
playwright-report
89
test-results
Lines changed: 212 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,212 @@
1+
import type { LucideIcon } from "lucide-react";
2+
import {
3+
Boxes,
4+
CircleDot,
5+
Cloud,
6+
FileSearch,
7+
GaugeCircle,
8+
GitCommitHorizontal,
9+
GitPullRequest,
10+
KeyRound,
11+
MonitorDot,
12+
ScanSearch,
13+
Settings2,
14+
Sparkles,
15+
Type as TypeIcon,
16+
Zap,
17+
} from "lucide-react";
18+
import AnimatedBeam from "@/magic/AnimatedBeam";
19+
import IconOrbit from "@/magic/IconOrbit";
20+
import PulseNodes from "@/magic/PulseNodes";
21+
import TypingAnimation from "@/magic/TypingAnimation";
22+
import { cn } from "@/lib/utils";
23+
24+
type AnimationKind = "typing" | "icon-orbit" | "pulse-nodes" | "beam-connector";
25+
26+
interface Feature {
27+
slug: string;
28+
pillar_title: string;
29+
hook: string;
30+
body: string;
31+
bullets: string[];
32+
uniqueness: string;
33+
animation_kind: AnimationKind;
34+
primary_icon: LucideIcon;
35+
}
36+
37+
const features: Feature[] = [
38+
{
39+
slug: "incremental",
40+
pillar_title: "Incremental Test & Lint",
41+
hook: "Run only what your branch touched",
42+
body: "Gavel resolves your working tree against origin/main, walks the package graph, and runs only affected packages. Content fingerprints skip anything that already passed.",
43+
bullets: [
44+
"--changed against any ref",
45+
"--cache on green fingerprint",
46+
"Same engine for lint",
47+
"Drops to full suite in CI",
48+
],
49+
uniqueness:
50+
"`gavel test --changed --cache` gives sub-second feedback on monorepos without sacrificing the full CI run.",
51+
animation_kind: "typing",
52+
primary_icon: Zap,
53+
},
54+
{
55+
slug: "one-config",
56+
pillar_title: "Unified Test · Lint · Ignore",
57+
hook: "One YAML. Every language. Every linter.",
58+
body: "`.gavel.yaml` is the single policy layer across Go, TypeScript, Python, and Markdown. Native linter configs are still honored — gavel wraps them, it doesn't reinvent them.",
59+
bullets: [
60+
"Ignore by source + rule + file",
61+
"Enable or disable linters",
62+
"Pre/post hooks in one place",
63+
"Inherits ~/.gavel.yaml",
64+
],
65+
uniqueness:
66+
"One ignore list matches across golangci, eslint, ruff, and markdownlint — no per-language config sprawl.",
67+
animation_kind: "icon-orbit",
68+
primary_icon: Settings2,
69+
},
70+
{
71+
slug: "pr-watcher",
72+
pillar_title: "PR Watcher",
73+
hook: "Your PR queue, in one tab",
74+
body: "A live dashboard that polls PRs across repos, surfaces GitHub Actions check status, and tails failed-job logs inline — no more F5-ing.",
75+
bullets: [
76+
"Filter by author, org, state",
77+
"Failed-log tails inline",
78+
"Watch whole orgs in parallel",
79+
"Terminal or browser surface",
80+
],
81+
uniqueness:
82+
"`gavel pr list --ui` is the only PR dashboard that folds check status, failed logs, and TODO sync into one view.",
83+
animation_kind: "pulse-nodes",
84+
primary_icon: GitPullRequest,
85+
},
86+
{
87+
slug: "menu-bar",
88+
pillar_title: "macOS Menu Bar",
89+
hook: "Ambient CI, always on",
90+
body: "A native macOS menu-bar indicator color-coded by the worst current state across your PRs — green passing, amber pending, red broken. Click for the full dashboard.",
91+
bullets: [
92+
"Persists across sessions",
93+
"Survives reboots as a service",
94+
"Single-click to full UI",
95+
"No dock icon required",
96+
],
97+
uniqueness:
98+
"`gavel pr list --menu-bar` installs as a launchd agent — a GitHub checks light permanently wired into your desktop.",
99+
animation_kind: "beam-connector",
100+
primary_icon: MonitorDot,
101+
},
102+
];
103+
104+
const LINTER_ICONS: LucideIcon[] = [
105+
Boxes,
106+
CircleDot,
107+
ScanSearch,
108+
Sparkles,
109+
TypeIcon,
110+
FileSearch,
111+
Cloud,
112+
GitCommitHorizontal,
113+
KeyRound,
114+
];
115+
116+
function AnimationVisual({ feature }: { feature: Feature }) {
117+
switch (feature.animation_kind) {
118+
case "typing":
119+
return (
120+
<div className="flex h-full w-full items-center justify-center p-3">
121+
<div className="w-full max-w-sm overflow-hidden rounded-md border border-border bg-background shadow-sm">
122+
<div className="flex items-center gap-1.5 border-b border-border bg-muted/60 px-3 py-1.5">
123+
<span className="h-2 w-2 rounded-full bg-red-400/70" />
124+
<span className="h-2 w-2 rounded-full bg-amber-400/70" />
125+
<span className="h-2 w-2 rounded-full bg-emerald-400/70" />
126+
<span className="ml-2 font-mono text-[0.65rem] text-muted-foreground">~/repo</span>
127+
</div>
128+
<div className="px-3 py-3 font-mono text-xs">
129+
<TypingAnimation text="$ gavel test --changed --cache" />
130+
</div>
131+
</div>
132+
</div>
133+
);
134+
case "icon-orbit":
135+
return <IconOrbit icons={LINTER_ICONS} />;
136+
case "pulse-nodes":
137+
return <PulseNodes count={5} labels={["#12", "#13", "#14", "#15", "✓"]} />;
138+
case "beam-connector":
139+
return (
140+
<AnimatedBeam
141+
fromLabel="github"
142+
toLabel="menu bar"
143+
fromIcon={<GitPullRequest size={16} />}
144+
toIcon={<GaugeCircle size={16} />}
145+
/>
146+
);
147+
}
148+
}
149+
150+
function BentoCard({ feature, className }: { feature: Feature; className?: string }) {
151+
const Icon = feature.primary_icon;
152+
return (
153+
<article
154+
id={feature.slug}
155+
className={cn(
156+
"group flex flex-col overflow-hidden rounded-xl border border-border bg-card transition-colors hover:border-brand-sky/40",
157+
className,
158+
)}
159+
>
160+
<div className="relative h-44 border-b border-border bg-gradient-to-br from-muted/40 via-background to-background">
161+
<AnimationVisual feature={feature} />
162+
</div>
163+
<div className="flex flex-1 flex-col p-6">
164+
<div className="flex items-center gap-3">
165+
<span className="inline-flex h-8 w-8 items-center justify-center rounded-md bg-brand-sky/10 text-brand-sky">
166+
<Icon size={18} />
167+
</span>
168+
<h3 className="text-lg font-semibold">{feature.pillar_title}</h3>
169+
</div>
170+
<p className="mt-2 text-sm font-medium text-foreground/90">{feature.hook}</p>
171+
<p className="mt-3 text-sm text-muted-foreground">{feature.body}</p>
172+
173+
<ul className="mt-4 grid grid-cols-2 gap-x-4 gap-y-2 text-sm text-foreground/80">
174+
{feature.bullets.map((b) => (
175+
<li key={b} className="flex gap-2 before:mt-2 before:h-1 before:w-1 before:shrink-0 before:rounded-full before:bg-brand-sky">
176+
<span>{b}</span>
177+
</li>
178+
))}
179+
</ul>
180+
181+
<p className="mt-auto pt-5 text-xs italic text-muted-foreground">
182+
<span className="font-medium not-italic text-brand-sky">Unique:</span>{" "}
183+
{feature.uniqueness}
184+
</p>
185+
</div>
186+
</article>
187+
);
188+
}
189+
190+
export default function BentoGrid() {
191+
return (
192+
<section className="mx-auto max-w-6xl px-6 py-20">
193+
<div className="flex flex-col gap-4 sm:flex-row sm:items-end sm:justify-between">
194+
<div>
195+
<p className="text-sm font-medium uppercase tracking-wider text-brand-sky">What ships</p>
196+
<h2 className="mt-2 text-3xl font-bold tracking-tight sm:text-4xl">
197+
Four pillars, one binary
198+
</h2>
199+
</div>
200+
<p className="max-w-sm text-sm text-muted-foreground">
201+
Every card below is a capability MANUAL.md documents today — not a roadmap.
202+
</p>
203+
</div>
204+
205+
<div className="mt-10 grid gap-4 lg:grid-cols-2">
206+
{features.map((f) => (
207+
<BentoCard key={f.slug} feature={f} />
208+
))}
209+
</div>
210+
</section>
211+
);
212+
}

site/src/components/site/FeatureGrid.tsx

Lines changed: 0 additions & 117 deletions
This file was deleted.

0 commit comments

Comments
 (0)