Skip to content

Commit c0e3d6e

Browse files
fyalavuzruvnet
andcommitted
fix: migrate hardcoded gray classes to shadcn theme tokens and enhance Storybook
Replace hardcoded Tailwind gray classes (text-gray-*, bg-white, border-gray-*) with shadcn design tokens across 14 example pages, enabling proper dark mode support and visual consistency. Extract shared IndicatorPill and SearchFilter components, add Inter font, polish index page, and expand Storybook from 6 to 23 stories covering Header, Footer, Sidebar, ScrollNav, and AppShell. Co-Authored-By: claude-flow <ruv@ruv.net>
1 parent c30cba4 commit c0e3d6e

25 files changed

Lines changed: 744 additions & 431 deletions

File tree

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
export function IndicatorPill({ children }: { children: React.ReactNode }) {
2+
return (
3+
<div className="fixed bottom-4 left-1/2 -translate-x-1/2 z-[60] flex items-center gap-2 rounded-full bg-foreground/90 px-4 py-2 text-[11px] font-mono text-background/90 shadow-2xl backdrop-blur-md border border-background/10">
4+
<span className="size-1.5 rounded-full bg-emerald-400 animate-pulse" />
5+
{children}
6+
</div>
7+
);
8+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { Search, SlidersHorizontal } from "lucide-react";
2+
3+
export function SearchFilter() {
4+
return (
5+
<div className="flex items-center gap-2">
6+
<div className="relative flex-1">
7+
<Search className="absolute left-3.5 top-1/2 -translate-y-1/2 size-4 text-muted-foreground/70" />
8+
<input
9+
type="text"
10+
placeholder="Search photos, people, or locations..."
11+
className="w-full rounded-xl border border-input bg-muted/50 py-2.5 pl-10 pr-4 text-sm placeholder:text-muted-foreground focus:border-ring focus:bg-background focus:outline-none focus:ring-2 focus:ring-ring/30 transition-all"
12+
/>
13+
</div>
14+
<button
15+
type="button"
16+
className="flex items-center gap-1.5 rounded-xl border border-input bg-card px-3.5 py-2.5 text-sm font-medium text-muted-foreground hover:bg-accent/50 hover:border-ring transition-all cursor-pointer"
17+
>
18+
<SlidersHorizontal className="size-4" />
19+
<span className="hidden sm:inline">Filters</span>
20+
</button>
21+
</div>
22+
);
23+
}

apps/examples/app/content-only/page.tsx

Lines changed: 19 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { useState } from "react";
44
import { AppShell, Content, MotionProvider } from "@appshell/react";
55
import { framerMotionAdapter } from "@appshell/react/motion-framer";
66
import { Lock, Mail, ArrowRight, Github, Chrome, Sparkles, Eye, EyeOff } from "lucide-react";
7+
import { IndicatorPill } from "../_shared/indicator-pill";
78

89
export default function ContentOnlyPage() {
910
const [email, setEmail] = useState("");
@@ -30,18 +31,18 @@ export default function ContentOnlyPage() {
3031
<div className="flex items-center justify-center size-11 rounded-2xl bg-gradient-to-br from-violet-500 to-purple-600 text-white shadow-lg shadow-violet-500/20">
3132
<Sparkles className="size-5" />
3233
</div>
33-
<span className="text-xl font-bold tracking-tight text-gray-900">
34+
<span className="text-xl font-bold tracking-tight text-foreground">
3435
Acme
3536
</span>
3637
</div>
3738

3839
{/* Card - polished */}
39-
<div className="rounded-2xl border border-gray-200/80 bg-white/80 backdrop-blur-sm p-6 sm:p-8 shadow-xl shadow-gray-200/40">
40+
<div className="rounded-2xl border border-input bg-card/80 backdrop-blur-sm p-6 sm:p-8 shadow-xl shadow-border/40">
4041
<div className="mb-6">
41-
<h1 className="text-2xl font-bold text-gray-900 tracking-tight">
42+
<h1 className="text-2xl font-bold text-foreground tracking-tight">
4243
Welcome back
4344
</h1>
44-
<p className="mt-1.5 text-sm text-gray-500">
45+
<p className="mt-1.5 text-sm text-muted-foreground">
4546
Sign in to your account to continue
4647
</p>
4748
</div>
@@ -50,14 +51,14 @@ export default function ContentOnlyPage() {
5051
<div className="grid grid-cols-2 gap-3 mb-6">
5152
<button
5253
type="button"
53-
className="flex items-center justify-center gap-2 rounded-xl border border-gray-200 bg-white px-4 py-2.5 text-sm font-medium text-gray-700 hover:bg-gray-50 hover:border-gray-300 transition-all"
54+
className="flex items-center justify-center gap-2 rounded-xl border border-border bg-card px-4 py-2.5 text-sm font-medium text-foreground hover:bg-accent/50 hover:border-ring transition-all"
5455
>
5556
<Github className="size-4" />
5657
GitHub
5758
</button>
5859
<button
5960
type="button"
60-
className="flex items-center justify-center gap-2 rounded-xl border border-gray-200 bg-white px-4 py-2.5 text-sm font-medium text-gray-700 hover:bg-gray-50 hover:border-gray-300 transition-all"
61+
className="flex items-center justify-center gap-2 rounded-xl border border-border bg-card px-4 py-2.5 text-sm font-medium text-foreground hover:bg-accent/50 hover:border-ring transition-all"
6162
>
6263
<Chrome className="size-4" />
6364
Google
@@ -67,10 +68,10 @@ export default function ContentOnlyPage() {
6768
{/* Divider */}
6869
<div className="relative mb-6">
6970
<div className="absolute inset-0 flex items-center">
70-
<div className="w-full border-t border-gray-200" />
71+
<div className="w-full border-t border-border" />
7172
</div>
7273
<div className="relative flex justify-center text-xs">
73-
<span className="bg-white px-3 text-gray-400">
74+
<span className="bg-card px-3 text-muted-foreground/70">
7475
or continue with email
7576
</span>
7677
</div>
@@ -84,19 +85,19 @@ export default function ContentOnlyPage() {
8485
<div>
8586
<label
8687
htmlFor="email"
87-
className="block text-sm font-medium text-gray-700 mb-1.5"
88+
className="block text-sm font-medium text-foreground mb-1.5"
8889
>
8990
Email
9091
</label>
9192
<div className="relative">
92-
<Mail className="absolute left-3.5 top-1/2 -translate-y-1/2 size-4 text-gray-400" />
93+
<Mail className="absolute left-3.5 top-1/2 -translate-y-1/2 size-4 text-muted-foreground/70" />
9394
<input
9495
id="email"
9596
type="email"
9697
placeholder="you@example.com"
9798
value={email}
9899
onChange={(e) => setEmail(e.target.value)}
99-
className="w-full rounded-xl border border-gray-200 bg-gray-50/80 py-2.5 pl-10 pr-4 text-sm placeholder:text-gray-400 focus:border-violet-400 focus:bg-white focus:outline-none focus:ring-4 focus:ring-violet-100 transition-all"
100+
className="w-full rounded-xl border border-border bg-muted/50 py-2.5 pl-10 pr-4 text-sm placeholder:text-muted-foreground focus:border-violet-400 focus:bg-background focus:outline-none focus:ring-4 focus:ring-violet-100 transition-all"
100101
/>
101102
</div>
102103
</div>
@@ -105,7 +106,7 @@ export default function ContentOnlyPage() {
105106
<div className="flex items-center justify-between mb-1.5">
106107
<label
107108
htmlFor="password"
108-
className="block text-sm font-medium text-gray-700"
109+
className="block text-sm font-medium text-foreground"
109110
>
110111
Password
111112
</label>
@@ -117,19 +118,19 @@ export default function ContentOnlyPage() {
117118
</button>
118119
</div>
119120
<div className="relative">
120-
<Lock className="absolute left-3.5 top-1/2 -translate-y-1/2 size-4 text-gray-400" />
121+
<Lock className="absolute left-3.5 top-1/2 -translate-y-1/2 size-4 text-muted-foreground/70" />
121122
<input
122123
id="password"
123124
type={showPassword ? "text" : "password"}
124125
placeholder="Enter your password"
125126
value={password}
126127
onChange={(e) => setPassword(e.target.value)}
127-
className="w-full rounded-xl border border-gray-200 bg-gray-50/80 py-2.5 pl-10 pr-10 text-sm placeholder:text-gray-400 focus:border-violet-400 focus:bg-white focus:outline-none focus:ring-4 focus:ring-violet-100 transition-all"
128+
className="w-full rounded-xl border border-border bg-muted/50 py-2.5 pl-10 pr-10 text-sm placeholder:text-muted-foreground focus:border-violet-400 focus:bg-background focus:outline-none focus:ring-4 focus:ring-violet-100 transition-all"
128129
/>
129130
<button
130131
type="button"
131132
onClick={() => setShowPassword(!showPassword)}
132-
className="absolute right-3 top-1/2 -translate-y-1/2 text-gray-400 hover:text-gray-600 transition-colors"
133+
className="absolute right-3 top-1/2 -translate-y-1/2 text-muted-foreground/70 hover:text-foreground transition-colors"
133134
aria-label={showPassword ? "Hide password" : "Show password"}
134135
>
135136
{showPassword ? <EyeOff className="size-4" /> : <Eye className="size-4" />}
@@ -148,7 +149,7 @@ export default function ContentOnlyPage() {
148149
</div>
149150

150151
{/* Bottom text */}
151-
<p className="mt-6 text-center text-sm text-gray-500">
152+
<p className="mt-6 text-center text-sm text-muted-foreground">
152153
Don&apos;t have an account?{" "}
153154
<button
154155
type="button"
@@ -159,14 +160,11 @@ export default function ContentOnlyPage() {
159160
</p>
160161
</div>
161162

162-
{/* Floating variant indicator */}
163-
<div className="fixed bottom-4 left-1/2 -translate-x-1/2 z-50 flex items-center gap-1.5 rounded-full bg-black/70 backdrop-blur-sm px-3 py-1.5 text-[11px] font-mono text-white/80 shadow-lg">
164-
<span className="size-1.5 rounded-full bg-emerald-400 animate-pulse" />
165-
content-only &middot; no header, no footer
166-
</div>
167163
</div>
168164
</Content>
169165
</AppShell>
166+
167+
<IndicatorPill>content-only &middot; no header, no footer</IndicatorPill>
170168
</MotionProvider>
171169
);
172170
}

0 commit comments

Comments
 (0)