Skip to content

Commit ca75fa0

Browse files
authored
PR.04 feat/shadcn-theme-nav
feature: add navbar with shadcn/ui theming
2 parents f0036d1 + f6e53e6 commit ca75fa0

14 files changed

Lines changed: 320 additions & 165 deletions

File tree

.claude/commands/commit.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ description: Create git commit for staged changes using template
4545
- `style:` code formatting, visual consistency, linting fixes; no functional change
4646
- `chore:` dev workflow, workspace config, dependency updates, dev tools e.g. `.vscode/**/*`, `pyproject.toml`, `.gitignore`
4747
- `docs:` documentation changes only e.g. `README.md`, `docs/**/*.md`, `x_docs/**/*.md`
48-
- `feature:` new feature for users (adds functionality)
48+
- `feat:` new feature for users (adds functionality)
4949
</main_prefix>
5050

5151
<rules>

app/globals.css

Lines changed: 58 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,67 @@
11
@import "tailwindcss";
2+
@import "tw-animate-css";
23

34
/* Applies dark: styles when element is .dark or inside .dark */
45
@custom-variant dark (&:where(.dark, .dark *));
56

7+
@theme inline {
8+
/* Register theme tokens with Tailwind so it generates utilities (bg-*, text-*, etc.) */
9+
/* E.g. --color-background → bg-background, text-background, border-background, etc. */
10+
/* "inline" (v4) means values are embedded here, not in a separate CSS layer */
11+
--font-display: var(--font-space-grotesk), Arial, sans-serif;
12+
--font-sans-serif: var(--font-inter), ui-sans-serif, system-ui, sans-serif;
13+
--font-mono: var(--font-jetbrains-mono), ui-monospace, Menlo, monospace;
14+
15+
--radius-sm: calc(var(--radius) - 4px);
16+
--radius-md: calc(var(--radius) - 2px);
17+
--radius-lg: var(--radius);
18+
--radius-xl: calc(var(--radius) + 4px);
19+
20+
--color-background: var(--background);
21+
--color-foreground: var(--foreground);
22+
--color-sidebar-ring: var(--sidebar-ring);
23+
--color-sidebar-border: var(--sidebar-border);
24+
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
25+
--color-sidebar-accent: var(--sidebar-accent);
26+
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
27+
--color-sidebar-primary: var(--sidebar-primary);
28+
--color-sidebar-foreground: var(--sidebar-foreground);
29+
--color-sidebar: var(--sidebar);
30+
--color-chart-5: var(--chart-5);
31+
--color-chart-4: var(--chart-4);
32+
--color-chart-3: var(--chart-3);
33+
--color-chart-2: var(--chart-2);
34+
--color-chart-1: var(--chart-1);
35+
--color-ring: var(--ring);
36+
--color-input: var(--input);
37+
--color-border: var(--border);
38+
--color-destructive: var(--destructive);
39+
--color-accent-foreground: var(--accent-foreground);
40+
--color-accent: var(--accent);
41+
--color-muted-foreground: var(--muted-foreground);
42+
--color-muted: var(--muted);
43+
--color-secondary-foreground: var(--secondary-foreground);
44+
--color-secondary: var(--secondary);
45+
--color-primary-foreground: var(--primary-foreground);
46+
--color-primary: var(--primary);
47+
--color-popover-foreground: var(--popover-foreground);
48+
--color-popover: var(--popover);
49+
--color-card-foreground: var(--card-foreground);
50+
--color-card: var(--card);
51+
}
52+
653
:root {
754
/* Creates CSS variables available to all elements (no utility generation) */
855
--radius: 0.625rem;
956

10-
--background: oklch(1 0 0);
57+
--background: oklch(0.994 0 0); /* #FDFDFD */
1158
--foreground: oklch(0.129 0.042 264.695);
12-
1359
--card: oklch(1 0 0);
1460
--card-foreground: oklch(0.129 0.042 264.695);
1561
--popover: oklch(1 0 0);
1662
--popover-foreground: oklch(0.129 0.042 264.695);
17-
--primary: oklch(0.208 0.042 265.755);
18-
--primary-foreground: oklch(0.984 0.003 247.858);
63+
--primary: oklch(0.7089 0.1967 46.81);
64+
--primary-foreground: oklch(1 0 0);
1965
--secondary: oklch(0.968 0.007 247.896);
2066
--secondary-foreground: oklch(0.208 0.042 265.755);
2167
--muted: oklch(0.968 0.007 247.896);
@@ -26,15 +72,13 @@
2672
--border: oklch(0.929 0.013 255.508);
2773
--input: oklch(0.929 0.013 255.508);
2874
--ring: oklch(0.704 0.04 256.788);
29-
3075
--chart-1: oklch(0.646 0.222 41.116);
3176
--chart-2: oklch(0.6 0.118 184.704);
3277
--chart-3: oklch(0.398 0.07 227.392);
3378
--chart-4: oklch(0.828 0.189 84.429);
3479
--chart-5: oklch(0.769 0.188 70.08);
35-
--sidebar: oklch(0.984 0.003 247.858);
36-
37-
--sidebar-foreground: oklch(0.129 0.042 264.695);
80+
--sidebar: oklch(1 0 0); /* #FFFFFF */
81+
--sidebar-foreground: oklch(0.2102 0.0185 270.39); /* #151821 */
3882
--sidebar-primary: oklch(0.208 0.042 265.755);
3983
--sidebar-primary-foreground: oklch(0.984 0.003 247.858);
4084
--sidebar-accent: oklch(0.968 0.007 247.896);
@@ -45,14 +89,14 @@
4589

4690
.dark {
4791
/* Overrides CSS variables when .dark class is present */
48-
--background: oklch(0.129 0.042 264.695);
92+
--background: oklch(0 0 0); /* #000000 */
4993
--foreground: oklch(0.984 0.003 247.858);
5094
--card: oklch(0.208 0.042 265.755);
5195
--card-foreground: oklch(0.984 0.003 247.858);
5296
--popover: oklch(0.208 0.042 265.755);
5397
--popover-foreground: oklch(0.984 0.003 247.858);
54-
--primary: oklch(0.929 0.013 255.508);
55-
--primary-foreground: oklch(0.208 0.042 265.755);
98+
--primary: oklch(0.7089 0.1967 46.81);
99+
--primary-foreground: oklch(1 0 0);
56100
--secondary: oklch(0.279 0.041 260.031);
57101
--secondary-foreground: oklch(0.984 0.003 247.858);
58102
--muted: oklch(0.279 0.041 260.031);
@@ -68,8 +112,8 @@
68112
--chart-3: oklch(0.769 0.188 70.08);
69113
--chart-4: oklch(0.627 0.265 303.9);
70114
--chart-5: oklch(0.645 0.246 16.439);
71-
--sidebar: oklch(0.208 0.042 265.755);
72-
--sidebar-foreground: oklch(0.984 0.003 247.858);
115+
--sidebar: oklch(0.178 0.013 270.6); /* #0F1117 */
116+
--sidebar-foreground: oklch(1 0 0); /* #FFFFFF */
73117
--sidebar-primary: oklch(0.488 0.243 264.376);
74118
--sidebar-primary-foreground: oklch(0.984 0.003 247.858);
75119
--sidebar-accent: oklch(0.279 0.041 260.031);
@@ -78,56 +122,6 @@
78122
--sidebar-ring: oklch(0.551 0.027 264.364);
79123
}
80124

81-
@theme inline {
82-
/* Maps root and .dark variables to Tailwind utility classes */
83-
/* E.g. --color-background → bg-background, text-background, border-background, etc. */
84-
/* The keyword "inline" (v4) means "define theme values directly in this CSS file" */
85-
--color-background: var(--background);
86-
--color-foreground: var(--foreground);
87-
88-
--font-display: var(--font-space-grotesk), Arial, sans-serif;
89-
--font-sans-serif: var(--font-inter), ui-sans-serif, system-ui, sans-serif;
90-
--font-mono:
91-
var(--font-jetbrains-mono), ui-monospace, Menlo, Consolas, monospace;
92-
93-
--color-sidebar-ring: var(--sidebar-ring);
94-
--color-sidebar-border: var(--sidebar-border);
95-
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
96-
--color-sidebar-accent: var(--sidebar-accent);
97-
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
98-
--color-sidebar-primary: var(--sidebar-primary);
99-
--color-sidebar-foreground: var(--sidebar-foreground);
100-
--color-sidebar: var(--sidebar);
101-
102-
--color-chart-5: var(--chart-5);
103-
--color-chart-4: var(--chart-4);
104-
--color-chart-3: var(--chart-3);
105-
--color-chart-2: var(--chart-2);
106-
--color-chart-1: var(--chart-1);
107-
108-
--color-ring: var(--ring);
109-
--color-input: var(--input);
110-
--color-border: var(--border);
111-
--color-destructive: var(--destructive);
112-
--color-accent-foreground: var(--accent-foreground);
113-
--color-accent: var(--accent);
114-
--color-muted-foreground: var(--muted-foreground);
115-
--color-muted: var(--muted);
116-
--color-secondary-foreground: var(--secondary-foreground);
117-
--color-secondary: var(--secondary);
118-
--color-primary-foreground: var(--primary-foreground);
119-
--color-primary: var(--primary);
120-
--color-popover-foreground: var(--popover-foreground);
121-
--color-popover: var(--popover);
122-
--color-card-foreground: var(--card-foreground);
123-
--color-card: var(--card);
124-
125-
--radius-sm: calc(var(--radius) - 4px);
126-
--radius-md: calc(var(--radius) - 2px);
127-
--radius-lg: var(--radius);
128-
--radius-xl: calc(var(--radius) + 4px);
129-
}
130-
131125
@layer base {
132126
/* Applies styles to actual elements (unlike :root/.dark which only store variables) */
133127
/* Low specificity - can be overridden by components and utilities */
@@ -137,7 +131,7 @@
137131
@apply border-border outline-ring/50;
138132
}
139133
body {
140-
/* Page background and text color (text color inherited) */
134+
/* Page background and text color */
141135
@apply bg-background text-foreground;
142136
}
143137

app/layout.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
import { Analytics } from "@vercel/analytics/next";
22
import { SpeedInsights } from "@vercel/speed-insights/next";
33
import type { Metadata } from "next";
4-
import "@/app/globals.css";
54
import { inter, jetbrainsMono, spaceGrotesk } from "@/app/fonts";
5+
import { Navbar } from "@/components/navigation/navbar";
66
import { ThemeProvider } from "@/components/theme-provider";
7+
import "@/app/globals.css";
78

89
export const metadata: Metadata = {
910
title: "Devflow",
@@ -29,6 +30,7 @@ export default function RootLayout({
2930
enableSystem
3031
disableTransitionOnChange
3132
>
33+
<Navbar />
3234
{children}
3335
</ThemeProvider>
3436
<SpeedInsights />

app/page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
const Home = () => (
22
<>
3-
<h1 className="h1-bold">Welcome to the world of Next.js</h1>
3+
<h1>Welcome to the world of Next.js</h1>
44
</>
55
);
66

components.json

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
{
2+
"$schema": "https://ui.shadcn.com/schema.json",
3+
"style": "new-york",
4+
"rsc": true,
5+
"tsx": true,
6+
"tailwind": {
7+
"config": "",
8+
"css": "globals.css",
9+
"baseColor": "slate",
10+
"cssVariables": true,
11+
"prefix": ""
12+
},
13+
"iconLibrary": "lucide",
14+
"aliases": {
15+
"components": "@/components",
16+
"utils": "@/lib/utils",
17+
"ui": "@/components/ui",
18+
"lib": "@/lib",
19+
"hooks": "@/hooks"
20+
},
21+
"registries": {}
22+
}

components/button.tsx

Lines changed: 0 additions & 34 deletions
This file was deleted.
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import Image from "next/image";
2+
import Link from "next/link";
3+
import { ThemeToggle } from "@/components/navigation/theme-toggle";
4+
5+
export const Navbar = () => (
6+
<nav
7+
aria-label="Primary navigation"
8+
className="sticky top-0 z-50 grid w-full grid-cols-3 items-center bg-sidebar px-6 py-4 shadow-sm"
9+
>
10+
{/* Left: Logo + DevFlow text */}
11+
<Link href="/" className="flex items-center gap-2 justify-self-start">
12+
<Image
13+
src="/images/site-logo.svg"
14+
alt="DevFlow logo"
15+
width={25}
16+
height={25}
17+
/>
18+
<h2 className="hidden font-display text-2xl font-semibold text-sidebar-foreground sm:block">
19+
Dev<span className="text-primary">Flow</span>
20+
</h2>
21+
</Link>
22+
23+
{/* Centre: Global Search placeholder */}
24+
<p className="hidden justify-self-center text-sidebar-foreground sm:block">
25+
Global Search
26+
</p>
27+
28+
{/* Right: Theme toggle */}
29+
<div className="justify-self-end">
30+
<ThemeToggle />
31+
</div>
32+
</nav>
33+
);
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
"use client";
2+
3+
import { Sun } from "lucide-react";
4+
import { useTheme } from "next-themes";
5+
import { useEffect, useState } from "react";
6+
import { Button } from "@/components/ui/button";
7+
8+
export function ThemeToggle() {
9+
const { theme, setTheme } = useTheme();
10+
const [mounted, setMounted] = useState(false);
11+
12+
useEffect(() => setMounted(true), []);
13+
14+
if (!mounted) {
15+
return <div className="size-10" aria-hidden="true" />;
16+
}
17+
18+
return (
19+
<Button
20+
variant="ghost"
21+
size="icon-lg"
22+
onClick={() => setTheme(theme === "dark" ? "light" : "dark")}
23+
className="text-sidebar-foreground"
24+
aria-label={`Switch to ${theme === "dark" ? "light" : "dark"} theme`}
25+
>
26+
<Sun
27+
className="size-6"
28+
fill={theme === "light" ? "currentColor" : "none"}
29+
/>
30+
</Button>
31+
);
32+
}

0 commit comments

Comments
 (0)