Skip to content

Commit 884a152

Browse files
nullcoderclaude
andcommitted
feat: install and configure UI dependencies (closes #8)
- Initialized shadcn/ui with New York style and CSS variables - Installed next-themes for dark/light mode support - Created ThemeProvider component and integrated into layout - Added theme toggle button with smooth transitions - Configured global styles with CSS variables for theming - Updated home page to demonstrate theme switching - All Tailwind v4 compatible with oklch colors 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 101171d commit 884a152

File tree

11 files changed

+382
-118
lines changed

11 files changed

+382
-118
lines changed

app/globals.css

Lines changed: 109 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,122 @@
11
@import "tailwindcss";
2+
@import "tw-animate-css";
23

3-
:root {
4-
--background: #ffffff;
5-
--foreground: #171717;
6-
}
4+
@custom-variant dark (&:is(.dark *));
75

86
@theme inline {
97
--color-background: var(--background);
108
--color-foreground: var(--foreground);
119
--font-sans: var(--font-geist-sans);
1210
--font-mono: var(--font-geist-mono);
11+
--color-sidebar-ring: var(--sidebar-ring);
12+
--color-sidebar-border: var(--sidebar-border);
13+
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
14+
--color-sidebar-accent: var(--sidebar-accent);
15+
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
16+
--color-sidebar-primary: var(--sidebar-primary);
17+
--color-sidebar-foreground: var(--sidebar-foreground);
18+
--color-sidebar: var(--sidebar);
19+
--color-chart-5: var(--chart-5);
20+
--color-chart-4: var(--chart-4);
21+
--color-chart-3: var(--chart-3);
22+
--color-chart-2: var(--chart-2);
23+
--color-chart-1: var(--chart-1);
24+
--color-ring: var(--ring);
25+
--color-input: var(--input);
26+
--color-border: var(--border);
27+
--color-destructive: var(--destructive);
28+
--color-accent-foreground: var(--accent-foreground);
29+
--color-accent: var(--accent);
30+
--color-muted-foreground: var(--muted-foreground);
31+
--color-muted: var(--muted);
32+
--color-secondary-foreground: var(--secondary-foreground);
33+
--color-secondary: var(--secondary);
34+
--color-primary-foreground: var(--primary-foreground);
35+
--color-primary: var(--primary);
36+
--color-popover-foreground: var(--popover-foreground);
37+
--color-popover: var(--popover);
38+
--color-card-foreground: var(--card-foreground);
39+
--color-card: var(--card);
40+
--radius-sm: calc(var(--radius) - 4px);
41+
--radius-md: calc(var(--radius) - 2px);
42+
--radius-lg: var(--radius);
43+
--radius-xl: calc(var(--radius) + 4px);
1344
}
1445

15-
@media (prefers-color-scheme: dark) {
16-
:root {
17-
--background: #0a0a0a;
18-
--foreground: #ededed;
19-
}
46+
:root {
47+
--radius: 0.625rem;
48+
--background: oklch(1 0 0);
49+
--foreground: oklch(0.145 0 0);
50+
--card: oklch(1 0 0);
51+
--card-foreground: oklch(0.145 0 0);
52+
--popover: oklch(1 0 0);
53+
--popover-foreground: oklch(0.145 0 0);
54+
--primary: oklch(0.205 0 0);
55+
--primary-foreground: oklch(0.985 0 0);
56+
--secondary: oklch(0.97 0 0);
57+
--secondary-foreground: oklch(0.205 0 0);
58+
--muted: oklch(0.97 0 0);
59+
--muted-foreground: oklch(0.556 0 0);
60+
--accent: oklch(0.97 0 0);
61+
--accent-foreground: oklch(0.205 0 0);
62+
--destructive: oklch(0.577 0.245 27.325);
63+
--border: oklch(0.922 0 0);
64+
--input: oklch(0.922 0 0);
65+
--ring: oklch(0.708 0 0);
66+
--chart-1: oklch(0.646 0.222 41.116);
67+
--chart-2: oklch(0.6 0.118 184.704);
68+
--chart-3: oklch(0.398 0.07 227.392);
69+
--chart-4: oklch(0.828 0.189 84.429);
70+
--chart-5: oklch(0.769 0.188 70.08);
71+
--sidebar: oklch(0.985 0 0);
72+
--sidebar-foreground: oklch(0.145 0 0);
73+
--sidebar-primary: oklch(0.205 0 0);
74+
--sidebar-primary-foreground: oklch(0.985 0 0);
75+
--sidebar-accent: oklch(0.97 0 0);
76+
--sidebar-accent-foreground: oklch(0.205 0 0);
77+
--sidebar-border: oklch(0.922 0 0);
78+
--sidebar-ring: oklch(0.708 0 0);
2079
}
2180

22-
body {
23-
background: var(--background);
24-
color: var(--foreground);
25-
font-family: Arial, Helvetica, sans-serif;
81+
.dark {
82+
--background: oklch(0.145 0 0);
83+
--foreground: oklch(0.985 0 0);
84+
--card: oklch(0.205 0 0);
85+
--card-foreground: oklch(0.985 0 0);
86+
--popover: oklch(0.205 0 0);
87+
--popover-foreground: oklch(0.985 0 0);
88+
--primary: oklch(0.922 0 0);
89+
--primary-foreground: oklch(0.205 0 0);
90+
--secondary: oklch(0.269 0 0);
91+
--secondary-foreground: oklch(0.985 0 0);
92+
--muted: oklch(0.269 0 0);
93+
--muted-foreground: oklch(0.708 0 0);
94+
--accent: oklch(0.269 0 0);
95+
--accent-foreground: oklch(0.985 0 0);
96+
--destructive: oklch(0.704 0.191 22.216);
97+
--border: oklch(1 0 0 / 10%);
98+
--input: oklch(1 0 0 / 15%);
99+
--ring: oklch(0.556 0 0);
100+
--chart-1: oklch(0.488 0.243 264.376);
101+
--chart-2: oklch(0.696 0.17 162.48);
102+
--chart-3: oklch(0.769 0.188 70.08);
103+
--chart-4: oklch(0.627 0.265 303.9);
104+
--chart-5: oklch(0.645 0.246 16.439);
105+
--sidebar: oklch(0.205 0 0);
106+
--sidebar-foreground: oklch(0.985 0 0);
107+
--sidebar-primary: oklch(0.488 0.243 264.376);
108+
--sidebar-primary-foreground: oklch(0.985 0 0);
109+
--sidebar-accent: oklch(0.269 0 0);
110+
--sidebar-accent-foreground: oklch(0.985 0 0);
111+
--sidebar-border: oklch(1 0 0 / 10%);
112+
--sidebar-ring: oklch(0.556 0 0);
113+
}
114+
115+
@layer base {
116+
* {
117+
@apply border-border outline-ring/50;
118+
}
119+
body {
120+
@apply bg-background text-foreground;
121+
}
26122
}

app/layout.tsx

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import type { Metadata } from "next";
22
import { Geist, Geist_Mono } from "next/font/google";
3+
import { ThemeProvider } from "@/components/theme-provider";
34
import "./globals.css";
45

56
const geistSans = Geist({
@@ -13,8 +14,9 @@ const geistMono = Geist_Mono({
1314
});
1415

1516
export const metadata: Metadata = {
16-
title: "Create Next App",
17-
description: "Generated by create next app",
17+
title: "GhostPaste - Zero-Knowledge Encrypted Code Sharing",
18+
description:
19+
"Share code snippets securely with client-side encryption. Your code, your privacy.",
1820
};
1921

2022
export default function RootLayout({
@@ -23,11 +25,18 @@ export default function RootLayout({
2325
children: React.ReactNode;
2426
}>) {
2527
return (
26-
<html lang="en">
28+
<html lang="en" suppressHydrationWarning>
2729
<body
2830
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
2931
>
30-
{children}
32+
<ThemeProvider
33+
attribute="class"
34+
defaultTheme="system"
35+
enableSystem
36+
disableTransitionOnChange
37+
>
38+
{children}
39+
</ThemeProvider>
3140
</body>
3241
</html>
3342
);

app/page.tsx

Lines changed: 27 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -1,103 +1,35 @@
1-
import Image from "next/image";
1+
import { ThemeToggle } from "@/components/theme-toggle";
22

33
export default function Home() {
44
return (
5-
<div className="grid min-h-screen grid-rows-[20px_1fr_20px] items-center justify-items-center gap-16 p-8 pb-20 font-[family-name:var(--font-geist-sans)] sm:p-20">
6-
<main className="row-start-2 flex flex-col items-center gap-[32px] sm:items-start">
7-
<Image
8-
className="dark:invert"
9-
src="/next.svg"
10-
alt="Next.js logo"
11-
width={180}
12-
height={38}
13-
priority
14-
/>
15-
<ol className="list-inside list-decimal text-center font-[family-name:var(--font-geist-mono)] text-sm/6 sm:text-left">
16-
<li className="mb-2 tracking-[-.01em]">
17-
Get started by editing{" "}
18-
<code className="rounded bg-black/[.05] px-1 py-0.5 font-[family-name:var(--font-geist-mono)] font-semibold dark:bg-white/[.06]">
19-
app/page.tsx
20-
</code>
21-
.
22-
</li>
23-
<li className="tracking-[-.01em]">
24-
Save and see your changes instantly.
25-
</li>
26-
</ol>
27-
28-
<div className="flex flex-col items-center gap-4 sm:flex-row">
29-
<a
30-
className="bg-foreground text-background flex h-10 items-center justify-center gap-2 rounded-full border border-solid border-transparent px-4 text-sm font-medium transition-colors hover:bg-[#383838] sm:h-12 sm:w-auto sm:px-5 sm:text-base dark:hover:bg-[#ccc]"
31-
href="https://vercel.com/new?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
32-
target="_blank"
33-
rel="noopener noreferrer"
34-
>
35-
<Image
36-
className="dark:invert"
37-
src="/vercel.svg"
38-
alt="Vercel logomark"
39-
width={20}
40-
height={20}
41-
/>
42-
Deploy now
43-
</a>
44-
<a
45-
className="flex h-10 w-full items-center justify-center rounded-full border border-solid border-black/[.08] px-4 text-sm font-medium transition-colors hover:border-transparent hover:bg-[#f2f2f2] sm:h-12 sm:w-auto sm:px-5 sm:text-base md:w-[158px] dark:border-white/[.145] dark:hover:bg-[#1a1a1a]"
46-
href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
47-
target="_blank"
48-
rel="noopener noreferrer"
49-
>
50-
Read our docs
51-
</a>
5+
<div className="min-h-screen p-8">
6+
<header className="mb-8 flex items-center justify-between">
7+
<h1 className="text-3xl font-bold">GhostPaste</h1>
8+
<ThemeToggle />
9+
</header>
10+
<main className="mx-auto max-w-4xl">
11+
<div className="bg-card text-card-foreground rounded-lg border p-6 shadow-sm">
12+
<h2 className="mb-4 text-2xl font-semibold">
13+
Zero-Knowledge Encrypted Code Sharing
14+
</h2>
15+
<p className="text-muted-foreground mb-4">
16+
Share code snippets securely with client-side encryption. Your code,
17+
your privacy.
18+
</p>
19+
<div className="space-y-2">
20+
<p>
21+
Current theme:{" "}
22+
<code className="bg-muted rounded px-2 py-1 text-sm">
23+
system/light/dark
24+
</code>
25+
</p>
26+
<p>
27+
Click the theme toggle button in the header to switch between
28+
light and dark modes.
29+
</p>
30+
</div>
5231
</div>
5332
</main>
54-
<footer className="row-start-3 flex flex-wrap items-center justify-center gap-[24px]">
55-
<a
56-
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
57-
href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
58-
target="_blank"
59-
rel="noopener noreferrer"
60-
>
61-
<Image
62-
aria-hidden
63-
src="/file.svg"
64-
alt="File icon"
65-
width={16}
66-
height={16}
67-
/>
68-
Learn
69-
</a>
70-
<a
71-
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
72-
href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
73-
target="_blank"
74-
rel="noopener noreferrer"
75-
>
76-
<Image
77-
aria-hidden
78-
src="/window.svg"
79-
alt="Window icon"
80-
width={16}
81-
height={16}
82-
/>
83-
Examples
84-
</a>
85-
<a
86-
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
87-
href="https://nextjs.org?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
88-
target="_blank"
89-
rel="noopener noreferrer"
90-
>
91-
<Image
92-
aria-hidden
93-
src="/globe.svg"
94-
alt="Globe icon"
95-
width={16}
96-
height={16}
97-
/>
98-
Go to nextjs.org →
99-
</a>
100-
</footer>
10133
</div>
10234
);
10335
}

components.json

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
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": "app/globals.css",
9+
"baseColor": "neutral",
10+
"cssVariables": true,
11+
"prefix": ""
12+
},
13+
"aliases": {
14+
"components": "@/components",
15+
"utils": "@/lib/utils",
16+
"ui": "@/components/ui",
17+
"lib": "@/lib",
18+
"hooks": "@/hooks"
19+
},
20+
"iconLibrary": "lucide"
21+
}

components/theme-provider.tsx

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
"use client";
2+
3+
import * as React from "react";
4+
import { ThemeProvider as NextThemesProvider } from "next-themes";
5+
6+
export function ThemeProvider({
7+
children,
8+
...props
9+
}: React.ComponentProps<typeof NextThemesProvider>) {
10+
return <NextThemesProvider {...props}>{children}</NextThemesProvider>;
11+
}

components/theme-toggle.tsx

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
"use client";
2+
3+
import * as React from "react";
4+
import { Moon, Sun } from "lucide-react";
5+
import { useTheme } from "next-themes";
6+
7+
import { Button } from "@/components/ui/button";
8+
9+
export function ThemeToggle() {
10+
const { setTheme, theme } = useTheme();
11+
12+
return (
13+
<Button
14+
variant="ghost"
15+
size="icon"
16+
onClick={() => setTheme(theme === "light" ? "dark" : "light")}
17+
>
18+
<Sun className="h-[1.2rem] w-[1.2rem] scale-100 rotate-0 transition-all dark:scale-0 dark:-rotate-90" />
19+
<Moon className="absolute h-[1.2rem] w-[1.2rem] scale-0 rotate-90 transition-all dark:scale-100 dark:rotate-0" />
20+
<span className="sr-only">Toggle theme</span>
21+
</Button>
22+
);
23+
}

0 commit comments

Comments
 (0)