一键导入
taurinext-shadcn
taurinext-shadcn template reference doc. Use this when to find in-depth information about this template.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
taurinext-shadcn template reference doc. Use this when to find in-depth information about this template.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
| name | taurinext-shadcn |
| description | taurinext-shadcn template reference doc. Use this when to find in-depth information about this template. |
This document provides context for AI assistants (like Claude) working on this taurinext-shadcn template codebase.
This TauriNext-shadcn is a template repository for building cross-platform applications that run as:
Both targets share the same Next.js codebase with shadcn/ui components.
class-variance-authority - Component variantsclsx + tailwind-merge - Class name mergingtailwindcss-animate - AnimationsMUST 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 devsrc-next/ directoryMUST 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:
package.json has "type": "module".js files use ES module syntax by default.cjs explicitly forces CommonJS, overriding package.json settingMUST BE v3.4.x, NEVER v4.x
{
"devDependencies": {
"tailwindcss": "^3.4.18", // ✅ CORRECT
"tailwindcss-animate": "^1.0.7" // ✅ Required
}
}
Why:
@import in CSS)If v4 accidentally installed:
npm uninstall tailwindcss
npm install -D tailwindcss@^3.4.18
THE SHADCN CLI DOES NOT WORK WITH THIS PROJECT
Reason:
src-next/ subdirectorytsconfig.json at root levelSolution: Manual Component Installation
@radix-ui packages)npm install @radix-ui/react-dialogsrc-next/components/ui/[name].tsx@/ aliasesExample: 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
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"
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:
asChild prop (uses Radix Slot)cva (class-variance-authority) for variantsUsage:
import { Button } from "@/components/ui/button"
<Button variant="outline" size="lg">Click Me</Button>
<Button asChild><Link href="/page">Link Button</Link></Button>
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>
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>
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:
0 0% 100%hsl() wrapper: hsl(var(--background))bg-primary/50File: 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)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:
clsx (conditional classes) + twMerge (deduplication)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
)
Configuration:
// src-next/next.config.ts
output: 'export',
images: { unoptimized: true },
Allowed:
'use client')NOT Allowed:
// src-next/tsconfig.json
{
"compilerOptions": {
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"baseUrl": ".",
"paths": {
"@/*": ["./*"]
}
}
}
Requirements:
any): void, : Promise<void>)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
// 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>
);
}
'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>
);
}
'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 is configured but NOT automatically implemented. To add:
'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.
Symptoms:
Common Causes & Solutions:
Configs in wrong location
# Check files exist in src-next/
ls src-next/tailwind.config.cjs
ls src-next/postcss.config.cjs
Wrong file extension
.cjs (CommonJS).js or .mjsMissing Tailwind directives
/* src-next/app/globals.css must have: */
@tailwind base;
@tailwind components;
@tailwind utilities;
Cache issues
rm -rf src-next/.next
npm run dev:next
Error: Cannot find module '@/components/ui/button'
Solutions:
src-next/components/ui/button.tsxSymptoms:
@import "tailwindcss" in CSSSolution:
npm uninstall tailwindcss
npm install -D tailwindcss@^3.4.18
Error: "Couldn't find tsconfig.json"
Solution: Don't use the CLI. Install components manually (see section above).
# Web only (fast iteration)
npm run dev:next
# Desktop app
npm run dev
# 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).cjs extension for Tailwind/PostCSS configs'use client' to interactive components@/ path aliases for importscn() utility for conditional classesWhen working on this codebase, remember:
src-next/, not root.cjs extension@/ path aliasescn() for class mergingWhen uncertain: Refer to SETUP.md or TauriNextShadcn.md for detailed guidance.