Skip to content

fix: Fix ShadCN UI styles in Nx monorepo with Tailwind v4 #7838

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 62 additions & 0 deletions apps/v4/app/test-ui/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { Button } from "@workspace/ui"

export default function TestUIPage() {
return (
<div className="bg-background min-h-screen p-8">
<div className="mx-auto max-w-4xl space-y-8">
<div className="space-y-4">
<h1 className="text-foreground text-4xl font-bold">
ShadCN UI with Tailwind v4 Test
</h1>
<p className="text-muted-foreground">
This page demonstrates that ShadCN UI components are working
properly with Tailwind CSS v4 in the Nx monorepo setup.
</p>
</div>

<div className="space-y-6">
<section className="space-y-4">
<h2 className="text-foreground text-2xl font-semibold">
Button Variants
</h2>
<div className="flex flex-wrap gap-4">
<Button>Default Button</Button>
<Button variant="secondary">Secondary</Button>
<Button variant="destructive">Destructive</Button>
<Button variant="outline">Outline</Button>
<Button variant="ghost">Ghost</Button>
<Button variant="link">Link</Button>
</div>
</section>

<section className="space-y-4">
<h2 className="text-foreground text-2xl font-semibold">
Button Sizes
</h2>
<div className="flex flex-wrap items-center gap-4">
<Button size="sm">Small</Button>
<Button size="default">Default</Button>
<Button size="lg">Large</Button>
<Button size="icon">🔍</Button>
</div>
</section>

<section className="space-y-4">
<h2 className="text-foreground text-2xl font-semibold">
Disabled States
</h2>
<div className="flex flex-wrap gap-4">
<Button disabled>Disabled Default</Button>
<Button disabled variant="outline">
Disabled Outline
</Button>
<Button disabled variant="secondary">
Disabled Secondary
</Button>
</div>
</section>
</div>
</div>
</div>
)
}
3 changes: 2 additions & 1 deletion apps/v4/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"type": "module",
"scripts": {
"dev": "next dev --turbopack --port 4000",
"build": "pnpm --filter=shadcn build && next build",
"build": "pnpm --filter=shadcn build && pnpm --filter=@workspace/ui build && next build",
"start": "next start --port 4000",
"lint": "next lint",
"lint:fix": "next lint --fix",
Expand Down Expand Up @@ -57,6 +57,7 @@
"@tailwindcss/postcss": "^4.0.1",
"@tanstack/react-table": "^8.9.1",
"@vercel/analytics": "^1.4.1",
"@workspace/ui": "workspace:*",
"change-case": "^5.4.4",
"chrono-node": "^2.8.2",
"class-variance-authority": "^0.7.1",
Expand Down
5 changes: 4 additions & 1 deletion apps/v4/postcss.config.mjs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
const config = {
plugins: {
"@tailwindcss/postcss": {},
"@tailwindcss/postcss": {
// Ensure proper processing for monorepo structure
config: "./tailwind.config.cjs",
},
},
}
export default config
1 change: 1 addition & 0 deletions apps/v4/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"baseUrl": ".",
"paths": {
"@/*": ["./*"],
"@workspace/ui": ["../../packages/ui/src"],
"react": ["./node_modules/@types/react"]
}
},
Expand Down
61 changes: 61 additions & 0 deletions packages/ui/dist/index.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions packages/ui/dist/index.js.map

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

42 changes: 42 additions & 0 deletions packages/ui/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
{
"name": "@workspace/ui",
"version": "0.0.0",
"private": true,
"type": "module",
"main": "./dist/index.js",
"module": "./dist/index.js",
"types": "./dist/index.d.ts",
"exports": {
".": {
"import": "./dist/index.js",
"types": "./dist/index.d.ts"
},
"./styles.css": "./dist/styles.css"
},
"files": [
"dist/**"
],
"scripts": {
"build": "tsup",
"dev": "tsup --watch",
"clean": "rm -rf dist",
"typecheck": "tsc --noEmit"
},
"dependencies": {
"@radix-ui/react-slot": "^1.1.1",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"tailwind-merge": "^3.0.1"
},
"devDependencies": {
"@types/react": "^19.1.2",
"@types/react-dom": "^19.1.2",
"react": "^19.1.0",
"tsup": "^8.0.0",
"typescript": "^5.0.0"
},
"peerDependencies": {
"react": "^19.1.0",
"react-dom": "^19.1.0"
}
}
77 changes: 77 additions & 0 deletions packages/ui/src/components/button.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import * as React from "react"
import { Slot } from "@radix-ui/react-slot"
import { cva, type VariantProps } from "class-variance-authority"

import { cn } from "../utils"

const buttonVariants = cv(
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
{
variants: {
variant: {
default:
"bg-primary text-primary-foreground shadow-xs hover:bg-primary/90",
destructive:
"bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
outline:
"border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50",
secondary:
"bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80",
ghost:
"hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
link: "text-primary underline-offset-4 hover:underline",
},
size: {
default: "h-9 px-4 py-2 has-[>svg]:px-3",
sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",
lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
icon: "size-9",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
}
)

export interface ButtonProps
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
VariantProps<typeof buttonVariants> {
asChild?: boolean
}

const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
({ className, variant, size, asChild = false, ...props }, ref) => {
const Comp = asChild ? Slot : "button"
return (
<Comp
className={cn(buttonVariants({ variant, size, className }))}
ref={ref}
{...props}
/>
)
}
)
Button.displayName = "Button"

export { Button, buttonVariants }
function cv(
arg0: string,
arg1: {
variants: {
variant: {
default: string
destructive: string
outline: string
secondary: string
ghost: string
link: string
}
size: { default: string; sm: string; lg: string; icon: string }
}
defaultVariants: { variant: string; size: string }
}
) {
throw new Error("Function not implemented.")
}
2 changes: 2 additions & 0 deletions packages/ui/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { Button, buttonVariants, type ButtonProps } from "./components/button"
export { cn } from "./utils"
6 changes: 6 additions & 0 deletions packages/ui/src/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { clsx, type ClassValue } from "clsx"
import { twMerge } from "tailwind-merge"

export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
}
14 changes: 14 additions & 0 deletions packages/ui/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "./dist",
"rootDir": "./src",
"baseUrl": ".",
"jsx": "react-jsx",
"paths": {
"@/*": ["./src/*"]
}
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}
16 changes: 16 additions & 0 deletions packages/ui/tsup.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { defineConfig } from "tsup"

export default defineConfig({
entry: ["src/index.ts"],
format: ["esm"],
dts: true,
splitting: false,
sourcemap: true,
clean: true,
external: ["react", "react-dom"],
esbuildOptions(options) {
options.banner = {
js: '"use client";',
}
},
})
Loading