Back to list
Lightbridge-KS

taurinext-shadcn

by Lightbridge-KS

Tauri + Next JS + shadcn / ui

1🍴 0📅 Oct 27, 2025

SKILL.md


name: taurinext-shadcn description: taurinext-shadcn template reference doc. Use this when to find in-depth information about this template.

TauriNext-shadcn

This document provides context for AI assistants (like Claude) working on this taurinext-shadcn template codebase.

Project Architecture

This TauriNext-shadcn is a template repository for building cross-platform applications that run as:

  1. Web app (SPA in browser)
  2. Desktop app (native via Tauri)

Both targets share the same Next.js codebase with shadcn/ui components.

Tech Stack

  • Frontend Framework: Next.js 15.5.5 (App Router)
  • UI Library: React 19.1.0
  • Component System: shadcn/ui (New York style)
  • Styling: Tailwind CSS v3.4.18 (CRITICAL: NOT v4)
  • CSS Utilities:
    • class-variance-authority - Component variants
    • clsx + tailwind-merge - Class name merging
    • tailwindcss-animate - Animations
  • Icons: Lucide React
  • Desktop Runtime: Tauri v2
  • Language: TypeScript (strict mode)
  • Build Mode: Static Export (CSR only, NO SSR)

CRITICAL Configuration Details

1. Tailwind & PostCSS Config Location

MUST BE IN src-next/ DIRECTORY, NOT ROOT

✅ CORRECT:
src-next/tailwind.config.cjs
src-next/postcss.config.cjs

❌ WRONG:
tailwind.config.cjs (at root)
postcss.config.cjs (at root)

Why:

  • npm run dev:next executes cd src-next && next dev
  • Next.js runs from inside src-next/ directory
  • It looks for configs in its working directory
  • If configs are at root, Tailwind won't process CSS

2. Config File Format: CommonJS (.cjs)

MUST USE .cjs EXTENSION, NOT .js

// ✅ CORRECT: tailwind.config.cjs
module.exports = {
  darkMode: ["class"],
  content: ["./app/**/*.{js,ts,jsx,tsx,mdx}"],
  // ...
}

// ❌ WRONG: tailwind.config.js with ES modules
export default {
  // This will NOT work
}

Why:

  • Root package.json has "type": "module"
  • This makes .js files use ES module syntax by default
  • PostCSS loader requires CommonJS format
  • .cjs explicitly forces CommonJS, overriding package.json setting

3. Tailwind CSS Version

MUST BE v3.4.x, NEVER v4.x

{
  "devDependencies": {
    "tailwindcss": "^3.4.18",        // ✅ CORRECT
    "tailwindcss-animate": "^1.0.7"   // ✅ Required
  }
}

Why:

  • shadcn/ui components built for Tailwind v3
  • Tailwind v4 uses completely different config format (@import in CSS)
  • v4 incompatible with current shadcn/ui components
  • v4 config syntax is radically different

If v4 accidentally installed:

npm uninstall tailwindcss
npm install -D tailwindcss@^3.4.18

4. shadcn/ui CLI Limitation

THE SHADCN CLI DOES NOT WORK WITH THIS PROJECT

Reason:

  • CLI expects standard Next.js structure (project root = Next.js root)
  • This project has Next.js in src-next/ subdirectory
  • CLI cannot find tsconfig.json at root level
  • CLI fails with "Couldn't find tsconfig.json"

Solution: Manual Component Installation

  1. Visit https://ui.shadcn.com/docs/components/[component-name]
  2. Find component code (often linked to GitHub)
  3. Check for required dependencies (e.g., @radix-ui packages)
  4. Install dependencies: npm install @radix-ui/react-dialog
  5. Create file in src-next/components/ui/[name].tsx
  6. Copy component code
  7. Verify imports use @/ aliases

Example: Installing Dialog component

# 1. Install dependencies
npm install @radix-ui/react-dialog

# 2. Create file
# File: src-next/components/ui/dialog.tsx

# 3. Copy code from https://ui.shadcn.com/docs/components/dialog
# or from GitHub: https://github.com/shadcn-ui/ui/blob/main/apps/www/registry/new-york/ui/dialog.tsx

# 4. Verify imports
import { cn } from "@/lib/utils"  // ✅ Uses @ alias

Directory Structure

taurinext-shadcn/
├── src-next/                        # Next.js application
│   ├── app/                         # App Router
│   │   ├── layout.tsx               # Root layout (NO 'use client')
│   │   ├── page.tsx                 # Counter demo ('use client')
│   │   └── globals.css              # Tailwind + CSS variables
│   ├── components/                  # React components
│   │   └── ui/                      # shadcn/ui components
│   │       ├── button.tsx           # Installed
│   │       ├── card.tsx             # Installed
│   │       └── badge.tsx            # Installed
│   ├── lib/                         # Utilities
│   │   └── utils.ts                 # cn() helper
│   ├── public/                      # Static assets
│   ├── tailwind.config.cjs          # ⚠️ MUST be .cjs in src-next/
│   ├── postcss.config.cjs           # ⚠️ MUST be .cjs in src-next/
│   ├── next.config.ts               # Next.js config
│   └── tsconfig.json                # TypeScript config
├── src-tauri/                       # Tauri Rust code
├── components.json                  # shadcn config (root level)
└── package.json                     # "type": "module"

Installed shadcn/ui Components

Button (src-next/components/ui/button.tsx)

Dependencies:

{
  "dependencies": {
    "@radix-ui/react-slot": "^1.x"
  }
}

Variants: default, destructive, outline, secondary, ghost, link

Sizes: default, sm, lg, icon

Key Features:

  • Polymorphic via asChild prop (uses Radix Slot)
  • Uses cva (class-variance-authority) for variants
  • Supports all button HTML attributes

Usage:

import { Button } from "@/components/ui/button"

<Button variant="outline" size="lg">Click Me</Button>
<Button asChild><Link href="/page">Link Button</Link></Button>

Card (src-next/components/ui/card.tsx)

Dependencies: None (pure Tailwind)

Exports: Card, CardHeader, CardTitle, CardDescription, CardContent, CardFooter

Usage:

import { Card, CardHeader, CardTitle, CardContent } from "@/components/ui/card"

<Card>
  <CardHeader>
    <CardTitle>Title</CardTitle>
  </CardHeader>
  <CardContent>Content here</CardContent>
</Card>

Badge (src-next/components/ui/badge.tsx)

Dependencies: None

Variants: default, secondary, destructive, outline

Usage:

import { Badge } from "@/components/ui/badge"

<Badge variant="secondary">New</Badge>

Styling System

CSS Variables (HSL Format)

File: src-next/app/globals.css

@tailwind base;
@tailwind components;
@tailwind utilities;

@layer base {
  :root {
    --background: 0 0% 100%;           /* HSL values (no hsl() wrapper) */
    --foreground: 0 0% 3.9%;
    --primary: 0 0% 9%;
    --primary-foreground: 0 0% 98%;
    /* ... more colors */
  }

  .dark {
    --background: 0 0% 3.9%;           /* Dark mode values */
    --foreground: 0 0% 98%;
    /* ... more colors */
  }
}

body {
  background-color: hsl(var(--background));   /* Wrap in hsl() here */
  color: hsl(var(--foreground));
}

Format Notes:

  • Variables defined as bare HSL values: 0 0% 100%
  • Used with hsl() wrapper: hsl(var(--background))
  • Allows Tailwind opacity modifiers: bg-primary/50

Tailwind Color Mappings

File: src-next/tailwind.config.cjs

module.exports = {
  darkMode: ["class"],
  content: [
    "./app/**/*.{js,ts,jsx,tsx,mdx}",
    "./components/**/*.{js,ts,jsx,tsx,mdx}",
  ],
  theme: {
    extend: {
      colors: {
        border: "hsl(var(--border))",
        input: "hsl(var(--input))",
        ring: "hsl(var(--ring))",
        background: "hsl(var(--background))",
        foreground: "hsl(var(--foreground))",
        primary: {
          DEFAULT: "hsl(var(--primary))",
          foreground: "hsl(var(--primary-foreground))",
        },
        // ... all color mappings
      },
    },
  },
  plugins: [require("tailwindcss-animate")],
}

This enables:

  • bg-background class → background-color: hsl(var(--background))
  • text-primary class → color: hsl(var(--primary))
  • bg-primary/50 class → background-color: hsl(var(--primary) / 0.5)

cn() Utility Function

File: src-next/lib/utils.ts

import { clsx, type ClassValue } from "clsx"
import { twMerge } from "tailwind-merge"

export function cn(...inputs: ClassValue[]) {
  return twMerge(clsx(inputs))
}

Purpose:

  • Combines clsx (conditional classes) + twMerge (deduplication)
  • Properly merges Tailwind classes
  • Later classes override earlier ones

Usage:

// Conditional classes
cn("base-class", condition && "conditional-class")

// Merging with prop classes
cn("px-4 py-2", className)  // className from props can override

// Complex example
cn(
  "inline-flex items-center",
  variant === "default" && "bg-primary text-white",
  variant === "outline" && "border border-input",
  disabled && "opacity-50 cursor-not-allowed",
  className
)

Critical Constraints

1. Static Export Mode (CSR Only)

Configuration:

// src-next/next.config.ts
output: 'export',
images: { unoptimized: true },

Allowed:

  • ✅ Client Components ('use client')
  • ✅ Static generation at build time
  • ✅ Client-side routing
  • ✅ Client-side data fetching
  • ✅ Tailwind CSS
  • ✅ shadcn/ui components

NOT Allowed:

  • ❌ Server Components requiring runtime
  • ❌ API Routes
  • ❌ Server Actions
  • ❌ SSR, ISR
  • ❌ Middleware

2. TypeScript Strict Mode

// src-next/tsconfig.json
{
  "compilerOptions": {
    "strict": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "baseUrl": ".",
    "paths": {
      "@/*": ["./*"]
    }
  }
}

Requirements:

  • Explicit types (no any)
  • Proper null/undefined handling
  • Function return types (e.g., : void, : Promise<void>)

3. Path Aliases

Configuration: Already set in src-next/tsconfig.json

Usage:

import { Button } from "@/components/ui/button"     // ✅
import { cn } from "@/lib/utils"                    // ✅

import { Button } from "../../components/ui/button" // ❌ Avoid

Common Development Tasks

Adding a New Page

// src-next/app/newpage/page.tsx
'use client';

import { Card, CardContent } from "@/components/ui/card";

export default function NewPage() {
  return (
    <div className="container mx-auto p-4">
      <Card>
        <CardContent className="p-6">
          <h1 className="text-2xl font-bold">New Page</h1>
        </CardContent>
      </Card>
    </div>
  );
}

Adding Client-Side Data Fetching

'use client';

import { useEffect, useState } from 'react';
import { Card, CardContent } from '@/components/ui/card';

interface Post {
  id: number;
  title: string;
}

export default function Posts() {
  const [posts, setPosts] = useState<Post[]>([]);
  const [loading, setLoading] = useState<boolean>(true);

  useEffect(() => {
    fetch('https://api.example.com/posts')
      .then((res) => res.json())
      .then((data: Post[]) => setPosts(data))
      .finally(() => setLoading(false));
  }, []);

  if (loading) return <div>Loading...</div>;

  return (
    <div className="space-y-4">
      {posts.map((post) => (
        <Card key={post.id}>
          <CardContent className="p-4">
            <h2 className="font-semibold">{post.title}</h2>
          </CardContent>
        </Card>
      ))}
    </div>
  );
}

Using Tauri APIs

'use client';

import { invoke } from '@tauri-apps/api/core';
import { Button } from '@/components/ui/button';
import { useState } from 'react';

export default function TauriDemo() {
  const [result, setResult] = useState<string>('');

  const handleClick = async (): Promise<void> => {
    try {
      const message = await invoke<string>('greet', { name: 'World' });
      setResult(message);
    } catch (error) {
      console.error('Tauri command failed:', error);
    }
  };

  return (
    <div>
      <Button onClick={handleClick}>Call Tauri</Button>
      {result && <p>{result}</p>}
    </div>
  );
}

Dark Mode

Dark mode is configured but NOT automatically implemented. To add:

Option 1: Simple useState Toggle

'use client';

import { useEffect, useState } from 'react';
import { Button } from '@/components/ui/button';
import { Moon, Sun } from 'lucide-react';

export function ThemeToggle() {
  const [dark, setDark] = useState<boolean>(false);

  useEffect(() => {
    document.documentElement.classList.toggle('dark', dark);
  }, [dark]);

  return (
    <Button variant="ghost" size="icon" onClick={() => setDark(!dark)}>
      {dark ? <Sun className="h-4 w-4" /> : <Moon className="h-4 w-4" />}
    </Button>
  );
}
npm install next-themes

See TauriNextShadcn.md for full implementation.

Troubleshooting Guide

Tailwind Styles Not Applying

Symptoms:

  • Components render as unstyled HTML
  • No visual styling from Tailwind classes

Common Causes & Solutions:

  1. Configs in wrong location

    # Check files exist in src-next/
    ls src-next/tailwind.config.cjs
    ls src-next/postcss.config.cjs
    
  2. Wrong file extension

    • Must be .cjs (CommonJS)
    • NOT .js or .mjs
  3. Missing Tailwind directives

    /* src-next/app/globals.css must have: */
    @tailwind base;
    @tailwind components;
    @tailwind utilities;
    
  4. Cache issues

    rm -rf src-next/.next
    npm run dev:next
    

Component Import Errors

Error: Cannot find module '@/components/ui/button'

Solutions:

  1. Verify file exists: src-next/components/ui/button.tsx
  2. Check tsconfig.json has path aliases
  3. Restart TypeScript server in VS Code
  4. Check import statement uses exact filename

Tailwind v4 Accidentally Installed

Symptoms:

  • Config using @import "tailwindcss" in CSS
  • Different config syntax errors

Solution:

npm uninstall tailwindcss
npm install -D tailwindcss@^3.4.18

shadcn CLI Errors

Error: "Couldn't find tsconfig.json"

Solution: Don't use the CLI. Install components manually (see section above).

Build Process

Development

# Web only (fast iteration)
npm run dev:next

# Desktop app
npm run dev

Production

# Build desktop app (includes Next.js build)
npm run build

# Or just Next.js static export
npm run build:next

Output: src-next/out/ contains:

  • index.html
  • _next/static/ (compiled CSS, JS)
  • All routes as HTML files

Best Practices for AI Assistants

  1. Always check config locations before making changes
  2. Use .cjs extension for Tailwind/PostCSS configs
  3. Install components manually - don't suggest using shadcn CLI
  4. Check Tailwind version - must be v3, never v4
  5. Use explicit TypeScript types - follow strict mode
  6. Add 'use client' to interactive components
  7. Use @/ path aliases for imports
  8. Use cn() utility for conditional classes
  9. Test in both web and desktop modes when possible

Summary Checklist for AI Assistants

When working on this codebase, remember:

  • Configs in src-next/, not root
  • Configs use .cjs extension
  • Tailwind CSS v3 (NOT v4)
  • shadcn CLI doesn't work - manual install only
  • Static export mode (CSR only, no SSR)
  • TypeScript strict mode enforced
  • Use @/ path aliases
  • Use cn() for class merging
  • Dark mode requires manual implementation

When uncertain: Refer to SETUP.md or TauriNextShadcn.md for detailed guidance.

Score

Total Score

55/100

Based on repository quality metrics

SKILL.md

SKILL.mdファイルが含まれている

+20
LICENSE

ライセンスが設定されている

0/10
説明文

100文字以上の説明がある

0/10
人気

GitHub Stars 100以上

0/15
最近の活動

3ヶ月以内に更新がある

0/10
フォーク

10回以上フォークされている

0/5
Issue管理

オープンIssueが50未満

+5
言語

プログラミング言語が設定されている

+5
タグ

1つ以上のタグが設定されている

+5

Reviews

💬

Reviews coming soon