From 782923f2e0de698af36c64b32cc03ca17dec3400 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Kr=C3=BCger?= Date: Thu, 26 Feb 2026 22:22:32 +0100 Subject: [PATCH] feat: refactor theme, add tailwind-scrollbar, and improve UI components - Removed manual theme switching logic and ThemeProvider - Installed and configured tailwind-scrollbar plugin - Updated FileConverter and ConversionOptions to use shadcn Input - Refactored FontSelector to use shadcn Tabs - Simplified global styles and adjusted glassmorphic effects --- .mcp.json | 11 ++++ CLAUDE.md | 77 +++++++++++++++++++++++ app/(app)/layout.tsx | 1 + app/globals.css | 32 ++-------- app/layout.tsx | 25 +------- app/not-found.tsx | 6 -- app/page.tsx | 7 --- components/AnimatedBackground.tsx | 10 +-- components/ToolCard.tsx | 4 +- components/ascii/FontSelector.tsx | 56 +++++++---------- components/layout/AppHeader.tsx | 28 +-------- components/layout/AppShell.tsx | 4 +- components/layout/AppSidebar.tsx | 11 +--- components/media/ConversionOptions.tsx | 10 +-- components/media/FileConverter.tsx | 7 +-- components/providers/Providers.tsx | 17 +++--- components/providers/ThemeProvider.tsx | 85 -------------------------- components/units/SearchUnits.tsx | 2 +- package.json | 1 + pnpm-lock.yaml | 32 ++++++++++ 20 files changed, 178 insertions(+), 248 deletions(-) create mode 100644 .mcp.json create mode 100644 CLAUDE.md delete mode 100644 components/providers/ThemeProvider.tsx diff --git a/.mcp.json b/.mcp.json new file mode 100644 index 0000000..bd98b4f --- /dev/null +++ b/.mcp.json @@ -0,0 +1,11 @@ +{ + "mcpServers": { + "shadcn": { + "command": "npx", + "args": [ + "shadcn@latest", + "mcp" + ] + } + } +} diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..e519bb8 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,77 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Project + +Kit UI is a static-export toolkit (Next.js 16, React 19, TypeScript strict) with five browser-based tools: Color, Units, ASCII, Media, and Favicon. All heavy processing runs client-side via WebAssembly. Deployed at kit.pivoine.art. + +## Commands + +```bash +pnpm dev # Dev server with Turbopack (localhost:3000) +pnpm build # Static export to /out +pnpm lint # ESLint (next/core-web-vitals) +pnpm postinstall # Copies WASM binaries to public/wasm/ (runs automatically on install) +``` + +There are no test suites. Use `pnpm build` to verify changes compile correctly. + +## Architecture + +### Routing (App Router) + +``` +app/ +├── page.tsx # Landing page (dark mode forced) +├── (app)/layout.tsx # Wraps all tools with Providers + AppShell +├── (app)/color/ # Color manipulation (@valknarthing/pastel-wasm) +├── (app)/units/ # Unit converter (187+ units, 23 categories) +├── (app)/ascii/ # ASCII art (373 figlet fonts) +├── (app)/media/ # File converter (FFmpeg + ImageMagick WASM) +└── (app)/favicon/ # Favicon/PWA asset generator +``` + +### Code Organization + +Each tool follows a mirrored structure across three directories: + +- **`app/(app)/{tool}/page.tsx`** — Route entry point +- **`components/{tool}/`** — UI components for the tool +- **`lib/{tool}/`** — Business logic, WASM wrappers, stores, and utilities +- **`types/`** — Shared TypeScript definitions + +Shared UI primitives live in `components/ui/` (shadcn/ui, customized with glassmorphic styling). Layout shell in `components/layout/`. + +### State Management (three layers) + +1. **URL params** — Primary for shareable state (`useSearchParams` / `useRouter().push`) +2. **React Query** — Async WASM computations with caching +3. **Zustand** — Per-tool client stores in `lib/{tool}/` +4. **localStorage** — Persistence for theme, favorites, history + +### WASM Integration + +WASM modules (FFmpeg, ImageMagick, pastel-wasm) are lazy-loaded on first use. Binaries live in `public/wasm/` and are copied there by the postinstall script — don't move them manually. WASM logic is browser-only; do not attempt to run it in Node. + +## Conventions + +- **`'use client'`** only where needed (WASM, browser APIs, interactive state). Default to RSC. +- **Styling**: Tailwind CSS 4 with CSS-first config in `app/globals.css`. Use `cn()` from `lib/utils/cn.ts` for conditional classes. Use `@utility glass` for glassmorphic effects and gradient utilities (`gradient-purple-blue`, etc.). +- **Icons**: Lucide React exclusively. +- **Imports**: Use `@/` path alias (resolves to project root). +- **Components**: shadcn/ui from `components/ui/` as building blocks. +- **Logic/UI separation**: Business logic in `lib/`, UI in `components/`. Keep them separate. + +## Deployment + +Static export (`output: 'export'` in next.config.ts) served by Nginx via Docker. No Node.js runtime in production. + +```bash +docker build -t kit-ui . +docker run -p 80:80 kit-ui +``` + +## PWA + +Service worker at `public/sw.js` pre-caches core assets and WASM binaries. Manifest generated from `app/manifest.ts` (force-static). Cache version: `kit-ui-v1`. diff --git a/app/(app)/layout.tsx b/app/(app)/layout.tsx index 65c0b2e..0f00bc5 100644 --- a/app/(app)/layout.tsx +++ b/app/(app)/layout.tsx @@ -1,3 +1,4 @@ +import AnimatedBackground from '@/components/AnimatedBackground'; import { AppShell } from '@/components/layout/AppShell'; import { Providers } from '@/components/providers/Providers'; diff --git a/app/globals.css b/app/globals.css index 442d8b7..d1a6366 100644 --- a/app/globals.css +++ b/app/globals.css @@ -1,4 +1,5 @@ @import "tailwindcss"; +@plugin "tailwind-scrollbar"; @source "../components/*.{js,ts,jsx,tsx}"; @source "../components/ui/*.{js,ts,jsx,tsx}"; @@ -85,14 +86,13 @@ } } -:root, .dark { - color-scheme: dark; +:root { /* CORPORATE DARK THEME (The Standard) */ --background: #0a0a0f; --foreground: #ffffff; --card: rgba(255, 255, 255, 0.03); --card-foreground: #ffffff; - --popover: #0f0f15; + --popover: #5D429C; --popover-foreground: #ffffff; --primary: #8b5cf6; --primary-foreground: #ffffff; @@ -110,36 +110,12 @@ --radius: 1rem; } -.light { - color-scheme: light; - /* LIGHT ADAPTATION (Keeping the "Glass" look) */ - --background: oklch(98% 0.005 255); - --foreground: oklch(20% 0.04 255); - --card: rgba(255, 255, 255, 0.4); - --card-foreground: oklch(20% 0.04 255); - --popover: oklch(100% 0 0); - --popover-foreground: oklch(20% 0.04 255); - --primary: oklch(55% 0.22 270); - --primary-foreground: oklch(100% 0 0); - --secondary: rgba(0, 0, 0, 0.02); - --secondary-foreground: oklch(20% 0.04 255); - --muted: rgba(0, 0, 0, 0.02); - --muted-foreground: oklch(45% 0.04 255); - --accent: rgba(0, 0, 0, 0.03); - --accent-foreground: oklch(15% 0.05 255); - --destructive: oklch(60% 0.2 25); - --destructive-foreground: oklch(100% 0 0); - --border: rgba(0, 0, 0, 0.2); - --input: rgba(0, 0, 0, 0.08); - --ring: rgba(139, 92, 246, 0.4); -} - @layer base { * { @apply border-border outline-ring/50; } body { - @apply bg-background text-foreground; + @apply bg-background text-foreground scrollbar-thin scrollbar-thumb-primary/20 scrollbar-track-transparent hover:scrollbar-thumb-primary/40; font-family: var(--font-sans); -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; diff --git a/app/layout.tsx b/app/layout.tsx index f70e274..91b64ee 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -1,6 +1,5 @@ import type { Metadata } from 'next'; import './globals.css'; -import { Providers } from '@/components/providers/Providers'; const siteUrl = process.env.NEXT_PUBLIC_SITE_URL || 'http://localhost:3000'; @@ -37,7 +36,7 @@ export default function RootLayout({ const isProd = process.env.NODE_ENV === 'production'; return ( - + @@ -49,28 +48,6 @@ export default function RootLayout({ {isProd && umamiScript && umamiId && ( )} -