Compare commits

...

40 Commits

Author SHA1 Message Date
shadcn
0826d58d3a chore: ignore errors 2025-05-29 13:36:19 +04:00
shadcn
995b6fd1a8 fix: format 2025-05-29 12:59:39 +04:00
shadcn
410998525b fix: to v3 2025-05-29 12:47:58 +04:00
shadcn
0f18c2775a Merge branch 'main' into v2 2025-05-29 12:45:59 +04:00
Thibault Le Ouay
b84c990e42 fix registry item schema (#7486) 2025-05-28 16:52:24 +04:00
Joshua
2773f9e2e2 fix(tailwind-prefix): resolve prefixing issue for Tailwind CSS v4 compatibility (including tests) (#6885)
* WIP

* fix(tailwind-prefix): resolve prefixing issue for Tailwind CSS v4 compatibility
1. Fixed incorrect prefix application causing issues with Tailwind CSS v4.
2. Optimized implementation using map() for better performance and readability.

* fix(tailwind-prefix): fix test transform-tw-prefixt

* fix(tailwind-prefix): fix test apply-prefix

* fix(tailwind-prefix): add backwards compatibility for applyPrefix

* fix(tailwind-prefix): added changeset

---------

Co-authored-by: Al-Amin Islam Nerob <nerobit.786@gmail.com>
Co-authored-by: shadcn <m@shadcn.com>
2025-05-19 11:31:12 +04:00
shadcn
c41c6ece86 fix: move tw-animate-css to devDependencies (#7251) 2025-04-23 16:45:23 +04:00
github-actions[bot]
11267f2fed chore(release): version packages (#7228)
* chore(release): version packages

* deps: update pnpm lock

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: shadcn <m@shadcn.com>
2025-04-23 16:28:59 +04:00
Richard Szalay
9ad24d6a16 chore(shadcn): add update-dependencies tests (#7067) 2025-04-22 15:20:00 +04:00
shadcn
e8468793fc chore: temporarily move tw-animate-css to dependencies 2025-04-22 05:07:56 +04:00
迷渡
6f702f5fbf fix: Add npm: specifier when install dependencies with Deno (#6899)
* fix: Add `npm:` specifier when install dependencies with Deno

* chore: changeset

---------

Co-authored-by: shadcn <m@shadcn.com>
2025-04-21 23:36:36 +04:00
shadcn
d0306774fe feat(shadcn): resolve imports from anywhere (#7220)
* feat(shadcn): resolve imports from anywhere

* fix: type errors

* fix: add debug

* feat: handle root paths

* fix: src prefix

* fix: tests

* chore: changeset
2025-04-19 13:31:04 +04:00
Neeraj Dalal
f1e5cc4666 🔥 feat(fix): mark "tw-animate-css" as devDep (#6985)
* chore: tweaks to build-registry.mts ~ 1 file 2+ 5-

apps/v4/scripts/build-registry.mts

* chore: tweaks to index.json ~ 1 file 4+ 9-

apps/www/public/r/styles/new-york-v4/index.json

* chore: tweaks to registry.json ~ 1 file 221+ 850-

apps/v4/registry.json

* chore: tweaks to index.json ~ 1 file 47+ 144-

apps/www/public/r/index.json

* chore: tweaks to registry.json ~ 1 file 852+ 221-

apps/v4/registry.json

* chore: tweaks to build-registry.mts ~ 1 file 7+ 2-

apps/v4/scripts/build-registry.mts

* chore: tweaks to build-registry.mts ~ 1 file 2+ 2-

apps/v4/scripts/build-registry.mts

* chore: tweaks to index.json ~ 1 file 11+ 4-

apps/www/public/r/styles/new-york-v4/index.json

* chore: tweaks to index.json ~ 1 file 1+ 1-

apps/www/public/r/index.json

* chore: tweaks to index.json ~ 1 file 143+ 46-

apps/www/public/r/index.json

* chore: tweaks to update-dependencies.ts ~ 1 file 20+

packages/shadcn/src/utils/updaters/update-dependencies.ts

* chore: tweaks to update-dependencies.ts ~ 1 file 1-

packages/shadcn/src/utils/updaters/update-dependencies.ts

* chore: tweaks to update-dependencies.ts ~ 1 file 19-

packages/shadcn/src/utils/updaters/update-dependencies.ts

* chore: tweaks to add-components.ts update-dependencies.ts ~ 2 files 21+ 2-

packages/shadcn/src/utils/add-components.ts
packages/shadcn/src/utils/updaters/update-dependencies.ts

* chore: tweaks to update-dependencies.ts ~ 1 file 29+ 28-

packages/shadcn/src/utils/updaters/update-dependencies.ts

* refactor: remove redundant code

tw-animate-css already has accordion animations

* chore: tweaks > migrate-icons.ts

packages/shadcn/src/migrations/migrate-icons.ts

* fix: formatting > add-components.ts update-dependencies.ts

packages/shadcn/src/utils/add-components.ts
packages/shadcn/src/utils/updaters/update-dependencies.ts

* chore: add changeset

---------

Co-authored-by: shadcn <m@shadcn.com>
2025-04-19 13:05:52 +04:00
Titouan V
e3ca257f6e chore(www): replace shadcn-ui deprecated mentions to shadcn (#7207) 2025-04-18 10:44:46 +04:00
github-actions[bot]
d1a36d3e17 chore(release): version packages (#7198)
* chore(release): version packages

* fix

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: shadcn <m@shadcn.com>
2025-04-15 16:30:10 +04:00
shadcn
617483fe9c fix(shadcn): do not throw for empty dir (#7196)
* fix(shadcn): do not throw for empty dir

* chore: changeset
2025-04-15 15:01:20 +04:00
shadcn
6d2728db2e deps: update 2025-04-07 15:19:36 +04:00
github-actions[bot]
625be136f4 chore(release): version packages (#6505)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2025-04-07 15:16:43 +04:00
shadcn
1bd209a4db feat: add oklch colors to themes (#7090)
* wip

* feat: add oklch theme

* fix: keys
2025-03-31 10:33:48 +04:00
shadcn
812e2300f1 fix(shadcn): only run preflight check if url 2025-03-30 05:43:29 +04:00
shadcn
b52fa4559c docs(www): update registry examples 2025-03-28 21:05:55 +04:00
shadcn
2fade2326a feat(shadcn): add css props to schema (#7072)
* feat(shadcn): add css props to schema

* fix: types
2025-03-28 17:48:59 +04:00
shadcn
a6034127f9 fix: lint 2025-03-28 11:40:06 +04:00
shadcn
eaf8156fc0 chore: add banner 2025-03-28 11:31:48 +04:00
dependabot[bot]
754a66061d chore(deps): bump sharp from 0.31.3 to 0.32.6 (#7053)
Bumps [sharp](https://github.com/lovell/sharp) from 0.31.3 to 0.32.6.
- [Release notes](https://github.com/lovell/sharp/releases)
- [Changelog](https://github.com/lovell/sharp/blob/v0.32.6/docs/changelog.md)
- [Commits](https://github.com/lovell/sharp/compare/v0.31.3...v0.32.6)

---
updated-dependencies:
- dependency-name: sharp
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-27 22:24:16 +04:00
dependabot[bot]
cc53c2243e chore(deps): bump next from 15.2.0-canary.33 to 15.2.3 in /apps/v4 (#7049)
Bumps [next](https://github.com/vercel/next.js) from 15.2.0-canary.33 to 15.2.3.
- [Release notes](https://github.com/vercel/next.js/releases)
- [Changelog](https://github.com/vercel/next.js/blob/canary/release.js)
- [Commits](https://github.com/vercel/next.js/compare/v15.2.0-canary.33...v15.2.3)

---
updated-dependencies:
- dependency-name: next
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: shadcn <m@shadcn.com>
2025-03-27 22:24:00 +04:00
nicognaw
99fbf4cb77 fix: code block command overflow (#7031) 2025-03-27 18:15:14 +04:00
dependabot[bot]
a82db8395d chore(deps): bump vite from 5.4.14 to 5.4.15 (#7051)
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 5.4.14 to 5.4.15.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/v5.4.15/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v5.4.15/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-27 18:12:46 +04:00
dependabot[bot]
79eed50e5b chore(deps): bump next from 15.2.0-canary.33 to 15.2.3 (#7052)
Bumps [next](https://github.com/vercel/next.js) from 15.2.0-canary.33 to 15.2.3.
- [Release notes](https://github.com/vercel/next.js/releases)
- [Changelog](https://github.com/vercel/next.js/blob/canary/release.js)
- [Commits](https://github.com/vercel/next.js/compare/v15.2.0-canary.33...v15.2.3)

---
updated-dependencies:
- dependency-name: next
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-27 18:12:28 +04:00
dependabot[bot]
799a7f8aa7 chore(deps-dev): bump remark-gfm from 4.0.0 to 4.0.1 (#7048)
Bumps [remark-gfm](https://github.com/remarkjs/remark-gfm) from 4.0.0 to 4.0.1.
- [Release notes](https://github.com/remarkjs/remark-gfm/releases)
- [Commits](https://github.com/remarkjs/remark-gfm/compare/4.0.0...4.0.1)

---
updated-dependencies:
- dependency-name: remark-gfm
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-27 17:51:55 +04:00
dependabot[bot]
79a6f54d4e chore(deps): bump @radix-ui/react-scroll-area from 1.2.2 to 1.2.3 (#7045)
Bumps [@radix-ui/react-scroll-area](https://github.com/radix-ui/primitives) from 1.2.2 to 1.2.3.
- [Changelog](https://github.com/radix-ui/primitives/blob/main/release-process.md)
- [Commits](https://github.com/radix-ui/primitives/commits)

---
updated-dependencies:
- dependency-name: "@radix-ui/react-scroll-area"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-27 17:51:19 +04:00
dependabot[bot]
26edb8275e chore(deps-dev): bump esbuild from 0.17.19 to 0.25.0 (#7043)
Bumps [esbuild](https://github.com/evanw/esbuild) from 0.17.19 to 0.25.0.
- [Release notes](https://github.com/evanw/esbuild/releases)
- [Changelog](https://github.com/evanw/esbuild/blob/main/CHANGELOG-2023.md)
- [Commits](https://github.com/evanw/esbuild/compare/v0.17.19...v0.25.0)

---
updated-dependencies:
- dependency-name: esbuild
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-27 17:50:57 +04:00
dependabot[bot]
e15e31e3cb chore(deps): bump vitest from 2.1.8 to 2.1.9 (#7042)
Bumps [vitest](https://github.com/vitest-dev/vitest/tree/HEAD/packages/vitest) from 2.1.8 to 2.1.9.
- [Release notes](https://github.com/vitest-dev/vitest/releases)
- [Commits](https://github.com/vitest-dev/vitest/commits/v2.1.9/packages/vitest)

---
updated-dependencies:
- dependency-name: vitest
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-27 17:50:41 +04:00
dependabot[bot]
d4247d52da chore(deps): bump next from 15.2.0 to 15.2.3 in /templates/monorepo-next (#7041)
Bumps [next](https://github.com/vercel/next.js) from 15.2.0 to 15.2.3.
- [Release notes](https://github.com/vercel/next.js/releases)
- [Changelog](https://github.com/vercel/next.js/blob/canary/release.js)
- [Commits](https://github.com/vercel/next.js/compare/v15.2.0...v15.2.3)

---
updated-dependencies:
- dependency-name: next
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-27 17:50:22 +04:00
shadcn
9d908ae6ca docs: add security.md 2025-03-27 16:59:19 +04:00
shadcn
883ad8cbc4 chore: create dependabot config 2025-03-27 16:50:16 +04:00
shadcn
074eed5605 feat(shadcn): extend styles (#7033)
* feat(shadcn): extend styles

* fix(shadcn): typecheck
2025-03-26 17:11:08 +04:00
shadcn
ca7fbc3b64 fix(v4): minor focus styles (#7030) 2025-03-26 13:03:23 +04:00
shadcn
b3b2fe2755 feat(shadcn): registry updates (#7016)
* feat(shadcn): registry updates

* tests: fix snapshots

* feat(shadcn): add new theme prop

* fix: handle theme for index

* tests(shadcn): fix

* docs(www): update registry item docs

* chore: add changeset

* docs: update theming docs
2025-03-26 13:03:10 +04:00
shadcn
1fcb318c56 feat: use latest cmdk version 2025-03-24 09:45:59 +04:00
99 changed files with 4431 additions and 1395 deletions

View File

@@ -1,5 +0,0 @@
---
"shadcn": minor
---
add support for TanStack Start

View File

@@ -1,5 +0,0 @@
---
"shadcn": patch
---
support for version detection in monorepo

View File

@@ -1,5 +0,0 @@
---
"shadcn": patch
---
upgrade @antfu/ni

View File

@@ -1,5 +0,0 @@
---
"shadcn": patch
---
allow silent mode with npm

View File

@@ -1,5 +0,0 @@
---
"shadcn": patch
---
do not add ring for v3

View File

@@ -1,5 +0,0 @@
---
"shadcn": minor
---
add theme vars support

View File

@@ -1,5 +0,0 @@
---
"shadcn": minor
---
add tailwind version detection

View File

@@ -1,5 +0,0 @@
---
"shadcn": minor
---
add --base-color flag

View File

@@ -1,5 +0,0 @@
---
"shadcn": minor
---
add support for tailwind v4

View File

@@ -1,5 +0,0 @@
---
"shadcn": minor
---
default to css vars. add --no-css-variables

View File

@@ -1,5 +0,0 @@
---
"shadcn": patch
---
cache registry calls

View File

@@ -0,0 +1,5 @@
---
"shadcn": patch
---
resolved prefixing issue for Tailwind CSS v4 compatibility

View File

@@ -1,5 +0,0 @@
---
"shadcn": minor
---
replace tailwindcss-animate with tw-animate-css

View File

@@ -1,5 +0,0 @@
---
"shadcn": patch
---
add --template flag

View File

@@ -1,5 +0,0 @@
---
"shadcn": minor
---
default for new-york for v4

View File

@@ -1,5 +0,0 @@
---
"shadcn": minor
---
fix handling of sidebar colors

View File

@@ -1,5 +0,0 @@
---
"shadcn": patch
---
do not overwrite user defined vars

View File

@@ -1,5 +0,0 @@
---
"shadcn": patch
---
fix cn import bug in monorepo

View File

@@ -1,5 +0,0 @@
---
"shadcn": patch
---
filter out deprecated from --all

View File

@@ -1,5 +0,0 @@
---
"shadcn": minor
---
add oklch base color

View File

@@ -1,5 +0,0 @@
---
"shadcn": minor
---
hotswap style for v4

View File

@@ -1,5 +0,0 @@
---
"shadcn": patch
---
check for empty css vars

View File

@@ -1,5 +0,0 @@
---
"shadcn": patch
---
only show deprecated for new projects

View File

@@ -1,5 +0,0 @@
---
"shadcn": minor
---
add warning for deprecated components

View File

@@ -1,5 +0,0 @@
---
"shadcn": patch
---
fix tanstack check

View File

@@ -1,5 +0,0 @@
---
"shadcn": minor
---
add support for route install for react-router and laravel

6
.github/dependabot.yml vendored Normal file
View File

@@ -0,0 +1,6 @@
version: 2
updates:
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "weekly"

9
SECURITY.md Normal file
View File

@@ -0,0 +1,9 @@
# Security Policy
If you believe you have found a security vulnerability, we encourage you to let us know right away.
We will investigate all legitimate reports and do our best to quickly fix the problem.
Our preference is that you make use of GitHub's private vulnerability reporting feature to disclose potential security vulnerabilities in our Open Source Software.
To do this, please visit the security tab of the repository and click the "Report a vulnerability" button.

View File

@@ -2,11 +2,11 @@
import * as React from "react"
import {
closestCenter,
DndContext,
KeyboardSensor,
MouseSensor,
TouchSensor,
closestCenter,
useSensor,
useSensors,
type DragEndEvent,
@@ -14,8 +14,8 @@ import {
} from "@dnd-kit/core"
import { restrictToVerticalAxis } from "@dnd-kit/modifiers"
import {
SortableContext,
arrayMove,
SortableContext,
useSortable,
verticalListSortingStrategy,
} from "@dnd-kit/sortable"
@@ -37,9 +37,6 @@ import {
import {
ColumnDef,
ColumnFiltersState,
Row,
SortingState,
VisibilityState,
flexRender,
getCoreRowModel,
getFacetedRowModel,
@@ -47,7 +44,10 @@ import {
getFilteredRowModel,
getPaginationRowModel,
getSortedRowModel,
Row,
SortingState,
useReactTable,
VisibilityState,
} from "@tanstack/react-table"
import { Area, AreaChart, CartesianGrid, XAxis } from "recharts"
import { toast } from "sonner"

View File

@@ -8,6 +8,7 @@ import { Toaster } from "@/registry/new-york-v4/ui/sonner"
import { siteConfig } from "@/www/config/site"
import "./globals.css"
import { cn } from "@/lib/utils"
import { ActiveThemeProvider } from "@/components/active-theme"

View File

@@ -1,8 +1,8 @@
"use client"
import {
ReactNode,
createContext,
ReactNode,
useContext,
useEffect,
useState,

View File

@@ -39,7 +39,7 @@
"@radix-ui/react-portal": "^1.1.3",
"@radix-ui/react-progress": "^1.1.1",
"@radix-ui/react-radio-group": "^1.2.2",
"@radix-ui/react-scroll-area": "^1.2.2",
"@radix-ui/react-scroll-area": "^1.2.3",
"@radix-ui/react-select": "^2.1.5",
"@radix-ui/react-separator": "^1.1.1",
"@radix-ui/react-slider": "^1.2.2",
@@ -63,7 +63,7 @@
"geist": "^1.2.2",
"input-otp": "^1.4.2",
"lucide-react": "0.474.0",
"next": "15.2.0-canary.33",
"next": "15.2.3",
"next-themes": "^0.4.3",
"postcss": "^8.5.1",
"react": "^19.0.0",
@@ -73,7 +73,7 @@
"react-resizable-panels": "^2.1.7",
"recharts": "2.15.1",
"rimraf": "^6.0.1",
"shadcn": "2.3.0",
"shadcn": "2.5.0",
"sonner": "^2.0.0",
"tailwind-merge": "^3.0.1",
"tailwindcss": "^4.0.7",

View File

@@ -6,10 +6,12 @@
"name": "index",
"type": "registry:style",
"dependencies": [
"tw-animate-css",
"class-variance-authority",
"lucide-react"
],
"devDependencies": [
"tw-animate-css"
],
"registryDependencies": [
"utils"
],
@@ -27,37 +29,7 @@
"path": "registry/new-york-v4/ui/accordion.tsx",
"type": "registry:ui"
}
],
"tailwind": {
"config": {
"theme": {
"extend": {
"keyframes": {
"accordion-down": {
"from": {
"height": "0"
},
"to": {
"height": "var(--radix-accordion-content-height)"
}
},
"accordion-up": {
"from": {
"height": "var(--radix-accordion-content-height)"
},
"to": {
"height": "0"
}
}
},
"animation": {
"accordion-down": "accordion-down 0.2s ease-out",
"accordion-up": "accordion-up 0.2s ease-out"
}
}
}
}
}
]
},
{
"name": "alert",
@@ -240,7 +212,7 @@
"name": "command",
"type": "registry:ui",
"dependencies": [
"cmdk@1.0.0"
"cmdk"
],
"registryDependencies": [
"dialog"

View File

@@ -2,11 +2,11 @@
import * as React from "react"
import {
closestCenter,
DndContext,
KeyboardSensor,
MouseSensor,
TouchSensor,
closestCenter,
useSensor,
useSensors,
type DragEndEvent,
@@ -14,8 +14,8 @@ import {
} from "@dnd-kit/core"
import { restrictToVerticalAxis } from "@dnd-kit/modifiers"
import {
SortableContext,
arrayMove,
SortableContext,
useSortable,
verticalListSortingStrategy,
} from "@dnd-kit/sortable"
@@ -37,9 +37,6 @@ import {
import {
ColumnDef,
ColumnFiltersState,
Row,
SortingState,
VisibilityState,
flexRender,
getCoreRowModel,
getFacetedRowModel,
@@ -47,7 +44,10 @@ import {
getFilteredRowModel,
getPaginationRowModel,
getSortedRowModel,
Row,
SortingState,
useReactTable,
VisibilityState,
} from "@tanstack/react-table"
import { Area, AreaChart, CartesianGrid, XAxis } from "recharts"
import { toast } from "sonner"

View File

@@ -14,7 +14,7 @@ const badgeVariants = cva(
secondary:
"border-transparent bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90",
destructive:
"border-transparent bg-destructive text-white [a&]:hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/70",
"border-transparent bg-destructive text-white [a&]:hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
outline:
"text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground",
},

View File

@@ -59,7 +59,7 @@ function NavigationMenuItem({
}
const navigationMenuTriggerStyle = cva(
"group inline-flex h-9 w-max items-center justify-center rounded-md bg-background px-4 py-2 text-sm font-medium hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground disabled:pointer-events-none disabled:opacity-50 data-[state=open]:hover:bg-accent data-[state=open]:text-accent-foreground data-[state=open]:focus:bg-accent data-[state=open]:bg-accent/50 ring-ring/10 dark:ring-ring/20 dark:outline-ring/40 outline-ring/50 transition-[color,box-shadow] focus-visible:ring-4 focus-visible:outline-1"
"group inline-flex h-9 w-max items-center justify-center rounded-md bg-background px-4 py-2 text-sm font-medium hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground disabled:pointer-events-none disabled:opacity-50 data-[state=open]:hover:bg-accent data-[state=open]:text-accent-foreground data-[state=open]:focus:bg-accent data-[state=open]:bg-accent/50 focus-visible:ring-ring/50 outline-none transition-[color,box-shadow] focus-visible:ring-[3px] focus-visible:outline-1"
)
function NavigationMenuTrigger({
@@ -129,7 +129,7 @@ function NavigationMenuLink({
<NavigationMenuPrimitive.Link
data-slot="navigation-menu-link"
className={cn(
"data-[active=true]:focus:bg-accent data-[active=true]:hover:bg-accent data-[active=true]:bg-accent/50 data-[active=true]:text-accent-foreground hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground ring-ring/10 dark:ring-ring/20 dark:outline-ring/40 outline-ring/50 [&_svg:not([class*='text-'])]:text-muted-foreground flex flex-col gap-1 rounded-sm p-2 text-sm transition-[color,box-shadow] focus-visible:ring-4 focus-visible:outline-1 [&_svg:not([class*='size-'])]:size-4",
"data-[active=true]:focus:bg-accent data-[active=true]:hover:bg-accent data-[active=true]:bg-accent/50 data-[active=true]:text-accent-foreground hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground focus-visible:ring-ring/50 [&_svg:not([class*='text-'])]:text-muted-foreground flex flex-col gap-1 rounded-sm p-2 text-sm transition-all outline-none focus-visible:ring-[3px] focus-visible:outline-1 [&_svg:not([class*='size-'])]:size-4",
className
)}
{...props}

View File

@@ -39,7 +39,7 @@ function ResizableHandle({
<ResizablePrimitive.PanelResizeHandle
data-slot="resizable-handle"
className={cn(
"bg-border focus-visible:ring-ring relative flex w-px items-center justify-center after:absolute after:inset-y-0 after:left-1/2 after:w-1 after:-translate-x-1/2 focus-visible:ring-1 focus-visible:ring-offset-1 focus-visible:outline-hidden data-[panel-group-direction=vertical]:h-px data-[panel-group-direction=vertical]:w-full data-[panel-group-direction=vertical]:after:left-0 data-[panel-group-direction=vertical]:after:h-1 data-[panel-group-direction=vertical]:after:w-full data-[panel-group-direction=vertical]:after:-translate-y-1/2 data-[panel-group-direction=vertical]:after:translate-x-0 [&[data-panel-group-direction=vertical]>div]:rotate-90",
"bg-border focus-visible:ring-ring relative flex w-px items-center justify-center after:absolute after:inset-y-0 after:left-1/2 after:w-1 after:-translate-x-1/2 focus-visible:ring-1 focus-visible:ring-offset-1 focus-visible:outline-hidden data-[panel-group-direction=vertical]:h-px data-[panel-group-direction=vertical]:w-full data-[panel-group-direction=vertical]:after:left-0 data-[panel-group-direction=vertical]:after:h-1 data-[panel-group-direction=vertical]:after:w-full data-[panel-group-direction=vertical]:after:translate-x-0 data-[panel-group-direction=vertical]:after:-translate-y-1/2 [&[data-panel-group-direction=vertical]>div]:rotate-90",
className
)}
{...props}

View File

@@ -18,7 +18,7 @@ function ScrollArea({
>
<ScrollAreaPrimitive.Viewport
data-slot="scroll-area-viewport"
className="ring-ring/10 dark:ring-ring/20 dark:outline-ring/40 outline-ring/50 size-full rounded-[inherit] transition-[color,box-shadow] focus-visible:ring-4 focus-visible:outline-1"
className="focus-visible:ring-ring/50 size-full rounded-[inherit] transition-[color,box-shadow] outline-none focus-visible:ring-[3px] focus-visible:outline-1"
>
{children}
</ScrollAreaPrimitive.Viewport>

View File

@@ -2,7 +2,7 @@
import * as React from "react"
import { Slot } from "@radix-ui/react-slot"
import { VariantProps, cva } from "class-variance-authority"
import { cva, VariantProps } from "class-variance-authority"
import { PanelLeftIcon } from "lucide-react"
import { useIsMobile } from "@/registry/new-york-v4/hooks/use-mobile"

View File

@@ -20,11 +20,8 @@ const registry = {
{
name: "index",
type: "registry:style",
dependencies: [
"tw-animate-css",
"class-variance-authority",
"lucide-react",
],
dependencies: ["class-variance-authority", "lucide-react"],
devDependencies: ["tw-animate-css"],
registryDependencies: ["utils"],
cssVars: {},
files: [],
@@ -78,6 +75,11 @@ const registry = {
if (item.name === "dashboard-01") {
item.dependencies?.push("@tabler/icons-react")
}
if (item.name === "accordion" && "tailwind" in item) {
delete item.tailwind
}
return item
})
),

View File

@@ -7,7 +7,8 @@ import {
PageHeaderDescription,
PageHeaderHeading,
} from "@/components/page-header"
import { ThemeCustomizer } from "@/components/theme-customizer"
import { Customizer } from "@/components/theme-customizer"
import { Button } from "@/registry/new-york/ui/button"
const title = "Add colors. Make it yours."
const description =
@@ -48,10 +49,17 @@ export default function ThemesLayout({
<Announcement />
<PageHeaderHeading>{title}</PageHeaderHeading>
<PageHeaderDescription>{description}</PageHeaderDescription>
<PageActions>
<ThemeCustomizer />
</PageActions>
<div className="mt-2 rounded-full bg-blue-600 px-3 py-1 text-xs text-white">
New Theme Editor coming soon
</div>
</PageHeader>
<div id="themes" className="border-grid scroll-mt-24 border-b">
<div className="container-wrapper">
<div className="container flex items-center py-4">
<Customizer />
</div>
</div>
</div>
<div className="container-wrapper">
<div className="container py-6">
<section id="themes" className="scroll-mt-20">

View File

@@ -1,5 +1,7 @@
import "@/styles/globals.css"
import { Metadata, Viewport } from "next"
import Link from "next/link"
import { ArrowRightIcon } from "lucide-react"
import { META_THEME_COLORS, siteConfig } from "@/config/site"
import { fontMono, fontSans } from "@/lib/fonts"
@@ -91,7 +93,7 @@ export default function RootLayout({ children }: RootLayoutProps) {
</head>
<body
className={cn(
"min-h-svh bg-background font-sans antialiased",
"bg-background min-h-svh font-sans antialiased",
fontSans.variable,
fontMono.variable
)}
@@ -104,7 +106,16 @@ export default function RootLayout({ children }: RootLayoutProps) {
enableColorScheme
>
<div vaul-drawer-wrapper="">
<div className="relative flex min-h-svh flex-col bg-background">
<div className="bg-background relative flex min-h-svh flex-col">
<div className="bg-muted text-muted-foreground sticky top-0 z-[100] flex h-10 items-center justify-center gap-2 px-4 text-sm">
You are viewing the v3 docs.{" "}
<Link
href="https://ui.shadcn.com"
className="text-primary flex items-center gap-1 underline"
>
Switch to latest <ArrowRightIcon className="size-3" />
</Link>
</div>
{children}
</div>
</div>

View File

@@ -79,20 +79,22 @@ export function CodeBlockCommand({
})}
</TabsList>
</div>
{Object.entries(tabs).map(([key, value]) => {
return (
<TabsContent key={key} value={key} className="mt-0">
<pre className="px-4 py-5">
<code
className="relative font-mono text-sm leading-none"
data-language="bash"
>
{value}
</code>
</pre>
</TabsContent>
)
})}
<div className="overflow-x-auto">
{Object.entries(tabs).map(([key, value]) => {
return (
<TabsContent key={key} value={key} className="mt-0">
<pre className="px-4 py-5">
<code
className="relative font-mono text-sm leading-none"
data-language="bash"
>
{value}
</code>
</pre>
</TabsContent>
)
})}
</div>
</Tabs>
<Button
size="icon"

View File

@@ -2,7 +2,7 @@
import * as React from "react"
import template from "lodash/template"
import { Check, Copy, Moon, Repeat, Sun } from "lucide-react"
import { Check, ClipboardIcon, Copy } from "lucide-react"
import { useTheme } from "next-themes"
import { cn } from "@/lib/utils"
@@ -21,6 +21,9 @@ import {
import {
Drawer,
DrawerContent,
DrawerDescription,
DrawerHeader,
DrawerTitle,
DrawerTrigger,
} from "@/registry/new-york/ui/drawer"
import { Label } from "@/registry/new-york/ui/label"
@@ -29,15 +32,28 @@ import {
PopoverContent,
PopoverTrigger,
} from "@/registry/new-york/ui/popover"
import { Separator } from "@/registry/new-york/ui/separator"
import { Skeleton } from "@/registry/new-york/ui/skeleton"
import {
Tooltip,
TooltipContent,
TooltipTrigger,
} from "@/registry/new-york/ui/tooltip"
import { BaseColor, baseColors } from "@/registry/registry-base-colors"
BaseColor,
baseColors,
baseColorsOKLCH,
} from "@/registry/registry-base-colors"
import "@/styles/mdx.css"
import { toast } from "sonner"
import {
Tabs,
TabsContent,
TabsList,
TabsTrigger,
} from "@/registry/new-york/ui/tabs"
interface BaseColorOKLCH {
light: Record<string, string>
dark: Record<string, string>
}
export function ThemeCustomizer() {
const [config, setConfig] = useConfig()
@@ -78,9 +94,9 @@ export function ThemeCustomizer() {
)
}
function Customizer() {
export function Customizer() {
const [mounted, setMounted] = React.useState(false)
const { setTheme: setMode, resolvedTheme: mode } = useTheme()
const { resolvedTheme: mode } = useTheme()
const [config, setConfig] = useConfig()
React.useEffect(() => {
@@ -88,39 +104,11 @@ function Customizer() {
}, [])
return (
<ThemeWrapper
defaultTheme="zinc"
className="flex flex-col space-y-4 md:space-y-6"
>
<div className="flex items-start pt-4 md:pt-0">
<div className="space-y-1 pr-2">
<div className="font-semibold leading-none tracking-tight">
Theme Customizer
</div>
<div className="text-xs text-muted-foreground">
Customize your components colors.
</div>
</div>
<Button
variant="ghost"
size="icon"
className="ml-auto rounded-[0.5rem]"
onClick={() => {
setConfig({
...config,
theme: "zinc",
radius: 0.5,
})
}}
>
<Repeat />
<span className="sr-only">Reset</span>
</Button>
</div>
<div className="flex flex-1 flex-col space-y-4 md:space-y-6">
<div className="space-y-1.5">
<Label className="text-xs">Color</Label>
<div className="grid grid-cols-3 gap-2">
<ThemeWrapper defaultTheme="zinc">
<div className="grid w-full flex-1 grid-cols-2 flex-wrap items-start gap-2 sm:flex sm:items-center md:gap-6">
<div className="flex flex-col gap-2">
<Label className="sr-only text-xs">Color</Label>
<div className="flex flex-wrap gap-1 md:gap-2">
{baseColors
.filter(
(theme) =>
@@ -131,7 +119,7 @@ function Customizer() {
return mounted ? (
<Button
variant={"outline"}
variant="outline"
size="sm"
key={theme.name}
onClick={() => {
@@ -141,8 +129,8 @@ function Customizer() {
})
}}
className={cn(
"justify-start",
isActive && "border-2 border-primary"
"w-[32px] rounded-lg lg:px-2.5 xl:w-[86px]",
isActive && "border-primary/50 ring-[2px] ring-primary/30"
)}
style={
{
@@ -154,22 +142,28 @@ function Customizer() {
>
<span
className={cn(
"mr-1 flex h-5 w-5 shrink-0 -translate-x-1 items-center justify-center rounded-full bg-[--theme-primary]"
"flex h-4 w-4 shrink-0 items-center justify-center rounded-full bg-[--theme-primary]"
)}
>
{isActive && <Check className="h-4 w-4 text-white" />}
{isActive && <Check className="!size-2.5 text-white" />}
</span>
<span className="hidden xl:block">
{theme.label === "Zinc" ? "Default" : theme.label}
</span>
{theme.label}
</Button>
) : (
<Skeleton className="h-8 w-full" key={theme.name} />
<Skeleton
className="h-8 w-[32px] xl:w-[86px]"
key={theme.name}
/>
)
})}
</div>
</div>
<div className="space-y-1.5">
<Label className="text-xs">Radius</Label>
<div className="grid grid-cols-5 gap-2">
<Separator orientation="vertical" className="hidden h-6 sm:block" />
<div className="flex flex-col gap-2">
<Label className="sr-only text-xs">Radius</Label>
<div className="flex flex-wrap gap-1 md:gap-2">
{["0", "0.3", "0.5", "0.75", "1.0"].map((value) => {
return (
<Button
@@ -183,8 +177,9 @@ function Customizer() {
})
}}
className={cn(
"w-[40px] rounded-lg",
config.radius === parseFloat(value) &&
"border-2 border-primary"
"border-primary/50 ring-[2px] ring-primary/30"
)}
>
{value}
@@ -193,81 +188,50 @@ function Customizer() {
})}
</div>
</div>
<div className="space-y-1.5">
<Label className="text-xs">Mode</Label>
<div className="grid grid-cols-3 gap-2">
{mounted ? (
<>
<Button
variant={"outline"}
size="sm"
onClick={() => setMode("light")}
className={cn(mode === "light" && "border-2 border-primary")}
>
<Sun className="mr-1 -translate-x-1" />
Light
</Button>
<Button
variant={"outline"}
size="sm"
onClick={() => setMode("dark")}
className={cn(mode === "dark" && "border-2 border-primary")}
>
<Moon className="mr-1 -translate-x-1" />
Dark
</Button>
</>
) : (
<>
<Skeleton className="h-8 w-full" />
<Skeleton className="h-8 w-full" />
</>
)}
</div>
<div className="flex gap-2 sm:ml-auto">
<CopyCodeButton />
</div>
</div>
</ThemeWrapper>
)
}
function CopyCodeButton({
export function CopyCodeButton({
className,
...props
}: React.ComponentProps<typeof Button>) {
const [config] = useConfig()
const activeTheme = baseColors.find((theme) => theme.name === config.theme)
const [hasCopied, setHasCopied] = React.useState(false)
React.useEffect(() => {
setTimeout(() => {
setHasCopied(false)
}, 2000)
}, [hasCopied])
return (
<>
{activeTheme && (
<Button
onClick={() => {
copyToClipboardWithMeta(getThemeCode(activeTheme, config.radius), {
name: "copy_theme_code",
properties: {
theme: activeTheme.name,
radius: config.radius,
},
})
setHasCopied(true)
}}
className={cn("md:hidden", className)}
{...props}
>
{hasCopied ? <Check /> : <Copy />}
Copy code
</Button>
)}
<Drawer>
<DrawerTrigger asChild>
<Button
className={cn("h-8 rounded-lg shadow-none sm:hidden", className)}
{...props}
>
Copy
</Button>
</DrawerTrigger>
<DrawerContent>
<DrawerHeader>
<DrawerTitle>Theme</DrawerTitle>
<DrawerDescription>
Copy and paste the following code into your CSS file.
</DrawerDescription>
</DrawerHeader>
<ThemeWrapper defaultTheme="zinc" className="relative px-6">
<CustomizerCode />
</ThemeWrapper>
</DrawerContent>
</Drawer>
<Dialog>
<DialogTrigger asChild>
<Button className={cn("hidden md:flex", className)} {...props}>
<Button
className={cn(
"hidden h-8 rounded-lg shadow-none sm:flex",
className
)}
{...props}
>
Copy code
</Button>
</DialogTrigger>
@@ -280,28 +244,6 @@ function CopyCodeButton({
</DialogHeader>
<ThemeWrapper defaultTheme="zinc" className="relative">
<CustomizerCode />
{activeTheme && (
<Button
size="sm"
onClick={() => {
copyToClipboardWithMeta(
getThemeCode(activeTheme, config.radius),
{
name: "copy_theme_code",
properties: {
theme: activeTheme.name,
radius: config.radius,
},
}
)
setHasCopied(true)
}}
className="absolute right-4 top-4 bg-muted text-muted-foreground hover:bg-muted hover:text-muted-foreground"
>
{hasCopied ? <Check /> : <Copy />}
Copy
</Button>
)}
</ThemeWrapper>
</DialogContent>
</Dialog>
@@ -311,168 +253,269 @@ function CopyCodeButton({
function CustomizerCode() {
const [config] = useConfig()
const activeTheme = baseColors.find((theme) => theme.name === config.theme)
const [hasCopied, setHasCopied] = React.useState(false)
const [themeVersion, setThemeVersion] = React.useState("v4")
const activeTheme = React.useMemo(
() => baseColors.find((theme) => theme.name === config.theme),
[config.theme]
)
const activeThemeOKLCH = React.useMemo(
() => baseColorsOKLCH[config.theme as keyof typeof baseColorsOKLCH],
[config.theme]
)
React.useEffect(() => {
if (hasCopied) {
setTimeout(() => {
setHasCopied(false)
}, 2000)
}
}, [hasCopied])
return (
<ThemeWrapper defaultTheme="zinc" className="relative space-y-4">
<div data-rehype-pretty-code-fragment="">
<pre className="max-h-[450px] overflow-x-auto rounded-lg border bg-zinc-950 py-4 dark:bg-zinc-900">
<code className="relative rounded bg-muted px-[0.3rem] py-[0.2rem] font-mono text-sm">
<span className="line text-white">@layer base &#123;</span>
<span className="line text-white">&nbsp;&nbsp;:root &#123;</span>
<span className="line text-white">
&nbsp;&nbsp;&nbsp;&nbsp;--background:{" "}
{activeTheme?.cssVars.light["background"]};
</span>
<span className="line text-white">
&nbsp;&nbsp;&nbsp;&nbsp;--foreground:{" "}
{activeTheme?.cssVars.light["foreground"]};
</span>
{[
"card",
"popover",
"primary",
"secondary",
"muted",
"accent",
"destructive",
].map((prefix) => (
<>
<span className="line text-white">
&nbsp;&nbsp;&nbsp;&nbsp;--{prefix}:{" "}
{
activeTheme?.cssVars.light[
prefix as keyof typeof activeTheme.cssVars.light
]
}
;
</span>
<span className="line text-white">
&nbsp;&nbsp;&nbsp;&nbsp;--{prefix}-foreground:{" "}
{
activeTheme?.cssVars.light[
`${prefix}-foreground` as keyof typeof activeTheme.cssVars.light
]
}
;
</span>
</>
))}
<span className="line text-white">
&nbsp;&nbsp;&nbsp;&nbsp;--border:{" "}
{activeTheme?.cssVars.light["border"]};
</span>
<span className="line text-white">
&nbsp;&nbsp;&nbsp;&nbsp;--input:{" "}
{activeTheme?.cssVars.light["input"]};
</span>
<span className="line text-white">
&nbsp;&nbsp;&nbsp;&nbsp;--ring:{" "}
{activeTheme?.cssVars.light["ring"]};
</span>
<span className="line text-white">
&nbsp;&nbsp;&nbsp;&nbsp;--radius: {config.radius}rem;
</span>
{["chart-1", "chart-2", "chart-3", "chart-4", "chart-5"].map(
(prefix) => (
<>
<span className="line text-white">
&nbsp;&nbsp;&nbsp;&nbsp;--{prefix}:{" "}
{
activeTheme?.cssVars.light[
prefix as keyof typeof activeTheme.cssVars.light
]
}
;
</span>
</>
<Tabs value={themeVersion} onValueChange={setThemeVersion}>
<div className="flex items-center justify-between">
<TabsList>
<TabsTrigger value="v4">Tailwind v4</TabsTrigger>
<TabsTrigger value="v3">v3</TabsTrigger>
</TabsList>
<Button
size="sm"
variant="outline"
onClick={() => {
copyToClipboardWithMeta(
themeVersion === "v3"
? getThemeCode(activeTheme, config.radius)
: getThemeCodeOKLCH(activeThemeOKLCH, config.radius),
{
name: "copy_theme_code",
properties: {
theme: config.theme,
radius: config.radius,
},
}
)
)}
<span className="line text-white">&nbsp;&nbsp;&#125;</span>
<span className="line text-white">&nbsp;</span>
<span className="line text-white">&nbsp;&nbsp;.dark &#123;</span>
<span className="line text-white">
&nbsp;&nbsp;&nbsp;&nbsp;--background:{" "}
{activeTheme?.cssVars.dark["background"]};
</span>
<span className="line text-white">
&nbsp;&nbsp;&nbsp;&nbsp;--foreground:{" "}
{activeTheme?.cssVars.dark["foreground"]};
</span>
{[
"card",
"popover",
"primary",
"secondary",
"muted",
"accent",
"destructive",
].map((prefix) => (
<>
setHasCopied(true)
}}
className="absolute right-0 top-0 shadow-none"
>
{hasCopied ? <Check /> : <ClipboardIcon />}
Copy
</Button>
</div>
<TabsContent value="v4">
<div data-rehype-pretty-code-fragment="">
<pre className="max-h-[450px] overflow-x-auto rounded-lg border bg-zinc-950 py-4 dark:bg-zinc-900">
<code className="relative rounded bg-muted px-[0.3rem] py-[0.2rem] font-mono text-sm">
<span className="line text-white">&nbsp;:root &#123;</span>
<span className="line text-white">
&nbsp;&nbsp;&nbsp;&nbsp;--{prefix}:{" "}
{
activeTheme?.cssVars.dark[
prefix as keyof typeof activeTheme.cssVars.dark
]
}
;
&nbsp;&nbsp;&nbsp;--radius: {config.radius}rem;
</span>
<span className="line text-white">
&nbsp;&nbsp;&nbsp;&nbsp;--{prefix}-foreground:{" "}
{
activeTheme?.cssVars.dark[
`${prefix}-foreground` as keyof typeof activeTheme.cssVars.dark
]
}
;
</span>
</>
))}
<span className="line text-white">
&nbsp;&nbsp;&nbsp;&nbsp;--border:{" "}
{activeTheme?.cssVars.dark["border"]};
</span>
<span className="line text-white">
&nbsp;&nbsp;&nbsp;&nbsp;--input:{" "}
{activeTheme?.cssVars.dark["input"]};
</span>
<span className="line text-white">
&nbsp;&nbsp;&nbsp;&nbsp;--ring:{" "}
{activeTheme?.cssVars.dark["ring"]};
</span>
{["chart-1", "chart-2", "chart-3", "chart-4", "chart-5"].map(
(prefix) => (
<>
<span className="line text-white">
&nbsp;&nbsp;&nbsp;&nbsp;--{prefix}:{" "}
{
activeTheme?.cssVars.dark[
prefix as keyof typeof activeTheme.cssVars.dark
]
}
;
{Object.entries(activeThemeOKLCH?.light).map(([key, value]) => (
<span className="line text-white" key={key}>
&nbsp;&nbsp;&nbsp;--{key}: {value};
</span>
</>
)
)}
<span className="line text-white">&nbsp;&nbsp;&#125;</span>
<span className="line text-white">&#125;</span>
</code>
</pre>
</div>
))}
<span className="line text-white">&nbsp;&#125;</span>
<span className="line text-white">&nbsp;</span>
<span className="line text-white">&nbsp;.dark &#123;</span>
{Object.entries(activeThemeOKLCH?.dark).map(([key, value]) => (
<span className="line text-white" key={key}>
&nbsp;&nbsp;&nbsp;--{key}: {value};
</span>
))}
<span className="line text-white">&nbsp;&#125;</span>
</code>
</pre>
</div>
</TabsContent>
<TabsContent value="v3">
<div data-rehype-pretty-code-fragment="">
<pre className="max-h-[450px] overflow-x-auto rounded-lg border bg-zinc-950 py-4 dark:bg-zinc-900">
<code className="relative rounded bg-muted px-[0.3rem] py-[0.2rem] font-mono text-sm">
<span className="line text-white">@layer base &#123;</span>
<span className="line text-white">
&nbsp;&nbsp;:root &#123;
</span>
<span className="line text-white">
&nbsp;&nbsp;&nbsp;&nbsp;--background:{" "}
{activeTheme?.cssVars.light["background"]};
</span>
<span className="line text-white">
&nbsp;&nbsp;&nbsp;&nbsp;--foreground:{" "}
{activeTheme?.cssVars.light["foreground"]};
</span>
{[
"card",
"popover",
"primary",
"secondary",
"muted",
"accent",
"destructive",
].map((prefix) => (
<>
<span className="line text-white">
&nbsp;&nbsp;&nbsp;&nbsp;--{prefix}:{" "}
{
activeTheme?.cssVars.light[
prefix as keyof typeof activeTheme.cssVars.light
]
}
;
</span>
<span className="line text-white">
&nbsp;&nbsp;&nbsp;&nbsp;--{prefix}-foreground:{" "}
{
activeTheme?.cssVars.light[
`${prefix}-foreground` as keyof typeof activeTheme.cssVars.light
]
}
;
</span>
</>
))}
<span className="line text-white">
&nbsp;&nbsp;&nbsp;&nbsp;--border:{" "}
{activeTheme?.cssVars.light["border"]};
</span>
<span className="line text-white">
&nbsp;&nbsp;&nbsp;&nbsp;--input:{" "}
{activeTheme?.cssVars.light["input"]};
</span>
<span className="line text-white">
&nbsp;&nbsp;&nbsp;&nbsp;--ring:{" "}
{activeTheme?.cssVars.light["ring"]};
</span>
<span className="line text-white">
&nbsp;&nbsp;&nbsp;&nbsp;--radius: {config.radius}rem;
</span>
{["chart-1", "chart-2", "chart-3", "chart-4", "chart-5"].map(
(prefix) => (
<>
<span className="line text-white">
&nbsp;&nbsp;&nbsp;&nbsp;--{prefix}:{" "}
{
activeTheme?.cssVars.light[
prefix as keyof typeof activeTheme.cssVars.light
]
}
;
</span>
</>
)
)}
<span className="line text-white">&nbsp;&nbsp;&#125;</span>
<span className="line text-white">&nbsp;</span>
<span className="line text-white">
&nbsp;&nbsp;.dark &#123;
</span>
<span className="line text-white">
&nbsp;&nbsp;&nbsp;&nbsp;--background:{" "}
{activeTheme?.cssVars.dark["background"]};
</span>
<span className="line text-white">
&nbsp;&nbsp;&nbsp;&nbsp;--foreground:{" "}
{activeTheme?.cssVars.dark["foreground"]};
</span>
{[
"card",
"popover",
"primary",
"secondary",
"muted",
"accent",
"destructive",
].map((prefix) => (
<>
<span className="line text-white">
&nbsp;&nbsp;&nbsp;&nbsp;--{prefix}:{" "}
{
activeTheme?.cssVars.dark[
prefix as keyof typeof activeTheme.cssVars.dark
]
}
;
</span>
<span className="line text-white">
&nbsp;&nbsp;&nbsp;&nbsp;--{prefix}-foreground:{" "}
{
activeTheme?.cssVars.dark[
`${prefix}-foreground` as keyof typeof activeTheme.cssVars.dark
]
}
;
</span>
</>
))}
<span className="line text-white">
&nbsp;&nbsp;&nbsp;&nbsp;--border:{" "}
{activeTheme?.cssVars.dark["border"]};
</span>
<span className="line text-white">
&nbsp;&nbsp;&nbsp;&nbsp;--input:{" "}
{activeTheme?.cssVars.dark["input"]};
</span>
<span className="line text-white">
&nbsp;&nbsp;&nbsp;&nbsp;--ring:{" "}
{activeTheme?.cssVars.dark["ring"]};
</span>
{["chart-1", "chart-2", "chart-3", "chart-4", "chart-5"].map(
(prefix) => (
<>
<span className="line text-white">
&nbsp;&nbsp;&nbsp;&nbsp;--{prefix}:{" "}
{
activeTheme?.cssVars.dark[
prefix as keyof typeof activeTheme.cssVars.dark
]
}
;
</span>
</>
)
)}
<span className="line text-white">&nbsp;&nbsp;&#125;</span>
<span className="line text-white">&#125;</span>
</code>
</pre>
</div>
</TabsContent>
</Tabs>
</ThemeWrapper>
)
}
function getThemeCode(theme: BaseColor, radius: number) {
function getThemeCodeOKLCH(theme: BaseColorOKLCH | undefined, radius: number) {
if (!theme) {
return ""
}
const rootSection =
":root {\n --radius: " +
radius +
"rem;\n" +
Object.entries(theme.light)
.map((entry) => " --" + entry[0] + ": " + entry[1] + ";")
.join("\n") +
"\n}\n\n.dark {\n" +
Object.entries(theme.dark)
.map((entry) => " --" + entry[0] + ": " + entry[1] + ";")
.join("\n") +
"\n}\n"
return rootSection
}
function getThemeCode(theme: BaseColor | undefined, radius: number) {
if (!theme) {
return ""
}
return template(BASE_STYLES_WITH_VARIABLES)({
colors: theme.cssVars,
radius,
radius: radius.toString(),
})
}

View File

@@ -433,6 +433,11 @@ export const docsConfig: DocsConfig = {
href: "/docs/registry/getting-started",
items: [],
},
{
title: "Examples",
href: "/docs/registry/examples",
items: [],
},
{
title: "Open in v0",
href: "/docs/registry/open-in-v0",

View File

@@ -321,7 +321,7 @@ I've been working on a new CLI for the past few weeks. It's a complete rewrite.
### `init`
```bash
npx shadcn-ui@latest init
npx shadcn@latest init
```
When you run the `init` command, you will be asked a few questions to configure `components.json`:
@@ -363,7 +363,7 @@ This means you can now use the CLI with any directory structure including `src`
### `add`
```bash
npx shadcn-ui@latest add
npx shadcn@latest add
```
The `add` command is now much more capable. You can now add UI components but also import more complex components (coming soon).
@@ -373,7 +373,7 @@ The CLI will automatically resolve all components and dependencies, format them
### `diff` (experimental)
```bash
npx shadcn-ui diff
npx shadcn diff
```
We're also introducing a new `diff` command to help you keep track of upstream updates.
@@ -383,7 +383,7 @@ You can use this command to see what has changed in the upstream repository and
Run the `diff` command to get a list of components that have updates available:
```bash
npx shadcn-ui diff
npx shadcn diff
```
```txt
@@ -398,7 +398,7 @@ The following components have updates available:
Then run `diff [component]` to see the changes:
```bash
npx shadcn-ui diff alert
npx shadcn diff alert
```
```diff /pl-12/

View File

@@ -79,7 +79,7 @@ export const onCreateWebpackConfig = ({ actions }) => {
### Run the CLI
Run the `shadcn-ui` init command to setup your project:
Run the `shadcn` init command to setup your project:
```bash
npx shadcn@latest init

View File

@@ -13,7 +13,7 @@ npx create-react-router@latest my-app
### Run the CLI
Run the `shadcn-ui` init command to setup your project:
Run the `shadcn` init command to setup your project:
```bash
npx shadcn@latest init

View File

@@ -26,7 +26,7 @@ npx create-remix@latest my-app
### Run the CLI
Run the `shadcn-ui` init command to setup your project:
Run the `shadcn` init command to setup your project:
```bash
npx shadcn@latest init

View File

@@ -0,0 +1,356 @@
---
title: Examples
description: "Examples of registry items: styles, components, css vars, etc."
---
## registry:style
### Custom style that extends shadcn/ui
The following registry item is a custom style that extends shadcn/ui. On `npx shadcn init`, it will:
- Install `@tabler/icons-react` as a dependency.
- Add the `login-01` block and `calendar` component to the project.
- Add the `editor` from a remote registry.
- Set the `font-sans` variable to `Inter, sans-serif`.
- Install a `brand` color in light and dark mode.
```json title="example-style.json" showLineNumbers
{
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
"name": "example-style",
"type": "registry:style",
"dependencies": ["@tabler/icons-react"],
"registryDependencies": [
"login-01",
"calendar",
"https://example.com/r/editor.json"
],
"cssVars": {
"theme": {
"font-sans": "Inter, sans-serif"
},
"light": {
"brand": "20 14.3% 4.1%"
},
"dark": {
"brand": "20 14.3% 4.1%"
}
}
}
```
### Custom style from scratch
The following registry item is a custom style that doesn't extend shadcn/ui. See the `extends: none` field.
It can be used to create a new style from scratch i.e custom components, css vars, dependencies, etc.
On `npx shadcn add`, the following will:
- Install `tailwind-merge` and `clsx` as dependencies.
- Add the `utils` registry item from the shadcn/ui registry.
- Add the `button`, `input`, `label`, and `select` components from a remote registry.
- Install new css vars: `main`, `bg`, `border`, `text`, `ring`.
```json title="example-style.json" showLineNumbers
{
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
"extends": "none",
"name": "new-style",
"type": "registry:style",
"dependencies": ["tailwind-merge", "clsx"],
"registryDependencies": [
"utils",
"https://example.com/r/button.json",
"https://example.com/r/input.json",
"https://example.com/r/label.json",
"https://example.com/r/select.json"
],
"cssVars": {
"theme": {
"font-sans": "Inter, sans-serif",
}
"light": {
"main": "#88aaee",
"bg": "#dfe5f2",
"border": "#000",
"text": "#000",
"ring": "#000",
},
"dark": {
"main": "#88aaee",
"bg": "#272933",
"border": "#000",
"text": "#e6e6e6",
"ring": "#fff",
}
}
}
```
## registry:theme
### Custom theme
```json title="example-theme.json" showLineNumbers
{
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
"name": "custom-theme",
"type": "registry:theme",
"cssVars": {
"light": {
"background": "oklch(1 0 0)",
"foreground": "oklch(0.141 0.005 285.823)",
"primary": "oklch(0.546 0.245 262.881)",
"primary-foreground": "oklch(0.97 0.014 254.604)",
"ring": "oklch(0.746 0.16 232.661)",
"sidebar-primary": "oklch(0.546 0.245 262.881)",
"sidebar-primary-foreground": "oklch(0.97 0.014 254.604)",
"sidebar-ring": "oklch(0.746 0.16 232.661)"
},
"dark": {
"background": "oklch(1 0 0)",
"foreground": "oklch(0.141 0.005 285.823)",
"primary": "oklch(0.707 0.165 254.624)",
"primary-foreground": "oklch(0.97 0.014 254.604)",
"ring": "oklch(0.707 0.165 254.624)",
"sidebar-primary": "oklch(0.707 0.165 254.624)",
"sidebar-primary-foreground": "oklch(0.97 0.014 254.604)",
"sidebar-ring": "oklch(0.707 0.165 254.624)"
}
}
}
```
### Custom colors
The following style will init using shadcn/ui defaults and then add a custom `brand` color.
```json title="example-style.json" showLineNumbers
{
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
"name": "custom-style",
"type": "registry:style",
"cssVars": {
"light": {
"brand": "oklch(0.99 0.00 0)"
},
"dark": {
"brand": "oklch(0.14 0.00 286)"
}
}
}
```
## registry:block
### Custom block
This blocks installs the `login-01` block from the shadcn/ui registry.
```json title="login-01.json" showLineNumbers
{
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
"name": "login-01",
"type": "registry:block",
"description": "A simple login form.",
"registryDependencies": ["button", "card", "input", "label"],
"files": [
{
"path": "blocks/login-01/page.tsx",
"content": "import { LoginForm } ...",
"type": "registry:page",
"target": "app/login/page.tsx"
},
{
"path": "blocks/login-01/components/login-form.tsx",
"content": "...",
"type": "registry:component"
}
]
}
```
### Install a block and override primitives
You can install a block fromt the shadcn/ui registry and override the primitives using your custom ones.
On `npx shadcn add`, the following will:
- Add the `login-01` block from the shadcn/ui registry.
- Override the `button`, `input`, and `label` primitives with the ones from the remote registry.
```json title="example-style.json" showLineNumbers
{
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
"name": "custom-login",
"type": "registry:block",
"registryDependencies": [
"login-01",
"https://example.com/r/button.json",
"https://example.com/r/input.json",
"https://example.com/r/label.json"
]
}
```
## CSS Variables
### Custom Theme Variables
Add custom theme variables to the `theme` object.
```json title="example-theme.json" showLineNumbers
{
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
"name": "custom-theme",
"type": "registry:theme",
"cssVars": {
"theme": {
"font-heading": "Inter, sans-serif",
"shadow-card": "0 0 0 1px rgba(0, 0, 0, 0.1)"
}
}
}
```
### Override Tailwind CSS variables
```json title="example-theme.json" showLineNumbers
{
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
"name": "custom-theme",
"type": "registry:theme",
"cssVars": {
"theme": {
"spacing": "0.2rem",
"breakpoint-sm": "640px",
"breakpoint-md": "768px",
"breakpoint-lg": "1024px",
"breakpoint-xl": "1280px",
"breakpoint-2xl": "1536px"
}
}
}
```
## Add custom CSS
### Base styles
```json title="example-base.json" showLineNumbers
{
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
"name": "custom-style",
"type": "registry:style",
"css": {
"@layer base": {
"h1": {
"font-size": "var(--text-2xl)"
},
"h2": {
"font-size": "var(--text-xl)"
}
}
}
}
```
### Components
```json title="example-card.json" showLineNumbers
{
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
"name": "custom-card",
"type": "registry:component",
"css": {
"@layer components": {
"card": {
"background-color": "var(--color-white)",
"border-radius": "var(--rounded-lg)",
"padding": "var(--spacing-6)",
"box-shadow": "var(--shadow-xl)"
}
}
}
}
```
## Add custom utilities
### Simple utility
```json title="example-component.json" showLineNumbers
{
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
"name": "custom-component",
"type": "registry:component",
"css": {
"@utility content-auto": {
"content-visibility": "auto"
}
}
}
```
### Complex utility
```json title="example-utility.json" showLineNumbers
{
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
"name": "custom-component",
"type": "registry:component",
"css": {
"@utility scrollbar-hidden": {
"scrollbar-hidden": {
"&::-webkit-scrollbar": {
"display": "none"
}
}
}
}
}
```
### Functional utilities
```json title="example-functional.json" showLineNumbers
{
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
"name": "custom-component",
"type": "registry:component",
"css": {
"@utility tab-*": {
"tab-size": "var(--tab-size-*)"
}
}
}
```
## Add custom animations
Note: you need to define both `@keyframes` in css and `theme` in cssVars to use animations.
```json title="example-component.json" showLineNumbers
{
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
"name": "custom-component",
"type": "registry:component",
"cssVars": {
"theme": {
"--animate-wiggle": "wiggle 1s ease-in-out infinite"
}
},
"css": {
"@keyframes wiggle": {
"0%, 100%": {
"transform": "rotate(-3deg)"
},
"50%": {
"transform": "rotate(3deg)"
}
}
}
}
```

View File

@@ -49,6 +49,46 @@ Here's an example of a complex component that installs a page, two components, a
### How do I add a new Tailwind color?
<Tabs defaultValue="v4">
<TabsList>
<TabsTrigger value="v4">Tailwind CSS v4</TabsTrigger>
<TabsTrigger value="v3">Tailwind CSS v3</TabsTrigger>
</TabsList>
<TabsContent value="v4">
To add a new color you need to add it to `cssVars` under `light` and `dark` keys.
```json showLineNumbers {10-18}
{
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
"name": "hello-world",
"title": "Hello World",
"type": "registry:block",
"description": "A complex hello world component",
"files": [
// ...
],
"cssVars": {
"light": {
"brand-background": "20 14.3% 4.1%",
"brand-accent": "20 14.3% 4.1%"
},
"dark": {
"brand-background": "20 14.3% 4.1%",
"brand-accent": "20 14.3% 4.1%"
}
}
}
```
The CLI will update the project CSS file. Once updated, the new colors will be available to be used as utility classes: `bg-brand` and `text-brand-accent`.
</TabsContent>
<TabsContent value="v3">
To add a new color you need to add it to `cssVars` and `tailwind.config.theme.extend.colors`.
```json showLineNumbers {10-19} {24-29}
@@ -90,9 +130,47 @@ To add a new color you need to add it to `cssVars` and `tailwind.config.theme.ex
The CLI will update the project CSS file and tailwind.config.js file. Once updated, the new colors will be available to be used as utility classes: `bg-brand` and `text-brand-accent`.
### How do I add a Tailwind animation?
</TabsContent>
</Tabs>
To add a new animation you add it to `tailwind.config.theme.extend.animation` and `tailwind.config.theme.extend.keyframes`.
### How do I add or override a Tailwind theme variable?
<Tabs defaultValue="v4">
<TabsList>
<TabsTrigger value="v4">Tailwind CSS v4</TabsTrigger>
<TabsTrigger value="v3">Tailwind CSS v3</TabsTrigger>
</TabsList>
<TabsContent value="v4">
To add or override a theme variable you add it to `cssVars.theme` under the key you want to add or override.
```json showLineNumbers {10-15}
{
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
"name": "hello-world",
"title": "Hello World",
"type": "registry:block",
"description": "A complex hello world component",
"files": [
// ...
],
"cssVars": {
"theme": {
"text-base": "3rem",
"ease-in-out": "cubic-bezier(0.4, 0, 0.2, 1)",
"font-heading": "Poppins, sans-serif"
}
}
}
```
</TabsContent>
<TabsContent value="v3">
To override a theme variable you add it to `tailwind.config.theme.extend` under the key you want to override.
```json showLineNumbers {14-22}
{
@@ -108,14 +186,8 @@ To add a new animation you add it to `tailwind.config.theme.extend.animation` an
"config": {
"theme": {
"extend": {
"keyframes": {
"wiggle": {
"0%, 100%": { "transform": "rotate(-3deg)" },
"50%": { "transform": "rotate(3deg)" }
}
},
"animation": {
"wiggle": "wiggle 1s ease-in-out infinite"
"text": {
"base": "3rem"
}
}
}
@@ -123,3 +195,6 @@ To add a new animation you add it to `tailwind.config.theme.extend.animation` an
}
}
```
</TabsContent>
</Tabs>

View File

@@ -21,10 +21,25 @@ The `registry-item.json` schema is used to define your custom registry items.
"path": "registry/new-york/hello-world/use-hello-world.ts",
"type": "registry:hook"
}
]
],
"cssVars": {
"theme": {
"font-heading": "Poppins, sans-serif"
},
"light": {
"brand": "20 14.3% 4.1%"
},
"dark": {
"brand": "20 14.3% 4.1%"
}
}
}
```
<div className="flex gap-2 items-center mt-6">
<Link href="/docs/registry/examples">See more examples</Link>
</div>
## Definitions
You can see the JSON Schema for `registry-item.json` [here](https://ui.shadcn.com/schema/registry-item.json).
@@ -41,7 +56,7 @@ The `$schema` property is used to specify the schema for the `registry-item.json
### name
The `name` property is used to specify the name of your registry item.
The name of the item. This is used to identify the item in the registry. It should be unique for your registry.
```json title="registry-item.json" showLineNumbers
{
@@ -71,7 +86,7 @@ A description of your registry item. This can be longer and more detailed than t
### type
The `type` property is used to specify the type of your registry item.
The `type` property is used to specify the type of your registry item. This is used to determine the type and target path of the item when resolved for a project.
```json title="registry-item.json" showLineNumbers
{
@@ -90,6 +105,8 @@ The following types are supported:
| `registry:ui` | Use for UI components and single-file primitives |
| `registry:page` | Use for page or file-based routes. |
| `registry:file` | Use for miscellaneous files. |
| `registry:style` | Use for registry styles. eg. `new-york` |
| `registry:theme` | Use for themes. |
### author
@@ -122,7 +139,7 @@ Use `@version` to specify the version of your registry item.
### registryDependencies
Used for registry dependencies. Can be names or URLs.
Used for registry dependencies. Can be names or URLs. Use the name of the item to reference shadcn/ui components and urls to reference other registries.
- For `shadcn/ui` registry items such as `button`, `input`, `select`, etc use the name eg. `['button', 'input', 'select']`.
- For custom registry items use the URL of the registry item eg. `['https://example.com/r/hello-world.json']`.
@@ -189,6 +206,8 @@ Use `~` to refer to the root of the project e.g `~/foo.config.js`.
### tailwind
**DEPRECATED:** Use `cssVars.theme` instead for Tailwind v4 projects.
The `tailwind` property is used for tailwind configuration such as `theme`, `plugins` and `content`.
You can use the `tailwind.config` property to add colors, animations and plugins to your registry item.
@@ -225,6 +244,9 @@ Use to define CSS variables for your registry item.
```json title="registry-item.json" showLineNumbers
{
"cssVars": {
"theme": {
"font-heading": "Poppins, sans-serif"
},
"light": {
"brand": "20 14.3% 4.1%",
"radius": "0.5rem"
@@ -236,10 +258,40 @@ Use to define CSS variables for your registry item.
}
```
<Callout>
**Note:** When adding colors, make sure to also add them to the
`tailwind.config.theme.extend.colors` property.
</Callout>
### css
Use `css` to add new rules to the project's CSS file eg. `@layer base`, `@layer components`, `@utility`, `@keyframes`, etc.
```json title="registry-item.json" showLineNumbers
{
"css": {
"@layer base": {
"body": {
"font-size": "var(--text-base)",
"line-height": "1.5"
}
},
"@layer components": {
"button": {
"background-color": "var(--color-primary)",
"color": "var(--color-white)"
}
},
"@utility text-magic": {
"font-size": "var(--text-base)",
"line-height": "1.5"
},
"@keyframes wiggle": {
"0%, 100%": {
"transform": "rotate(-3deg)"
},
"50%": {
"transform": "rotate(3deg)"
}
}
}
}
```
### docs

View File

@@ -92,6 +92,7 @@ Here's the list of variables available for customization:
```css title="app/globals.css"
:root {
--radius: 0.625rem;
--background: oklch(1 0 0);
--foreground: oklch(0.145 0 0);
--card: oklch(1 0 0);
@@ -107,7 +108,6 @@ Here's the list of variables available for customization:
--accent: oklch(0.97 0 0);
--accent-foreground: oklch(0.205 0 0);
--destructive: oklch(0.577 0.245 27.325);
--destructive-foreground: oklch(0.577 0.245 27.325);
--border: oklch(0.922 0 0);
--input: oklch(0.922 0 0);
--ring: oklch(0.708 0 0);
@@ -116,7 +116,6 @@ Here's the list of variables available for customization:
--chart-3: oklch(0.398 0.07 227.392);
--chart-4: oklch(0.828 0.189 84.429);
--chart-5: oklch(0.769 0.188 70.08);
--radius: 0.625rem;
--sidebar: oklch(0.985 0 0);
--sidebar-foreground: oklch(0.145 0 0);
--sidebar-primary: oklch(0.205 0 0);
@@ -130,22 +129,21 @@ Here's the list of variables available for customization:
.dark {
--background: oklch(0.145 0 0);
--foreground: oklch(0.985 0 0);
--card: oklch(0.145 0 0);
--card: oklch(0.205 0 0);
--card-foreground: oklch(0.985 0 0);
--popover: oklch(0.145 0 0);
--popover: oklch(0.269 0 0);
--popover-foreground: oklch(0.985 0 0);
--primary: oklch(0.985 0 0);
--primary: oklch(0.922 0 0);
--primary-foreground: oklch(0.205 0 0);
--secondary: oklch(0.269 0 0);
--secondary-foreground: oklch(0.985 0 0);
--muted: oklch(0.269 0 0);
--muted-foreground: oklch(0.708 0 0);
--accent: oklch(0.269 0 0);
--accent: oklch(0.371 0 0);
--accent-foreground: oklch(0.985 0 0);
--destructive: oklch(0.396 0.141 25.723);
--destructive-foreground: oklch(0.637 0.237 25.331);
--border: oklch(0.269 0 0);
--input: oklch(0.269 0 0);
--destructive: oklch(0.704 0.191 22.216);
--border: oklch(1 0 0 / 10%);
--input: oklch(1 0 0 / 15%);
--ring: oklch(0.556 0 0);
--chart-1: oklch(0.488 0.243 264.376);
--chart-2: oklch(0.696 0.17 162.48);
@@ -158,7 +156,7 @@ Here's the list of variables available for customization:
--sidebar-primary-foreground: oklch(0.985 0 0);
--sidebar-accent: oklch(0.269 0 0);
--sidebar-accent-foreground: oklch(0.985 0 0);
--sidebar-border: oklch(0.269 0 0);
--sidebar-border: oklch(1 0 0 / 10%);
--sidebar-ring: oklch(0.439 0 0);
}
```

View File

@@ -113,7 +113,7 @@ export default makeSource({
[
rehypePrettyCode,
{
theme: "github-dark",
theme: "github-dark-default",
getHighlighter,
onVisitLine(node) {
// Prevent lines from collapsing in `display: grid` mode, and allow empty

View File

@@ -2,6 +2,12 @@ import { createContentlayerPlugin } from "next-contentlayer2"
/** @type {import('next').NextConfig} */
const nextConfig = {
eslint: {
ignoreDuringBuilds: true,
},
typescript: {
ignoreBuildErrors: true,
},
experimental: {
outputFileTracingIncludes: {
"/blocks/*": ["./registry/**/*"],

View File

@@ -46,7 +46,7 @@
"@radix-ui/react-portal": "^1.0.4",
"@radix-ui/react-progress": "^1.0.3",
"@radix-ui/react-radio-group": "^1.1.3",
"@radix-ui/react-scroll-area": "^1.0.4",
"@radix-ui/react-scroll-area": "^1.2.3",
"@radix-ui/react-select": "^2.0.0",
"@radix-ui/react-separator": "^1.0.3",
"@radix-ui/react-slider": "^1.1.2",
@@ -86,8 +86,8 @@
"react-resizable-panels": "^2.0.22",
"react-wrap-balancer": "^0.4.1",
"recharts": "2.12.7",
"shadcn": "2.3.0",
"sharp": "^0.31.3",
"shadcn": "2.5.0",
"sharp": "^0.32.6",
"sonner": "^1.2.3",
"swr": "2.2.6-beta.3",
"tailwind-merge": "^1.12.0",
@@ -102,7 +102,7 @@
"@types/react": "^18.2.65",
"@types/react-color": "^3.0.6",
"@types/react-dom": "^18.2.22",
"esbuild": "^0.17.19",
"esbuild": "^0.25.0",
"eslint": "^8.41.0",
"mdast-util-toc": "^6.1.1",
"postcss": "^8.4.24",
@@ -112,7 +112,7 @@
"rehype-slug": "^5.1.0",
"remark": "^14.0.3",
"remark-code-import": "^1.2.0",
"remark-gfm": "^4.0.0",
"remark-gfm": "^4.0.1",
"rimraf": "^4.1.3",
"shiki": "^1.10.1",
"tailwindcss": "3.4.6",

View File

@@ -223,7 +223,7 @@
"name": "command",
"type": "registry:ui",
"dependencies": [
"cmdk@1.0.0"
"cmdk"
],
"registryDependencies": [
"dialog"

View File

@@ -4,7 +4,7 @@
"type": "registry:ui",
"author": "shadcn (https://ui.shadcn.com)",
"dependencies": [
"cmdk@1.0.0"
"cmdk"
],
"registryDependencies": [
"dialog"

View File

@@ -11,35 +11,5 @@
"content": "\"use client\"\n\nimport * as React from \"react\"\nimport * as AccordionPrimitive from \"@radix-ui/react-accordion\"\nimport { ChevronDownIcon } from \"lucide-react\"\n\nimport { cn } from \"@/lib/utils\"\n\nfunction Accordion({\n ...props\n}: React.ComponentProps<typeof AccordionPrimitive.Root>) {\n return <AccordionPrimitive.Root data-slot=\"accordion\" {...props} />\n}\n\nfunction AccordionItem({\n className,\n ...props\n}: React.ComponentProps<typeof AccordionPrimitive.Item>) {\n return (\n <AccordionPrimitive.Item\n data-slot=\"accordion-item\"\n className={cn(\"border-b last:border-b-0\", className)}\n {...props}\n />\n )\n}\n\nfunction AccordionTrigger({\n className,\n children,\n ...props\n}: React.ComponentProps<typeof AccordionPrimitive.Trigger>) {\n return (\n <AccordionPrimitive.Header className=\"flex\">\n <AccordionPrimitive.Trigger\n data-slot=\"accordion-trigger\"\n className={cn(\n \"focus-visible:border-ring focus-visible:ring-ring/50 flex flex-1 items-start justify-between gap-4 rounded-md py-4 text-left text-sm font-medium transition-all outline-none hover:underline focus-visible:ring-[3px] disabled:pointer-events-none disabled:opacity-50 [&[data-state=open]>svg]:rotate-180\",\n className\n )}\n {...props}\n >\n {children}\n <ChevronDownIcon className=\"text-muted-foreground pointer-events-none size-4 shrink-0 translate-y-0.5 transition-transform duration-200\" />\n </AccordionPrimitive.Trigger>\n </AccordionPrimitive.Header>\n )\n}\n\nfunction AccordionContent({\n className,\n children,\n ...props\n}: React.ComponentProps<typeof AccordionPrimitive.Content>) {\n return (\n <AccordionPrimitive.Content\n data-slot=\"accordion-content\"\n className=\"data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down overflow-hidden text-sm\"\n {...props}\n >\n <div className={cn(\"pt-0 pb-4\", className)}>{children}</div>\n </AccordionPrimitive.Content>\n )\n}\n\nexport { Accordion, AccordionItem, AccordionTrigger, AccordionContent }\n",
"type": "registry:ui"
}
],
"tailwind": {
"config": {
"theme": {
"extend": {
"keyframes": {
"accordion-down": {
"from": {
"height": "0"
},
"to": {
"height": "var(--radix-accordion-content-height)"
}
},
"accordion-up": {
"from": {
"height": "var(--radix-accordion-content-height)"
},
"to": {
"height": "0"
}
}
},
"animation": {
"accordion-down": "accordion-down 0.2s ease-out",
"accordion-up": "accordion-up 0.2s ease-out"
}
}
}
}
}
]
}

View File

@@ -8,7 +8,7 @@
"files": [
{
"path": "registry/new-york-v4/ui/badge.tsx",
"content": "import * as React from \"react\"\nimport { Slot } from \"@radix-ui/react-slot\"\nimport { cva, type VariantProps } from \"class-variance-authority\"\n\nimport { cn } from \"@/lib/utils\"\n\nconst badgeVariants = cva(\n \"inline-flex items-center justify-center rounded-md border px-2 py-0.5 text-xs font-medium w-fit whitespace-nowrap shrink-0 [&>svg]:size-3 gap-1 [&>svg]:pointer-events-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 transition-[color,box-shadow] overflow-hidden\",\n {\n variants: {\n variant: {\n default:\n \"border-transparent bg-primary text-primary-foreground [a&]:hover:bg-primary/90\",\n secondary:\n \"border-transparent bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90\",\n destructive:\n \"border-transparent bg-destructive text-white [a&]:hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/70\",\n outline:\n \"text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground\",\n },\n },\n defaultVariants: {\n variant: \"default\",\n },\n }\n)\n\nfunction Badge({\n className,\n variant,\n asChild = false,\n ...props\n}: React.ComponentProps<\"span\"> &\n VariantProps<typeof badgeVariants> & { asChild?: boolean }) {\n const Comp = asChild ? Slot : \"span\"\n\n return (\n <Comp\n data-slot=\"badge\"\n className={cn(badgeVariants({ variant }), className)}\n {...props}\n />\n )\n}\n\nexport { Badge, badgeVariants }\n",
"content": "import * as React from \"react\"\nimport { Slot } from \"@radix-ui/react-slot\"\nimport { cva, type VariantProps } from \"class-variance-authority\"\n\nimport { cn } from \"@/lib/utils\"\n\nconst badgeVariants = cva(\n \"inline-flex items-center justify-center rounded-md border px-2 py-0.5 text-xs font-medium w-fit whitespace-nowrap shrink-0 [&>svg]:size-3 gap-1 [&>svg]:pointer-events-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 transition-[color,box-shadow] overflow-hidden\",\n {\n variants: {\n variant: {\n default:\n \"border-transparent bg-primary text-primary-foreground [a&]:hover:bg-primary/90\",\n secondary:\n \"border-transparent bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90\",\n destructive:\n \"border-transparent bg-destructive text-white [a&]:hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60\",\n outline:\n \"text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground\",\n },\n },\n defaultVariants: {\n variant: \"default\",\n },\n }\n)\n\nfunction Badge({\n className,\n variant,\n asChild = false,\n ...props\n}: React.ComponentProps<\"span\"> &\n VariantProps<typeof badgeVariants> & { asChild?: boolean }) {\n const Comp = asChild ? Slot : \"span\"\n\n return (\n <Comp\n data-slot=\"badge\"\n className={cn(badgeVariants({ variant }), className)}\n {...props}\n />\n )\n}\n\nexport { Badge, badgeVariants }\n",
"type": "registry:ui"
}
]

View File

@@ -3,7 +3,7 @@
"name": "command",
"type": "registry:ui",
"dependencies": [
"cmdk@1.0.0"
"cmdk"
],
"registryDependencies": [
"dialog"

View File

@@ -3,10 +3,12 @@
"name": "index",
"type": "registry:style",
"dependencies": [
"tw-animate-css",
"class-variance-authority",
"lucide-react"
],
"devDependencies": [
"tw-animate-css"
],
"registryDependencies": [
"utils"
],

File diff suppressed because one or more lines are too long

View File

@@ -8,7 +8,7 @@
"files": [
{
"path": "registry/new-york-v4/ui/scroll-area.tsx",
"content": "\"use client\"\n\nimport * as React from \"react\"\nimport * as ScrollAreaPrimitive from \"@radix-ui/react-scroll-area\"\n\nimport { cn } from \"@/lib/utils\"\n\nfunction ScrollArea({\n className,\n children,\n ...props\n}: React.ComponentProps<typeof ScrollAreaPrimitive.Root>) {\n return (\n <ScrollAreaPrimitive.Root\n data-slot=\"scroll-area\"\n className={cn(\"relative\", className)}\n {...props}\n >\n <ScrollAreaPrimitive.Viewport\n data-slot=\"scroll-area-viewport\"\n className=\"ring-ring/10 dark:ring-ring/20 dark:outline-ring/40 outline-ring/50 size-full rounded-[inherit] transition-[color,box-shadow] focus-visible:ring-4 focus-visible:outline-1\"\n >\n {children}\n </ScrollAreaPrimitive.Viewport>\n <ScrollBar />\n <ScrollAreaPrimitive.Corner />\n </ScrollAreaPrimitive.Root>\n )\n}\n\nfunction ScrollBar({\n className,\n orientation = \"vertical\",\n ...props\n}: React.ComponentProps<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>) {\n return (\n <ScrollAreaPrimitive.ScrollAreaScrollbar\n data-slot=\"scroll-area-scrollbar\"\n orientation={orientation}\n className={cn(\n \"flex touch-none p-px transition-colors select-none\",\n orientation === \"vertical\" &&\n \"h-full w-2.5 border-l border-l-transparent\",\n orientation === \"horizontal\" &&\n \"h-2.5 flex-col border-t border-t-transparent\",\n className\n )}\n {...props}\n >\n <ScrollAreaPrimitive.ScrollAreaThumb\n data-slot=\"scroll-area-thumb\"\n className=\"bg-border relative flex-1 rounded-full\"\n />\n </ScrollAreaPrimitive.ScrollAreaScrollbar>\n )\n}\n\nexport { ScrollArea, ScrollBar }\n",
"content": "\"use client\"\n\nimport * as React from \"react\"\nimport * as ScrollAreaPrimitive from \"@radix-ui/react-scroll-area\"\n\nimport { cn } from \"@/lib/utils\"\n\nfunction ScrollArea({\n className,\n children,\n ...props\n}: React.ComponentProps<typeof ScrollAreaPrimitive.Root>) {\n return (\n <ScrollAreaPrimitive.Root\n data-slot=\"scroll-area\"\n className={cn(\"relative\", className)}\n {...props}\n >\n <ScrollAreaPrimitive.Viewport\n data-slot=\"scroll-area-viewport\"\n className=\"focus-visible:ring-ring/50 size-full rounded-[inherit] transition-[color,box-shadow] outline-none focus-visible:ring-[3px] focus-visible:outline-1\"\n >\n {children}\n </ScrollAreaPrimitive.Viewport>\n <ScrollBar />\n <ScrollAreaPrimitive.Corner />\n </ScrollAreaPrimitive.Root>\n )\n}\n\nfunction ScrollBar({\n className,\n orientation = \"vertical\",\n ...props\n}: React.ComponentProps<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>) {\n return (\n <ScrollAreaPrimitive.ScrollAreaScrollbar\n data-slot=\"scroll-area-scrollbar\"\n orientation={orientation}\n className={cn(\n \"flex touch-none p-px transition-colors select-none\",\n orientation === \"vertical\" &&\n \"h-full w-2.5 border-l border-l-transparent\",\n orientation === \"horizontal\" &&\n \"h-2.5 flex-col border-t border-t-transparent\",\n className\n )}\n {...props}\n >\n <ScrollAreaPrimitive.ScrollAreaThumb\n data-slot=\"scroll-area-thumb\"\n className=\"bg-border relative flex-1 rounded-full\"\n />\n </ScrollAreaPrimitive.ScrollAreaScrollbar>\n )\n}\n\nexport { ScrollArea, ScrollBar }\n",
"type": "registry:ui"
}
]

View File

@@ -4,7 +4,7 @@
"type": "registry:ui",
"author": "shadcn (https://ui.shadcn.com)",
"dependencies": [
"cmdk@1.0.0"
"cmdk"
],
"registryDependencies": [
"dialog"

View File

@@ -3,7 +3,8 @@
"type": "object",
"properties": {
"name": {
"type": "string"
"type": "string",
"description": "The name of the item. This is used to identify the item in the registry. It should be unique for your registry."
},
"type": {
"type": "string",
@@ -15,46 +16,57 @@
"registry:hook",
"registry:theme",
"registry:page",
"registry:file"
]
"registry:file",
"registry:style"
],
"description": "The type of the item. This is used to determine the type and target path of the item when resolved for a project."
},
"description": {
"type": "string"
"type": "string",
"description": "The description of the item. This is used to provide a brief overview of the item."
},
"title": {
"type": "string"
"type": "string",
"description": "The human-readable title for your registry item. Keep it short and descriptive."
},
"author": {
"type": "string"
"type": "string",
"description": "The author of the item. Recommended format: username <url>"
},
"dependencies": {
"type": "array",
"description": "An array of NPM dependencies required by the registry item.",
"items": {
"type": "string"
}
},
"devDependencies": {
"type": "array",
"description": "An array of NPM dev dependencies required by the registry item.",
"items": {
"type": "string"
}
},
"registryDependencies": {
"type": "array",
"description": "An array of registry items that this item depends on. Use the name of the item to reference shadcn/ui components and urls to reference other registries.",
"items": {
"type": "string"
}
},
"files": {
"type": "array",
"description": "The main payload of the registry item. This is an array of files that are part of the registry item. Each file is an object with a path, content, type, and target.",
"items": {
"type": "object",
"properties": {
"path": {
"type": "string"
"type": "string",
"description": "The path to the file relative to the registry root."
},
"content": {
"type": "string"
"type": "string",
"description": "The content of the file."
},
"type": {
"type": "string",
@@ -66,11 +78,14 @@
"registry:hook",
"registry:theme",
"registry:page",
"registry:file"
]
"registry:file",
"registry:style"
],
"description": "The type of the file. This is used to determine the type of the file when resolved for a project."
},
"target": {
"type": "string"
"type": "string",
"description": "The target path of the file. This is the path to the file in the project."
}
},
"if": {
@@ -90,6 +105,7 @@
},
"tailwind": {
"type": "object",
"description": "The tailwind configuration for the registry item. This is an object with a config property. Use cssVars for Tailwind v4 projects.",
"properties": {
"config": {
"type": "object",
@@ -116,33 +132,82 @@
},
"cssVars": {
"type": "object",
"description": "The css variables for the registry item. This will be merged with the project's css variables.",
"properties": {
"theme": {
"type": "object",
"description": "CSS variables for the @theme directive. For Tailwind v4 projects only. Use tailwind for older projects.",
"additionalProperties": {
"type": "string"
}
},
"light": {
"type": "object",
"description": "CSS variables for the light theme.",
"additionalProperties": {
"type": "string"
}
},
"dark": {
"type": "object",
"description": "CSS variables for the dark theme.",
"additionalProperties": {
"type": "string"
}
}
}
},
"css": {
"type": "object",
"description": "CSS definitions to be added to the project's CSS file. Supports at-rules, selectors, nested rules, utilities, layers, and more.",
"additionalProperties": {
"oneOf": [
{
"type": "string",
"description": "Direct CSS string (e.g., 'font-family: sans-serif; line-height: 1.5;')"
},
{
"type": "object",
"description": "CSS properties or nested selectors",
"additionalProperties": {
"oneOf": [
{
"type": "string",
"description": "CSS property value (e.g., 'blue', 'var(--color-primary)')"
},
{
"type": "object",
"description": "Nested selector or rule with properties",
"additionalProperties": {
"type": "string",
"description": "CSS property value for nested rule"
}
}
]
}
}
]
}
},
"meta": {
"type": "object",
"description": "Additional metadata for the registry item. This is an object with any key value pairs.",
"additionalProperties": true
},
"docs": {
"type": "string"
"type": "string",
"description": "The documentation for the registry item. This is a markdown string."
},
"categories": {
"type": "array",
"items": {
"type": "string"
"type": "string",
"description": "The categories of the registry item. This is an array of strings."
}
},
"extends": {
"type": "string",
"description": "The name of the registry item to extend. This is used to extend the base shadcn/ui style. Set to none to start fresh. This is available for registry:style items only."
}
},
"required": ["name", "type"]

View File

@@ -1102,3 +1102,550 @@ export const baseColorsV4 = {
},
},
} as const
export const baseColorsOKLCH = {
zinc: {
light: {
background: "oklch(1 0 0)", // --color-zinc-50
foreground: "oklch(0.141 0.005 285.823)", // --color-zinc-950
card: "oklch(1 0 0)", // --color-zinc-50
"card-foreground": "oklch(0.141 0.005 285.823)", // --color-zinc-950
popover: "oklch(1 0 0)", // --color-zinc-50
"popover-foreground": "oklch(0.141 0.005 285.823)", // --color-zinc-950
primary: "oklch(0.21 0.006 285.885)", // --color-zinc-900
"primary-foreground": "oklch(0.985 0 0)", // --color-zinc-50
secondary: "oklch(0.967 0.001 286.375)", // --color-zinc-100
"secondary-foreground": "oklch(0.21 0.006 285.885)", // --color-zinc-900
muted: "oklch(0.967 0.001 286.375)", // --color-zinc-100
"muted-foreground": "oklch(0.552 0.016 285.938)", // --color-zinc-500
accent: "oklch(0.967 0.001 286.375)", // --color-zinc-100
"accent-foreground": "oklch(0.21 0.006 285.885)", // --color-zinc-900
destructive: "oklch(0.577 0.245 27.325)", // --color-red-600
border: "oklch(0.92 0.004 286.32)", // --color-zinc-200
input: "oklch(0.92 0.004 286.32)", // --color-zinc-200
ring: "oklch(0.705 0.015 286.067)", // --color-zinc-400
"chart-1": "oklch(0.646 0.222 41.116)", // --color-orange-600
"chart-2": "oklch(0.6 0.118 184.704)", // --color-teal-600
"chart-3": "oklch(0.398 0.07 227.392)", // --color-cyan-900
"chart-4": "oklch(0.828 0.189 84.429)", // --color-amber-400
"chart-5": "oklch(0.769 0.188 70.08)", // --color-amber-500
sidebar: "oklch(0.985 0 0)", // --color-zinc-50
"sidebar-foreground": "oklch(0.141 0.005 285.823)", // --color-zinc-950
"sidebar-primary": "oklch(0.21 0.006 285.885)", // --color-zinc-900
"sidebar-primary-foreground": "oklch(0.985 0 0)", // --color-zinc-50
"sidebar-accent": "oklch(0.967 0.001 286.375)", // --color-zinc-100
"sidebar-accent-foreground": "oklch(0.21 0.006 285.885)", // --color-zinc-900
"sidebar-border": "oklch(0.92 0.004 286.32)", // --color-zinc-200
"sidebar-ring": "oklch(0.705 0.015 286.067)", // --color-zinc-400
},
dark: {
background: "oklch(0.141 0.005 285.823)", // --color-zinc-950
foreground: "oklch(0.985 0 0)", // --color-zinc-50
card: "oklch(0.21 0.006 285.885)", // --color-zinc-900
"card-foreground": "oklch(0.985 0 0)", // --color-zinc-50
popover: "oklch(0.21 0.006 285.885)", // --color-zinc-900
"popover-foreground": "oklch(0.985 0 0)", // --color-zinc-50
primary: "oklch(0.92 0.004 286.32)", // --color-zinc-200
"primary-foreground": "oklch(0.21 0.006 285.885)", // --color-zinc-900
secondary: "oklch(0.274 0.006 286.033)", // --color-zinc-800
"secondary-foreground": "oklch(0.985 0 0)", // --color-zinc-50
muted: "oklch(0.274 0.006 286.033)", // --color-zinc-800
"muted-foreground": "oklch(0.705 0.015 286.067)", // --color-zinc-400
accent: "oklch(0.274 0.006 286.033)", // --color-zinc-800
"accent-foreground": "oklch(0.985 0 0)", // --color-zinc-50
destructive: "oklch(0.704 0.191 22.216)", // --color-red-400
border: "oklch(1 0 0 / 10%)", // --color-white
input: "oklch(1 0 0 / 15%)", // --color-white
ring: "oklch(0.552 0.016 285.938)", // --color-zinc-500
"chart-1": "oklch(0.488 0.243 264.376)", // --color-blue-700
"chart-2": "oklch(0.696 0.17 162.48)", // --color-emerald-500
"chart-3": "oklch(0.769 0.188 70.08)", // --color-amber-500
"chart-4": "oklch(0.627 0.265 303.9)", // --color-purple-500
"chart-5": "oklch(0.645 0.246 16.439)", // --color-rose-500
sidebar: "oklch(0.21 0.006 285.885)", // --color-zinc-900
"sidebar-foreground": "oklch(0.985 0 0)", // --color-zinc-50
"sidebar-primary": "oklch(0.488 0.243 264.376)", // --color-blue-700
"sidebar-primary-foreground": "oklch(0.985 0 0)", // --color-zinc-50
"sidebar-accent": "oklch(0.274 0.006 286.033)", // --color-zinc-800
"sidebar-accent-foreground": "oklch(0.985 0 0)", // --color-zinc-50
"sidebar-border": "oklch(1 0 0 / 10%)", // --color-white
"sidebar-ring": "oklch(0.552 0.016 285.938)", // --color-zinc-500
},
},
red: {
light: {
background: "oklch(1 0 0)", // --color-zinc-50 (from zinc)
foreground: "oklch(0.141 0.005 285.823)", // --color-zinc-950 (from zinc)
card: "oklch(1 0 0)", // --color-zinc-50 (from zinc)
"card-foreground": "oklch(0.141 0.005 285.823)", // --color-zinc-950 (from zinc)
popover: "oklch(1 0 0)", // --color-zinc-50 (from zinc)
"popover-foreground": "oklch(0.141 0.005 285.823)", // --color-zinc-950 (from zinc)
primary: "oklch(0.637 0.237 25.331)", // --color-red-500 (approx HSL 0 72.2% 50.6%)
"primary-foreground": "oklch(0.971 0.013 17.38)", // --color-red-50 (approx HSL 0 85.7% 97.3%)
secondary: "oklch(0.967 0.001 286.375)", // --color-zinc-100 (from zinc)
"secondary-foreground": "oklch(0.21 0.006 285.885)", // --color-zinc-900 (from zinc)
muted: "oklch(0.967 0.001 286.375)", // --color-zinc-100 (from zinc)
"muted-foreground": "oklch(0.552 0.016 285.938)", // --color-zinc-500 (from zinc)
accent: "oklch(0.967 0.001 286.375)", // --color-zinc-100 (from zinc)
"accent-foreground": "oklch(0.21 0.006 285.885)", // --color-zinc-900 (from zinc)
destructive: "oklch(0.577 0.245 27.325)", // --color-red-600 (from zinc)
border: "oklch(0.92 0.004 286.32)", // --color-zinc-200 (from zinc)
input: "oklch(0.92 0.004 286.32)", // --color-zinc-200 (from zinc)
ring: "oklch(0.637 0.237 25.331)", // --color-red-500 (approx HSL 0 72.2% 50.6%)
"chart-1": "oklch(0.646 0.222 41.116)", // --color-orange-600 (from zinc)
"chart-2": "oklch(0.6 0.118 184.704)", // --color-teal-600 (from zinc)
"chart-3": "oklch(0.398 0.07 227.392)", // --color-cyan-900 (from zinc)
"chart-4": "oklch(0.828 0.189 84.429)", // --color-amber-400 (from zinc)
"chart-5": "oklch(0.769 0.188 70.08)", // --color-amber-500 (from zinc)
sidebar: "oklch(0.985 0 0)", // --color-zinc-50 (from zinc)
"sidebar-foreground": "oklch(0.141 0.005 285.823)", // --color-zinc-950 (from zinc)
"sidebar-primary": "oklch(0.637 0.237 25.331)", // --color-red-500 (approx HSL 0 72.2% 50.6%)
"sidebar-primary-foreground": "oklch(0.971 0.013 17.38)", // --color-red-50 (approx HSL 0 85.7% 97.3%)
"sidebar-accent": "oklch(0.967 0.001 286.375)", // --color-zinc-100 (from zinc)
"sidebar-accent-foreground": "oklch(0.21 0.006 285.885)", // --color-zinc-900 (from zinc)
"sidebar-border": "oklch(0.92 0.004 286.32)", // --color-zinc-200 (from zinc)
"sidebar-ring": "oklch(0.637 0.237 25.331)", // --color-red-500 (approx HSL 0 72.2% 50.6%)
},
dark: {
background: "oklch(0.141 0.005 285.823)", // --color-zinc-950 (from zinc)
foreground: "oklch(0.985 0 0)", // --color-zinc-50 (from zinc)
card: "oklch(0.21 0.006 285.885)", // --color-zinc-900 (from zinc)
"card-foreground": "oklch(0.985 0 0)", // --color-zinc-50 (from zinc)
popover: "oklch(0.21 0.006 285.885)", // --color-zinc-900 (from zinc)
"popover-foreground": "oklch(0.985 0 0)", // --color-zinc-50 (from zinc)
primary: "oklch(0.637 0.237 25.331)", // --color-red-500 (approx HSL 0 72.2% 50.6%)
"primary-foreground": "oklch(0.971 0.013 17.38)", // --color-red-50 (approx HSL 0 85.7% 97.3%)
secondary: "oklch(0.274 0.006 286.033)", // --color-zinc-800 (from zinc)
"secondary-foreground": "oklch(0.985 0 0)", // --color-zinc-50 (from zinc)
muted: "oklch(0.274 0.006 286.033)", // --color-zinc-800 (from zinc)
"muted-foreground": "oklch(0.705 0.015 286.067)", // --color-zinc-400 (from zinc)
accent: "oklch(0.274 0.006 286.033)", // --color-zinc-800 (from zinc)
"accent-foreground": "oklch(0.985 0 0)", // --color-zinc-50 (from zinc)
destructive: "oklch(0.704 0.191 22.216)", // --color-red-400 (from zinc)
border: "oklch(1 0 0 / 10%)", // --color-white (from zinc)
input: "oklch(1 0 0 / 15%)", // --color-white (from zinc)
ring: "oklch(0.637 0.237 25.331)", // --color-red-500 (approx HSL 0 72.2% 50.6%)
"chart-1": "oklch(0.488 0.243 264.376)", // --color-blue-700 (from zinc)
"chart-2": "oklch(0.696 0.17 162.48)", // --color-emerald-500 (from zinc)
"chart-3": "oklch(0.769 0.188 70.08)", // --color-amber-500 (from zinc)
"chart-4": "oklch(0.627 0.265 303.9)", // --color-purple-500 (from zinc)
"chart-5": "oklch(0.645 0.246 16.439)", // --color-rose-500 (from zinc)
sidebar: "oklch(0.21 0.006 285.885)", // --color-zinc-900 (from zinc)
"sidebar-foreground": "oklch(0.985 0 0)", // --color-zinc-50 (from zinc)
"sidebar-primary": "oklch(0.637 0.237 25.331)", // --color-red-500 (approx HSL 0 72.2% 50.6%)
"sidebar-primary-foreground": "oklch(0.971 0.013 17.38)", // --color-red-50 (approx HSL 0 85.7% 97.3%)
"sidebar-accent": "oklch(0.274 0.006 286.033)", // --color-zinc-800 (from zinc)
"sidebar-accent-foreground": "oklch(0.985 0 0)", // --color-zinc-50 (from zinc)
"sidebar-border": "oklch(1 0 0 / 10%)", // --color-white (from zinc)
"sidebar-ring": "oklch(0.637 0.237 25.331)", // --color-red-500 (approx HSL 0 72.2% 50.6%)
},
},
rose: {
light: {
background: "oklch(1 0 0)", // --color-zinc-50 (from zinc)
foreground: "oklch(0.141 0.005 285.823)", // --color-zinc-950 (from zinc)
card: "oklch(1 0 0)", // --color-zinc-50 (from zinc)
"card-foreground": "oklch(0.141 0.005 285.823)", // --color-zinc-950 (from zinc)
popover: "oklch(1 0 0)", // --color-zinc-50 (from zinc)
"popover-foreground": "oklch(0.141 0.005 285.823)", // --color-zinc-950 (from zinc)
primary: "oklch(0.645 0.246 16.439)", // --color-rose-500 (approx HSL 346.8 77.2% 49.8%)
"primary-foreground": "oklch(0.969 0.015 12.422)", // --color-rose-50 (approx HSL 355.7 100% 97.3%)
secondary: "oklch(0.967 0.001 286.375)", // --color-zinc-100 (from zinc)
"secondary-foreground": "oklch(0.21 0.006 285.885)", // --color-zinc-900 (from zinc)
muted: "oklch(0.967 0.001 286.375)", // --color-zinc-100 (from zinc)
"muted-foreground": "oklch(0.552 0.016 285.938)", // --color-zinc-500 (from zinc)
accent: "oklch(0.967 0.001 286.375)", // --color-zinc-100 (from zinc)
"accent-foreground": "oklch(0.21 0.006 285.885)", // --color-zinc-900 (from zinc)
destructive: "oklch(0.577 0.245 27.325)", // --color-red-600 (from zinc)
border: "oklch(0.92 0.004 286.32)", // --color-zinc-200 (from zinc)
input: "oklch(0.92 0.004 286.32)", // --color-zinc-200 (from zinc)
ring: "oklch(0.645 0.246 16.439)", // --color-rose-500 (approx HSL 346.8 77.2% 49.8%)
"chart-1": "oklch(0.646 0.222 41.116)", // --color-orange-600 (from zinc)
"chart-2": "oklch(0.6 0.118 184.704)", // --color-teal-600 (from zinc)
"chart-3": "oklch(0.398 0.07 227.392)", // --color-cyan-900 (from zinc)
"chart-4": "oklch(0.828 0.189 84.429)", // --color-amber-400 (from zinc)
"chart-5": "oklch(0.769 0.188 70.08)", // --color-amber-500 (from zinc)
sidebar: "oklch(0.985 0 0)", // --color-zinc-50 (from zinc)
"sidebar-foreground": "oklch(0.141 0.005 285.823)", // --color-zinc-950 (from zinc)
"sidebar-primary": "oklch(0.645 0.246 16.439)", // --color-rose-500 (approx HSL 346.8 77.2% 49.8%)
"sidebar-primary-foreground": "oklch(0.969 0.015 12.422)", // --color-rose-50 (approx HSL 355.7 100% 97.3%)
"sidebar-accent": "oklch(0.967 0.001 286.375)", // --color-zinc-100 (from zinc)
"sidebar-accent-foreground": "oklch(0.21 0.006 285.885)", // --color-zinc-900 (from zinc)
"sidebar-border": "oklch(0.92 0.004 286.32)", // --color-zinc-200 (from zinc)
"sidebar-ring": "oklch(0.645 0.246 16.439)", // --color-rose-500 (approx HSL 346.8 77.2% 49.8%)
},
dark: {
background: "oklch(0.141 0.005 285.823)", // --color-zinc-950 (from zinc)
foreground: "oklch(0.985 0 0)", // --color-zinc-50 (from zinc)
card: "oklch(0.21 0.006 285.885)", // --color-zinc-900 (from zinc)
"card-foreground": "oklch(0.985 0 0)", // --color-zinc-50 (from zinc)
popover: "oklch(0.21 0.006 285.885)", // --color-zinc-900 (from zinc)
"popover-foreground": "oklch(0.985 0 0)", // --color-zinc-50 (from zinc)
primary: "oklch(0.645 0.246 16.439)", // --color-rose-500 (approx HSL 346.8 77.2% 49.8%)
"primary-foreground": "oklch(0.969 0.015 12.422)", // --color-rose-50 (approx HSL 355.7 100% 97.3%)
secondary: "oklch(0.274 0.006 286.033)", // --color-zinc-800 (from zinc)
"secondary-foreground": "oklch(0.985 0 0)", // --color-zinc-50 (from zinc)
muted: "oklch(0.274 0.006 286.033)", // --color-zinc-800 (from zinc)
"muted-foreground": "oklch(0.705 0.015 286.067)", // --color-zinc-400 (from zinc)
accent: "oklch(0.274 0.006 286.033)", // --color-zinc-800 (from zinc)
"accent-foreground": "oklch(0.985 0 0)", // --color-zinc-50 (from zinc)
destructive: "oklch(0.704 0.191 22.216)", // --color-red-400 (from zinc)
border: "oklch(1 0 0 / 10%)", // --color-white (from zinc)
input: "oklch(1 0 0 / 15%)", // --color-white (from zinc)
ring: "oklch(0.645 0.246 16.439)", // --color-rose-500 (approx HSL 346.8 77.2% 49.8%)
"chart-1": "oklch(0.488 0.243 264.376)", // --color-blue-700 (from zinc)
"chart-2": "oklch(0.696 0.17 162.48)", // --color-emerald-500 (from zinc)
"chart-3": "oklch(0.769 0.188 70.08)", // --color-amber-500 (from zinc)
"chart-4": "oklch(0.627 0.265 303.9)", // --color-purple-500 (from zinc)
"chart-5": "oklch(0.645 0.246 16.439)", // --color-rose-500 (from zinc)
sidebar: "oklch(0.21 0.006 285.885)", // --color-zinc-900 (from zinc)
"sidebar-foreground": "oklch(0.985 0 0)", // --color-zinc-50 (from zinc)
"sidebar-primary": "oklch(0.645 0.246 16.439)", // --color-rose-500 (approx HSL 346.8 77.2% 49.8%)
"sidebar-primary-foreground": "oklch(0.969 0.015 12.422)", // --color-rose-50 (approx HSL 355.7 100% 97.3%)
"sidebar-accent": "oklch(0.274 0.006 286.033)", // --color-zinc-800 (from zinc)
"sidebar-accent-foreground": "oklch(0.985 0 0)", // --color-zinc-50 (from zinc)
"sidebar-border": "oklch(1 0 0 / 10%)", // --color-white (from zinc)
"sidebar-ring": "oklch(0.645 0.246 16.439)", // --color-rose-500 (approx HSL 346.8 77.2% 49.8%)
},
},
orange: {
light: {
background: "oklch(1 0 0)", // --color-zinc-50 (from zinc)
foreground: "oklch(0.141 0.005 285.823)", // --color-zinc-950 (from zinc)
card: "oklch(1 0 0)", // --color-zinc-50 (from zinc)
"card-foreground": "oklch(0.141 0.005 285.823)", // --color-zinc-950 (from zinc)
popover: "oklch(1 0 0)", // --color-zinc-50 (from zinc)
"popover-foreground": "oklch(0.141 0.005 285.823)", // --color-zinc-950 (from zinc)
primary: "oklch(0.705 0.213 47.604)", // --color-orange-500 (approx HSL 24.6 95% 53.1%)
"primary-foreground": "oklch(0.98 0.016 73.684)", // --color-orange-50 (approx HSL 60 9.1% 97.8%)
secondary: "oklch(0.967 0.001 286.375)", // --color-zinc-100 (from zinc)
"secondary-foreground": "oklch(0.21 0.006 285.885)", // --color-zinc-900 (from zinc)
muted: "oklch(0.967 0.001 286.375)", // --color-zinc-100 (from zinc)
"muted-foreground": "oklch(0.552 0.016 285.938)", // --color-zinc-500 (from zinc)
accent: "oklch(0.967 0.001 286.375)", // --color-zinc-100 (from zinc)
"accent-foreground": "oklch(0.21 0.006 285.885)", // --color-zinc-900 (from zinc)
destructive: "oklch(0.577 0.245 27.325)", // --color-red-600 (from zinc)
border: "oklch(0.92 0.004 286.32)", // --color-zinc-200 (from zinc)
input: "oklch(0.92 0.004 286.32)", // --color-zinc-200 (from zinc)
ring: "oklch(0.705 0.213 47.604)", // --color-orange-500 (approx HSL 24.6 95% 53.1%)
"chart-1": "oklch(0.646 0.222 41.116)", // --color-orange-600 (from zinc)
"chart-2": "oklch(0.6 0.118 184.704)", // --color-teal-600 (from zinc)
"chart-3": "oklch(0.398 0.07 227.392)", // --color-cyan-900 (from zinc)
"chart-4": "oklch(0.828 0.189 84.429)", // --color-amber-400 (from zinc)
"chart-5": "oklch(0.769 0.188 70.08)", // --color-amber-500 (from zinc)
sidebar: "oklch(0.985 0 0)", // --color-zinc-50 (from zinc)
"sidebar-foreground": "oklch(0.141 0.005 285.823)", // --color-zinc-950 (from zinc)
"sidebar-primary": "oklch(0.705 0.213 47.604)", // --color-orange-500 (approx HSL 24.6 95% 53.1%)
"sidebar-primary-foreground": "oklch(0.98 0.016 73.684)", // --color-orange-50 (approx HSL 60 9.1% 97.8%)
"sidebar-accent": "oklch(0.967 0.001 286.375)", // --color-zinc-100 (from zinc)
"sidebar-accent-foreground": "oklch(0.21 0.006 285.885)", // --color-zinc-900 (from zinc)
"sidebar-border": "oklch(0.92 0.004 286.32)", // --color-zinc-200 (from zinc)
"sidebar-ring": "oklch(0.705 0.213 47.604)", // --color-orange-500 (approx HSL 24.6 95% 53.1%)
},
dark: {
background: "oklch(0.141 0.005 285.823)", // --color-zinc-950 (from zinc)
foreground: "oklch(0.985 0 0)", // --color-zinc-50 (from zinc)
card: "oklch(0.21 0.006 285.885)", // --color-zinc-900 (from zinc)
"card-foreground": "oklch(0.985 0 0)", // --color-zinc-50 (from zinc)
popover: "oklch(0.21 0.006 285.885)", // --color-zinc-900 (from zinc)
"popover-foreground": "oklch(0.985 0 0)", // --color-zinc-50 (from zinc)
primary: "oklch(0.646 0.222 41.116)", // --color-orange-600 (approx HSL 20.5 90.2% 48.2%)
"primary-foreground": "oklch(0.98 0.016 73.684)", // --color-orange-50 (approx HSL 60 9.1% 97.8%)
secondary: "oklch(0.274 0.006 286.033)", // --color-zinc-800 (from zinc)
"secondary-foreground": "oklch(0.985 0 0)", // --color-zinc-50 (from zinc)
muted: "oklch(0.274 0.006 286.033)", // --color-zinc-800 (from zinc)
"muted-foreground": "oklch(0.705 0.015 286.067)", // --color-zinc-400 (from zinc)
accent: "oklch(0.274 0.006 286.033)", // --color-zinc-800 (from zinc)
"accent-foreground": "oklch(0.985 0 0)", // --color-zinc-50 (from zinc)
destructive: "oklch(0.704 0.191 22.216)", // --color-red-400 (from zinc)
border: "oklch(1 0 0 / 10%)", // --color-white (from zinc)
input: "oklch(1 0 0 / 15%)", // --color-white (from zinc)
ring: "oklch(0.646 0.222 41.116)", // --color-orange-600 (approx HSL 20.5 90.2% 48.2%)
"chart-1": "oklch(0.488 0.243 264.376)", // --color-blue-700 (from zinc)
"chart-2": "oklch(0.696 0.17 162.48)", // --color-emerald-500 (from zinc)
"chart-3": "oklch(0.769 0.188 70.08)", // --color-amber-500 (from zinc)
"chart-4": "oklch(0.627 0.265 303.9)", // --color-purple-500 (from zinc)
"chart-5": "oklch(0.645 0.246 16.439)", // --color-rose-500 (from zinc)
sidebar: "oklch(0.21 0.006 285.885)", // --color-zinc-900 (from zinc)
"sidebar-foreground": "oklch(0.985 0 0)", // --color-zinc-50 (from zinc)
"sidebar-primary": "oklch(0.646 0.222 41.116)", // --color-orange-600 (approx HSL 20.5 90.2% 48.2%)
"sidebar-primary-foreground": "oklch(0.98 0.016 73.684)", // --color-orange-50 (approx HSL 60 9.1% 97.8%)
"sidebar-accent": "oklch(0.274 0.006 286.033)", // --color-zinc-800 (from zinc)
"sidebar-accent-foreground": "oklch(0.985 0 0)", // --color-zinc-50 (from zinc)
"sidebar-border": "oklch(1 0 0 / 10%)", // --color-white (from zinc)
"sidebar-ring": "oklch(0.646 0.222 41.116)", // --color-orange-600 (approx HSL 20.5 90.2% 48.2%)
},
},
green: {
light: {
background: "oklch(1 0 0)", // --color-zinc-50 (from zinc)
foreground: "oklch(0.141 0.005 285.823)", // --color-zinc-950 (from zinc)
card: "oklch(1 0 0)", // --color-zinc-50 (from zinc)
"card-foreground": "oklch(0.141 0.005 285.823)", // --color-zinc-950 (from zinc)
popover: "oklch(1 0 0)", // --color-zinc-50 (from zinc)
"popover-foreground": "oklch(0.141 0.005 285.823)", // --color-zinc-950 (from zinc)
primary: "oklch(0.723 0.219 149.579)", // --color-green-500 (approx HSL 142.1 76.2% 36.3%)
"primary-foreground": "oklch(0.982 0.018 155.826)", // --color-green-50 (approx HSL 355.7 100% 97.3%)
secondary: "oklch(0.967 0.001 286.375)", // --color-zinc-100 (from zinc)
"secondary-foreground": "oklch(0.21 0.006 285.885)", // --color-zinc-900 (from zinc)
muted: "oklch(0.967 0.001 286.375)", // --color-zinc-100 (from zinc)
"muted-foreground": "oklch(0.552 0.016 285.938)", // --color-zinc-500 (from zinc)
accent: "oklch(0.967 0.001 286.375)", // --color-zinc-100 (from zinc)
"accent-foreground": "oklch(0.21 0.006 285.885)", // --color-zinc-900 (from zinc)
destructive: "oklch(0.577 0.245 27.325)", // --color-red-600 (from zinc)
border: "oklch(0.92 0.004 286.32)", // --color-zinc-200 (from zinc)
input: "oklch(0.92 0.004 286.32)", // --color-zinc-200 (from zinc)
ring: "oklch(0.723 0.219 149.579)", // --color-green-500 (approx HSL 142.1 76.2% 36.3%)
"chart-1": "oklch(0.646 0.222 41.116)", // --color-orange-600 (from zinc)
"chart-2": "oklch(0.6 0.118 184.704)", // --color-teal-600 (from zinc)
"chart-3": "oklch(0.398 0.07 227.392)", // --color-cyan-900 (from zinc)
"chart-4": "oklch(0.828 0.189 84.429)", // --color-amber-400 (from zinc)
"chart-5": "oklch(0.769 0.188 70.08)", // --color-amber-500 (from zinc)
sidebar: "oklch(0.985 0 0)", // --color-zinc-50 (from zinc)
"sidebar-foreground": "oklch(0.141 0.005 285.823)", // --color-zinc-950 (from zinc)
"sidebar-primary": "oklch(0.723 0.219 149.579)", // --color-green-500 (approx HSL 142.1 76.2% 36.3%)
"sidebar-primary-foreground": "oklch(0.982 0.018 155.826)", // --color-green-50 (approx HSL 355.7 100% 97.3%)
"sidebar-accent": "oklch(0.967 0.001 286.375)", // --color-zinc-100 (from zinc)
"sidebar-accent-foreground": "oklch(0.21 0.006 285.885)", // --color-zinc-900 (from zinc)
"sidebar-border": "oklch(0.92 0.004 286.32)", // --color-zinc-200 (from zinc)
"sidebar-ring": "oklch(0.723 0.219 149.579)", // --color-green-500 (approx HSL 142.1 76.2% 36.3%)
},
dark: {
background: "oklch(0.141 0.005 285.823)", // --color-zinc-950 (from zinc)
foreground: "oklch(0.985 0 0)", // --color-zinc-50 (from zinc)
card: "oklch(0.21 0.006 285.885)", // --color-zinc-900 (from zinc)
"card-foreground": "oklch(0.985 0 0)", // --color-zinc-50 (from zinc)
popover: "oklch(0.21 0.006 285.885)", // --color-zinc-900 (from zinc)
"popover-foreground": "oklch(0.985 0 0)", // --color-zinc-50 (from zinc)
primary: "oklch(0.696 0.17 162.48)", // --color-emerald-500 (approx HSL 142.1 70.6% 45.3%)
"primary-foreground": "oklch(0.393 0.095 152.535)", // --color-green-900 (approx HSL 144.9 80.4% 10%)
secondary: "oklch(0.274 0.006 286.033)", // --color-zinc-800 (from zinc)
"secondary-foreground": "oklch(0.985 0 0)", // --color-zinc-50 (from zinc)
muted: "oklch(0.274 0.006 286.033)", // --color-zinc-800 (from zinc)
"muted-foreground": "oklch(0.705 0.015 286.067)", // --color-zinc-400 (from zinc)
accent: "oklch(0.274 0.006 286.033)", // --color-zinc-800 (from zinc)
"accent-foreground": "oklch(0.985 0 0)", // --color-zinc-50 (from zinc)
destructive: "oklch(0.704 0.191 22.216)", // --color-red-400 (from zinc)
border: "oklch(1 0 0 / 10%)", // --color-white (from zinc)
input: "oklch(1 0 0 / 15%)", // --color-white (from zinc)
ring: "oklch(0.527 0.154 150.069)", // --color-green-700 (approx HSL 142.4 71.8% 29.2%)
"chart-1": "oklch(0.488 0.243 264.376)", // --color-blue-700 (from zinc)
"chart-2": "oklch(0.696 0.17 162.48)", // --color-emerald-500 (from zinc)
"chart-3": "oklch(0.769 0.188 70.08)", // --color-amber-500 (from zinc)
"chart-4": "oklch(0.627 0.265 303.9)", // --color-purple-500 (from zinc)
"chart-5": "oklch(0.645 0.246 16.439)", // --color-rose-500 (from zinc)
sidebar: "oklch(0.21 0.006 285.885)", // --color-zinc-900 (from zinc)
"sidebar-foreground": "oklch(0.985 0 0)", // --color-zinc-50 (from zinc)
"sidebar-primary": "oklch(0.696 0.17 162.48)", // --color-emerald-500 (approx HSL 142.1 70.6% 45.3%)
"sidebar-primary-foreground": "oklch(0.393 0.095 152.535)", // --color-green-900 (approx HSL 144.9 80.4% 10%)
"sidebar-accent": "oklch(0.274 0.006 286.033)", // --color-zinc-800 (from zinc)
"sidebar-accent-foreground": "oklch(0.985 0 0)", // --color-zinc-50 (from zinc)
"sidebar-border": "oklch(1 0 0 / 10%)", // --color-white (from zinc)
"sidebar-ring": "oklch(0.527 0.154 150.069)", // --color-green-700 (approx HSL 142.4 71.8% 29.2%)
},
},
blue: {
light: {
background: "oklch(1 0 0)", // --color-zinc-50 (from zinc)
foreground: "oklch(0.141 0.005 285.823)", // --color-zinc-950 (from zinc)
card: "oklch(1 0 0)", // --color-zinc-50 (from zinc)
"card-foreground": "oklch(0.141 0.005 285.823)", // --color-zinc-950 (from zinc)
popover: "oklch(1 0 0)", // --color-zinc-50 (from zinc)
"popover-foreground": "oklch(0.141 0.005 285.823)", // --color-zinc-950 (from zinc)
primary: "oklch(0.623 0.214 259.815)", // --color-blue-500 (approx HSL 221.2 83.2% 53.3%)
"primary-foreground": "oklch(0.97 0.014 254.604)", // --color-blue-50 (approx HSL 210 40% 98%)
secondary: "oklch(0.967 0.001 286.375)", // --color-zinc-100 (from zinc)
"secondary-foreground": "oklch(0.21 0.006 285.885)", // --color-zinc-900 (from zinc)
muted: "oklch(0.967 0.001 286.375)", // --color-zinc-100 (from zinc)
"muted-foreground": "oklch(0.552 0.016 285.938)", // --color-zinc-500 (from zinc)
accent: "oklch(0.967 0.001 286.375)", // --color-zinc-100 (from zinc)
"accent-foreground": "oklch(0.21 0.006 285.885)", // --color-zinc-900 (from zinc)
destructive: "oklch(0.577 0.245 27.325)", // --color-red-600 (from zinc)
border: "oklch(0.92 0.004 286.32)", // --color-zinc-200 (from zinc)
input: "oklch(0.92 0.004 286.32)", // --color-zinc-200 (from zinc)
ring: "oklch(0.623 0.214 259.815)", // --color-blue-500 (approx HSL 221.2 83.2% 53.3%)
"chart-1": "oklch(0.646 0.222 41.116)", // --color-orange-600 (from zinc)
"chart-2": "oklch(0.6 0.118 184.704)", // --color-teal-600 (from zinc)
"chart-3": "oklch(0.398 0.07 227.392)", // --color-cyan-900 (from zinc)
"chart-4": "oklch(0.828 0.189 84.429)", // --color-amber-400 (from zinc)
"chart-5": "oklch(0.769 0.188 70.08)", // --color-amber-500 (from zinc)
sidebar: "oklch(0.985 0 0)", // --color-zinc-50 (from zinc)
"sidebar-foreground": "oklch(0.141 0.005 285.823)", // --color-zinc-950 (from zinc)
"sidebar-primary": "oklch(0.623 0.214 259.815)", // --color-blue-500 (approx HSL 221.2 83.2% 53.3%)
"sidebar-primary-foreground": "oklch(0.97 0.014 254.604)", // --color-blue-50 (approx HSL 210 40% 98%)
"sidebar-accent": "oklch(0.967 0.001 286.375)", // --color-zinc-100 (from zinc)
"sidebar-accent-foreground": "oklch(0.21 0.006 285.885)", // --color-zinc-900 (from zinc)
"sidebar-border": "oklch(0.92 0.004 286.32)", // --color-zinc-200 (from zinc)
"sidebar-ring": "oklch(0.623 0.214 259.815)", // --color-blue-500 (approx HSL 221.2 83.2% 53.3%)
},
dark: {
background: "oklch(0.141 0.005 285.823)", // --color-zinc-950 (from zinc)
foreground: "oklch(0.985 0 0)", // --color-zinc-50 (from zinc)
card: "oklch(0.21 0.006 285.885)", // --color-zinc-900 (from zinc)
"card-foreground": "oklch(0.985 0 0)", // --color-zinc-50 (from zinc)
popover: "oklch(0.21 0.006 285.885)", // --color-zinc-900 (from zinc)
"popover-foreground": "oklch(0.985 0 0)", // --color-zinc-50 (from zinc)
primary: "oklch(0.546 0.245 262.881)", // --color-blue-600 (approx HSL 217.2 91.2% 59.8%)
"primary-foreground": "oklch(0.379 0.146 265.522)", // --color-blue-900 (approx HSL 222.2 47.4% 11.2%)
secondary: "oklch(0.274 0.006 286.033)", // --color-zinc-800 (from zinc)
"secondary-foreground": "oklch(0.985 0 0)", // --color-zinc-50 (from zinc)
muted: "oklch(0.274 0.006 286.033)", // --color-zinc-800 (from zinc)
"muted-foreground": "oklch(0.705 0.015 286.067)", // --color-zinc-400 (from zinc)
accent: "oklch(0.274 0.006 286.033)", // --color-zinc-800 (from zinc)
"accent-foreground": "oklch(0.985 0 0)", // --color-zinc-50 (from zinc)
destructive: "oklch(0.704 0.191 22.216)", // --color-red-400 (from zinc)
border: "oklch(1 0 0 / 10%)", // --color-white (from zinc)
input: "oklch(1 0 0 / 15%)", // --color-white (from zinc)
ring: "oklch(0.488 0.243 264.376)", // --color-blue-700 (approx HSL 224.3 76.3% 48%)
"chart-1": "oklch(0.488 0.243 264.376)", // --color-blue-700 (from zinc)
"chart-2": "oklch(0.696 0.17 162.48)", // --color-emerald-500 (from zinc)
"chart-3": "oklch(0.769 0.188 70.08)", // --color-amber-500 (from zinc)
"chart-4": "oklch(0.627 0.265 303.9)", // --color-purple-500 (from zinc)
"chart-5": "oklch(0.645 0.246 16.439)", // --color-rose-500 (from zinc)
sidebar: "oklch(0.21 0.006 285.885)", // --color-zinc-900 (from zinc)
"sidebar-foreground": "oklch(0.985 0 0)", // --color-zinc-50 (from zinc)
"sidebar-primary": "oklch(0.546 0.245 262.881)", // --color-blue-600 (approx HSL 217.2 91.2% 59.8%)
"sidebar-primary-foreground": "oklch(0.379 0.146 265.522)", // --color-blue-900 (approx HSL 222.2 47.4% 11.2%)
"sidebar-accent": "oklch(0.274 0.006 286.033)", // --color-zinc-800 (from zinc)
"sidebar-accent-foreground": "oklch(0.985 0 0)", // --color-zinc-50 (from zinc)
"sidebar-border": "oklch(1 0 0 / 10%)", // --color-white (from zinc)
"sidebar-ring": "oklch(0.488 0.243 264.376)", // --color-blue-700 (approx HSL 224.3 76.3% 48%)
},
},
yellow: {
light: {
background: "oklch(1 0 0)", // --color-zinc-50 (from zinc)
foreground: "oklch(0.141 0.005 285.823)", // --color-zinc-950 (from zinc)
card: "oklch(1 0 0)", // --color-zinc-50 (from zinc)
"card-foreground": "oklch(0.141 0.005 285.823)", // --color-zinc-950 (from zinc)
popover: "oklch(1 0 0)", // --color-zinc-50 (from zinc)
"popover-foreground": "oklch(0.141 0.005 285.823)", // --color-zinc-950 (from zinc)
primary: "oklch(0.795 0.184 86.047)", // --color-yellow-500 (approx HSL 47.9 95.8% 53.1%)
"primary-foreground": "oklch(0.421 0.095 57.708)", // --color-yellow-900 (approx HSL 26 83.3% 14.1%)
secondary: "oklch(0.967 0.001 286.375)", // --color-zinc-100 (from zinc)
"secondary-foreground": "oklch(0.21 0.006 285.885)", // --color-zinc-900 (from zinc)
muted: "oklch(0.967 0.001 286.375)", // --color-zinc-100 (from zinc)
"muted-foreground": "oklch(0.552 0.016 285.938)", // --color-zinc-500 (from zinc)
accent: "oklch(0.967 0.001 286.375)", // --color-zinc-100 (from zinc)
"accent-foreground": "oklch(0.21 0.006 285.885)", // --color-zinc-900 (from zinc)
destructive: "oklch(0.577 0.245 27.325)", // --color-red-600 (from zinc)
border: "oklch(0.92 0.004 286.32)", // --color-zinc-200 (from zinc)
input: "oklch(0.92 0.004 286.32)", // --color-zinc-200 (from zinc)
ring: "oklch(0.795 0.184 86.047)", // --color-yellow-500 (approx HSL 47.9 95.8% 53.1%)
"chart-1": "oklch(0.646 0.222 41.116)", // --color-orange-600 (from zinc)
"chart-2": "oklch(0.6 0.118 184.704)", // --color-teal-600 (from zinc)
"chart-3": "oklch(0.398 0.07 227.392)", // --color-cyan-900 (from zinc)
"chart-4": "oklch(0.828 0.189 84.429)", // --color-amber-400 (from zinc)
"chart-5": "oklch(0.769 0.188 70.08)", // --color-amber-500 (from zinc)
sidebar: "oklch(0.985 0 0)", // --color-zinc-50 (from zinc)
"sidebar-foreground": "oklch(0.141 0.005 285.823)", // --color-zinc-950 (from zinc)
"sidebar-primary": "oklch(0.795 0.184 86.047)", // --color-yellow-500 (approx HSL 47.9 95.8% 53.1%)
"sidebar-primary-foreground": "oklch(0.421 0.095 57.708)", // --color-yellow-900 (approx HSL 26 83.3% 14.1%)
"sidebar-accent": "oklch(0.967 0.001 286.375)", // --color-zinc-100 (from zinc)
"sidebar-accent-foreground": "oklch(0.21 0.006 285.885)", // --color-zinc-900 (from zinc)
"sidebar-border": "oklch(0.92 0.004 286.32)", // --color-zinc-200 (from zinc)
"sidebar-ring": "oklch(0.795 0.184 86.047)", // --color-yellow-500 (approx HSL 47.9 95.8% 53.1%)
},
dark: {
background: "oklch(0.141 0.005 285.823)", // --color-zinc-950 (from zinc)
foreground: "oklch(0.985 0 0)", // --color-zinc-50 (from zinc)
card: "oklch(0.21 0.006 285.885)", // --color-zinc-900 (from zinc)
"card-foreground": "oklch(0.985 0 0)", // --color-zinc-50 (from zinc)
popover: "oklch(0.21 0.006 285.885)", // --color-zinc-900 (from zinc)
"popover-foreground": "oklch(0.985 0 0)", // --color-zinc-50 (from zinc)
primary: "oklch(0.795 0.184 86.047)", // --color-yellow-500 (approx HSL 47.9 95.8% 53.1%)
"primary-foreground": "oklch(0.421 0.095 57.708)", // --color-yellow-900 (approx HSL 26 83.3% 14.1%)
secondary: "oklch(0.274 0.006 286.033)", // --color-zinc-800 (from zinc)
"secondary-foreground": "oklch(0.985 0 0)", // --color-zinc-50 (from zinc)
muted: "oklch(0.274 0.006 286.033)", // --color-zinc-800 (from zinc)
"muted-foreground": "oklch(0.705 0.015 286.067)", // --color-zinc-400 (from zinc)
accent: "oklch(0.274 0.006 286.033)", // --color-zinc-800 (from zinc)
"accent-foreground": "oklch(0.985 0 0)", // --color-zinc-50 (from zinc)
destructive: "oklch(0.704 0.191 22.216)", // --color-red-400 (from zinc)
border: "oklch(1 0 0 / 10%)", // --color-white (from zinc)
input: "oklch(1 0 0 / 15%)", // --color-white (from zinc)
ring: "oklch(0.554 0.135 66.442)", // --color-yellow-700 (approx HSL 35.5 91.7% 32.9%)
"chart-1": "oklch(0.488 0.243 264.376)", // --color-blue-700 (from zinc)
"chart-2": "oklch(0.696 0.17 162.48)", // --color-emerald-500 (from zinc)
"chart-3": "oklch(0.769 0.188 70.08)", // --color-amber-500 (from zinc)
"chart-4": "oklch(0.627 0.265 303.9)", // --color-purple-500 (from zinc)
"chart-5": "oklch(0.645 0.246 16.439)", // --color-rose-500 (from zinc)
sidebar: "oklch(0.21 0.006 285.885)", // --color-zinc-900 (from zinc)
"sidebar-foreground": "oklch(0.985 0 0)", // --color-zinc-50 (from zinc)
"sidebar-primary": "oklch(0.795 0.184 86.047)", // --color-yellow-500 (approx HSL 47.9 95.8% 53.1%)
"sidebar-primary-foreground": "oklch(0.421 0.095 57.708)", // --color-yellow-900 (approx HSL 26 83.3% 14.1%)
"sidebar-accent": "oklch(0.274 0.006 286.033)", // --color-zinc-800 (from zinc)
"sidebar-accent-foreground": "oklch(0.985 0 0)", // --color-zinc-50 (from zinc)
"sidebar-border": "oklch(1 0 0 / 10%)", // --color-white (from zinc)
"sidebar-ring": "oklch(0.554 0.135 66.442)", // --color-yellow-700 (approx HSL 35.5 91.7% 32.9%)
},
},
violet: {
light: {
background: "oklch(1 0 0)", // --color-zinc-50 (from zinc)
foreground: "oklch(0.141 0.005 285.823)", // --color-zinc-950 (from zinc)
card: "oklch(1 0 0)", // --color-zinc-50 (from zinc)
"card-foreground": "oklch(0.141 0.005 285.823)", // --color-zinc-950 (from zinc)
popover: "oklch(1 0 0)", // --color-zinc-50 (from zinc)
"popover-foreground": "oklch(0.141 0.005 285.823)", // --color-zinc-950 (from zinc)
primary: "oklch(0.606 0.25 292.717)", // --color-violet-500 (approx HSL 262.1 83.3% 57.8%)
"primary-foreground": "oklch(0.969 0.016 293.756)", // --color-violet-50 (approx HSL 210 20% 98%)
secondary: "oklch(0.967 0.001 286.375)", // --color-zinc-100 (from zinc)
"secondary-foreground": "oklch(0.21 0.006 285.885)", // --color-zinc-900 (from zinc)
muted: "oklch(0.967 0.001 286.375)", // --color-zinc-100 (from zinc)
"muted-foreground": "oklch(0.552 0.016 285.938)", // --color-zinc-500 (from zinc)
accent: "oklch(0.967 0.001 286.375)", // --color-zinc-100 (from zinc)
"accent-foreground": "oklch(0.21 0.006 285.885)", // --color-zinc-900 (from zinc)
destructive: "oklch(0.577 0.245 27.325)", // --color-red-600 (from zinc)
border: "oklch(0.92 0.004 286.32)", // --color-zinc-200 (from zinc)
input: "oklch(0.92 0.004 286.32)", // --color-zinc-200 (from zinc)
ring: "oklch(0.606 0.25 292.717)", // --color-violet-500 (approx HSL 262.1 83.3% 57.8%)
"chart-1": "oklch(0.646 0.222 41.116)", // --color-orange-600 (from zinc)
"chart-2": "oklch(0.6 0.118 184.704)", // --color-teal-600 (from zinc)
"chart-3": "oklch(0.398 0.07 227.392)", // --color-cyan-900 (from zinc)
"chart-4": "oklch(0.828 0.189 84.429)", // --color-amber-400 (from zinc)
"chart-5": "oklch(0.769 0.188 70.08)", // --color-amber-500 (from zinc)
sidebar: "oklch(0.985 0 0)", // --color-zinc-50 (from zinc)
"sidebar-foreground": "oklch(0.141 0.005 285.823)", // --color-zinc-950 (from zinc)
"sidebar-primary": "oklch(0.606 0.25 292.717)", // --color-violet-500 (approx HSL 262.1 83.3% 57.8%)
"sidebar-primary-foreground": "oklch(0.969 0.016 293.756)", // --color-violet-50 (approx HSL 210 20% 98%)
"sidebar-accent": "oklch(0.967 0.001 286.375)", // --color-zinc-100 (from zinc)
"sidebar-accent-foreground": "oklch(0.21 0.006 285.885)", // --color-zinc-900 (from zinc)
"sidebar-border": "oklch(0.92 0.004 286.32)", // --color-zinc-200 (from zinc)
"sidebar-ring": "oklch(0.606 0.25 292.717)", // --color-violet-500 (approx HSL 262.1 83.3% 57.8%)
},
dark: {
background: "oklch(0.141 0.005 285.823)", // --color-zinc-950 (from zinc)
foreground: "oklch(0.985 0 0)", // --color-zinc-50 (from zinc)
card: "oklch(0.21 0.006 285.885)", // --color-zinc-900 (from zinc)
"card-foreground": "oklch(0.985 0 0)", // --color-zinc-50 (from zinc)
popover: "oklch(0.21 0.006 285.885)", // --color-zinc-900 (from zinc)
"popover-foreground": "oklch(0.985 0 0)", // --color-zinc-50 (from zinc)
primary: "oklch(0.541 0.281 293.009)", // --color-violet-600 (approx HSL 263.4 70% 50.4%)
"primary-foreground": "oklch(0.969 0.016 293.756)", // --color-violet-50 (approx HSL 210 20% 98%)
secondary: "oklch(0.274 0.006 286.033)", // --color-zinc-800 (from zinc)
"secondary-foreground": "oklch(0.985 0 0)", // --color-zinc-50 (from zinc)
muted: "oklch(0.274 0.006 286.033)", // --color-zinc-800 (from zinc)
"muted-foreground": "oklch(0.705 0.015 286.067)", // --color-zinc-400 (from zinc)
accent: "oklch(0.274 0.006 286.033)", // --color-zinc-800 (from zinc)
"accent-foreground": "oklch(0.985 0 0)", // --color-zinc-50 (from zinc)
destructive: "oklch(0.704 0.191 22.216)", // --color-red-400 (from zinc)
border: "oklch(1 0 0 / 10%)", // --color-white (from zinc)
input: "oklch(1 0 0 / 15%)", // --color-white (from zinc)
ring: "oklch(0.541 0.281 293.009)", // --color-violet-600 (approx HSL 263.4 70% 50.4%)
"chart-1": "oklch(0.488 0.243 264.376)", // --color-blue-700 (from zinc)
"chart-2": "oklch(0.696 0.17 162.48)", // --color-emerald-500 (from zinc)
"chart-3": "oklch(0.769 0.188 70.08)", // --color-amber-500 (from zinc)
"chart-4": "oklch(0.627 0.265 303.9)", // --color-purple-500 (from zinc)
"chart-5": "oklch(0.645 0.246 16.439)", // --color-rose-500 (from zinc)
sidebar: "oklch(0.21 0.006 285.885)", // --color-zinc-900 (from zinc)
"sidebar-foreground": "oklch(0.985 0 0)", // --color-zinc-50 (from zinc)
"sidebar-primary": "oklch(0.541 0.281 293.009)", // --color-violet-600 (approx HSL 263.4 70% 50.4%)
"sidebar-primary-foreground": "oklch(0.969 0.016 293.756)", // --color-violet-50 (approx HSL 210 20% 98%)
"sidebar-accent": "oklch(0.274 0.006 286.033)", // --color-zinc-800 (from zinc)
"sidebar-accent-foreground": "oklch(0.985 0 0)", // --color-zinc-50 (from zinc)
"sidebar-border": "oklch(1 0 0 / 10%)", // --color-white (from zinc)
"sidebar-ring": "oklch(0.541 0.281 293.009)", // --color-violet-600 (approx HSL 263.4 70% 50.4%)
},
},
}

View File

@@ -182,7 +182,7 @@ export const ui: Registry["items"] = [
{
name: "command",
type: "registry:ui",
dependencies: ["cmdk@1.0.0"],
dependencies: ["cmdk"],
registryDependencies: ["dialog"],
files: [
{

View File

@@ -75,9 +75,9 @@
"tailwindcss-animate": "^1.0.5",
"tsx": "^4.1.4",
"turbo": "^1.9.9",
"vite": "^5.4.1",
"vite": "^5.4.15",
"vite-tsconfig-paths": "^4.2.0",
"vitest": "^2.0.5"
"vitest": "^2.1.9"
},
"devDependencies": {
"@types/node": "^20.11.27",

View File

@@ -1,5 +1,81 @@
# @shadcn/ui
## 2.5.0
### Minor Changes
- [#7220](https://github.com/shadcn-ui/ui/pull/7220) [`d0306774fe0ecc1eae9ef1e918bf7862e866a9e8`](https://github.com/shadcn-ui/ui/commit/d0306774fe0ecc1eae9ef1e918bf7862e866a9e8) Thanks [@shadcn](https://github.com/shadcn)! - resolve imports from anywhere
### Patch Changes
- [#6985](https://github.com/shadcn-ui/ui/pull/6985) [`f1e5cc4666ced2166a859660d769ccee16cde46e`](https://github.com/shadcn-ui/ui/commit/f1e5cc4666ced2166a859660d769ccee16cde46e) Thanks [@nrjdalal](https://github.com/nrjdalal)! - move tw-animate-css to devDependencies
- [#6899](https://github.com/shadcn-ui/ui/pull/6899) [`6f702f5fbf2b82a388e7da6ea08bcc84c2ec19c6`](https://github.com/shadcn-ui/ui/commit/6f702f5fbf2b82a388e7da6ea08bcc84c2ec19c6) Thanks [@justjavac](https://github.com/justjavac)! - add deno support
## 2.4.1
### Patch Changes
- [#7196](https://github.com/shadcn-ui/ui/pull/7196) [`617483fe9c26d607665fcaf79ee26e35d9825d7c`](https://github.com/shadcn-ui/ui/commit/617483fe9c26d607665fcaf79ee26e35d9825d7c) Thanks [@shadcn](https://github.com/shadcn)! - do not throw if empty dir
## 2.4.0
### Minor Changes
- [#6507](https://github.com/shadcn-ui/ui/pull/6507) [`5234c46722750f964d69c92ccbef2c4d260c211d`](https://github.com/shadcn-ui/ui/commit/5234c46722750f964d69c92ccbef2c4d260c211d) Thanks [@shadcn](https://github.com/shadcn)! - add support for TanStack Start
- [#6487](https://github.com/shadcn-ui/ui/pull/6487) [`5ef2bc5f455dfc394116267788c0514b696e13b0`](https://github.com/shadcn-ui/ui/commit/5ef2bc5f455dfc394116267788c0514b696e13b0) Thanks [@shadcn](https://github.com/shadcn)! - add theme vars support
- [#6478](https://github.com/shadcn-ui/ui/pull/6478) [`8f6a64f176defdb1f9c493598d952fb4e9844cd0`](https://github.com/shadcn-ui/ui/commit/8f6a64f176defdb1f9c493598d952fb4e9844cd0) Thanks [@shadcn](https://github.com/shadcn)! - add tailwind version detection
- [#6864](https://github.com/shadcn-ui/ui/pull/6864) [`19665adeeddc4ddb34e91fca219753d15bd46480`](https://github.com/shadcn-ui/ui/commit/19665adeeddc4ddb34e91fca219753d15bd46480) Thanks [@shadcn](https://github.com/shadcn)! - add --base-color flag
- [#6490](https://github.com/shadcn-ui/ui/pull/6490) [`9a14c1d0925d3df2c8f57a3381d212cc3e54f4a6`](https://github.com/shadcn-ui/ui/commit/9a14c1d0925d3df2c8f57a3381d212cc3e54f4a6) Thanks [@shadcn](https://github.com/shadcn)! - add support for tailwind v4
- [#6707](https://github.com/shadcn-ui/ui/pull/6707) [`3db8a07b3f132d396d7fb2e50da96156efcb4138`](https://github.com/shadcn-ui/ui/commit/3db8a07b3f132d396d7fb2e50da96156efcb4138) Thanks [@shadcn](https://github.com/shadcn)! - default to css vars. add --no-css-variables
- [#6968](https://github.com/shadcn-ui/ui/pull/6968) [`205bfc637e093717908ebd2c591b215672950558`](https://github.com/shadcn-ui/ui/commit/205bfc637e093717908ebd2c591b215672950558) Thanks [@shadcn](https://github.com/shadcn)! - replace tailwindcss-animate with tw-animate-css
- [#6574](https://github.com/shadcn-ui/ui/pull/6574) [`1e357cb20d6024b2bc9766fb15f61cb989eb7024`](https://github.com/shadcn-ui/ui/commit/1e357cb20d6024b2bc9766fb15f61cb989eb7024) Thanks [@shadcn](https://github.com/shadcn)! - default for new-york for v4
- [#6515](https://github.com/shadcn-ui/ui/pull/6515) [`d1eb24e23a973646d78cf101fa1e0a22861ac9fd`](https://github.com/shadcn-ui/ui/commit/d1eb24e23a973646d78cf101fa1e0a22861ac9fd) Thanks [@shadcn](https://github.com/shadcn)! - fix handling of sidebar colors
- [#6693](https://github.com/shadcn-ui/ui/pull/6693) [`3740373f99e39943514a45f5808ecb5f17faf700`](https://github.com/shadcn-ui/ui/commit/3740373f99e39943514a45f5808ecb5f17faf700) Thanks [@shadcn](https://github.com/shadcn)! - add oklch base color
- [#6571](https://github.com/shadcn-ui/ui/pull/6571) [`c74a094f14a6e338124709547932dbb20c8d1324`](https://github.com/shadcn-ui/ui/commit/c74a094f14a6e338124709547932dbb20c8d1324) Thanks [@shadcn](https://github.com/shadcn)! - hotswap style for v4
- [#6576](https://github.com/shadcn-ui/ui/pull/6576) [`9f4d65fc8fe72f632706fafd4036f63fd9317780`](https://github.com/shadcn-ui/ui/commit/9f4d65fc8fe72f632706fafd4036f63fd9317780) Thanks [@shadcn](https://github.com/shadcn)! - add warning for deprecated components
- [#6811](https://github.com/shadcn-ui/ui/pull/6811) [`bc7df68620f242ce6aa640839c80ddc8afc7e091`](https://github.com/shadcn-ui/ui/commit/bc7df68620f242ce6aa640839c80ddc8afc7e091) Thanks [@shadcn](https://github.com/shadcn)! - add support for route install for react-router and laravel
- [#7016](https://github.com/shadcn-ui/ui/pull/7016) [`b3b2fe2755e0ec1271c41a2a61b1a6933af42bc6`](https://github.com/shadcn-ui/ui/commit/b3b2fe2755e0ec1271c41a2a61b1a6933af42bc6) Thanks [@shadcn](https://github.com/shadcn)! - add theme prop to registry-item schema
### Patch Changes
- [#6724](https://github.com/shadcn-ui/ui/pull/6724) [`a3fe5074c1375cbd92e1ccdaab38d6808bfec696`](https://github.com/shadcn-ui/ui/commit/a3fe5074c1375cbd92e1ccdaab38d6808bfec696) Thanks [@Kaikaikaifang](https://github.com/Kaikaikaifang)! - support for version detection in monorepo
- [#6414](https://github.com/shadcn-ui/ui/pull/6414) [`202131cd7bf8829b962ae4027545afbdfe79e688`](https://github.com/shadcn-ui/ui/commit/202131cd7bf8829b962ae4027545afbdfe79e688) Thanks [@palmithor](https://github.com/palmithor)! - upgrade @antfu/ni
- [#6965](https://github.com/shadcn-ui/ui/pull/6965) [`69fc8e23cc0631aac6b708ba0481509f1125d3d7`](https://github.com/shadcn-ui/ui/commit/69fc8e23cc0631aac6b708ba0481509f1125d3d7) Thanks [@jherr](https://github.com/jherr)! - allow silent mode with npm
- [#6814](https://github.com/shadcn-ui/ui/pull/6814) [`8539dd6eec948e7a6218c7ca3372d2b1f349d7c0`](https://github.com/shadcn-ui/ui/commit/8539dd6eec948e7a6218c7ca3372d2b1f349d7c0) Thanks [@shadcn](https://github.com/shadcn)! - do not add ring for v3
- [#6732](https://github.com/shadcn-ui/ui/pull/6732) [`839afa714f61e2c0f83e9417354ea6ba8246c177`](https://github.com/shadcn-ui/ui/commit/839afa714f61e2c0f83e9417354ea6ba8246c177) Thanks [@shadcn](https://github.com/shadcn)! - cache registry calls
- [#6863](https://github.com/shadcn-ui/ui/pull/6863) [`c16c58d0f9e672edddd554269fdd0eb0d412cb9d`](https://github.com/shadcn-ui/ui/commit/c16c58d0f9e672edddd554269fdd0eb0d412cb9d) Thanks [@shadcn](https://github.com/shadcn)! - add --template flag
- [#6721](https://github.com/shadcn-ui/ui/pull/6721) [`a5122f9029c91963f493e7348ef7681dad4834e5`](https://github.com/shadcn-ui/ui/commit/a5122f9029c91963f493e7348ef7681dad4834e5) Thanks [@shadcn](https://github.com/shadcn)! - do not overwrite user defined vars
- [#6530](https://github.com/shadcn-ui/ui/pull/6530) [`d6159023ed0817adf14b4398874b1f5f05a73b02`](https://github.com/shadcn-ui/ui/commit/d6159023ed0817adf14b4398874b1f5f05a73b02) Thanks [@zwarunek](https://github.com/zwarunek)! - fix cn import bug in monorepo
- [#6617](https://github.com/shadcn-ui/ui/pull/6617) [`bd8533bd265de7765831d398f4db687483a0e53a`](https://github.com/shadcn-ui/ui/commit/bd8533bd265de7765831d398f4db687483a0e53a) Thanks [@shadcn](https://github.com/shadcn)! - filter out deprecated from --all
- [#6733](https://github.com/shadcn-ui/ui/pull/6733) [`779517a1d46f567c8e3fa8fcdea4c75c65ad4eb4`](https://github.com/shadcn-ui/ui/commit/779517a1d46f567c8e3fa8fcdea4c75c65ad4eb4) Thanks [@shadcn](https://github.com/shadcn)! - check for empty css vars
- [#6967](https://github.com/shadcn-ui/ui/pull/6967) [`9eae13639c10f0219872b5fd28f523a4c25f40df`](https://github.com/shadcn-ui/ui/commit/9eae13639c10f0219872b5fd28f523a4c25f40df) Thanks [@shadcn](https://github.com/shadcn)! - only show deprecated for new projects
- [#6590](https://github.com/shadcn-ui/ui/pull/6590) [`16d4d38f564c75d4187977275f04d5999ee9e2f4`](https://github.com/shadcn-ui/ui/commit/16d4d38f564c75d4187977275f04d5999ee9e2f4) Thanks [@prateekkumarweb](https://github.com/prateekkumarweb)! - fix tanstack check
## 2.2.0
### Minor Changes

View File

@@ -1,6 +1,6 @@
{
"name": "shadcn",
"version": "2.3.0",
"version": "2.5.0",
"description": "Add components to your apps.",
"publishConfig": {
"access": "public"

View File

@@ -1,7 +1,8 @@
import path from "path"
import { runInit } from "@/src/commands/init"
import { preFlightAdd } from "@/src/preflights/preflight-add"
import { getRegistryIndex } from "@/src/registry/api"
import { getRegistryIndex, getRegistryItem, isUrl } from "@/src/registry/api"
import { registryItemTypeSchema } from "@/src/registry/schema"
import { addComponents } from "@/src/utils/add-components"
import { createProject } from "@/src/utils/create-project"
import * as ERRORS from "@/src/utils/errors"
@@ -78,23 +79,31 @@ export const add = new Command()
...opts,
})
// Confirm if user is installing themes.
// For now, we assume a theme is prefixed with "theme-".
const isTheme = options.components?.some((component) =>
component.includes("theme-")
)
if (!options.yes && isTheme) {
let itemType: z.infer<typeof registryItemTypeSchema> | undefined
if (components.length > 0 && isUrl(components[0])) {
const item = await getRegistryItem(components[0], "")
itemType = item?.type
}
if (
!options.yes &&
(itemType === "registry:style" || itemType === "registry:theme")
) {
logger.break()
const { confirm } = await prompts({
type: "confirm",
name: "confirm",
message: highlighter.warn(
"You are about to install a new theme. \nExisting CSS variables will be overwritten. Continue?"
`You are about to install a new ${itemType.replace(
"registry:",
""
)}. \nExisting CSS variables and components will be overwritten. Continue?`
),
})
if (!confirm) {
logger.break()
logger.log("Theme installation cancelled.")
logger.log(`Installation cancelled.`)
logger.break()
process.exit(1)
}
@@ -148,6 +157,7 @@ export const add = new Command()
isNewProject: false,
srcDir: options.srcDir,
cssVariables: options.cssVariables,
style: "index",
})
}
@@ -179,6 +189,7 @@ export const add = new Command()
isNewProject: true,
srcDir: options.srcDir,
cssVariables: options.cssVariables,
style: "index",
})
shouldUpdateAppIndex =

View File

@@ -4,7 +4,9 @@ import { preFlightInit } from "@/src/preflights/preflight-init"
import {
BASE_COLORS,
getRegistryBaseColors,
getRegistryItem,
getRegistryStyles,
isUrl,
} from "@/src/registry/api"
import { addComponents } from "@/src/utils/add-components"
import { TEMPLATES, createProject } from "@/src/utils/create-project"
@@ -74,6 +76,7 @@ export const initOptionsSchema = z.object({
).join("', '")}'`,
}
),
style: z.string(),
})
export const init = new Command()
@@ -118,9 +121,24 @@ export const init = new Command()
cwd: path.resolve(opts.cwd),
isNewProject: false,
components,
style: "index",
...opts,
})
// We need to check if we're initializing with a new style.
// We fetch the payload of the first item.
// This is okay since the request is cached and deduped.
if (components.length > 0 && isUrl(components[0])) {
const item = await getRegistryItem(components[0], "")
// Skip base color if style.
// We set a default and let the style override it.
if (item?.type === "registry:style") {
options.baseColor = "neutral"
options.style = item.extends ?? "index"
}
}
await runInit(options)
logger.log(
@@ -191,11 +209,15 @@ export async function runInit(
// Add components.
const fullConfig = await resolveConfigPaths(options.cwd, config)
const components = ["index", ...(options.components || [])]
const components = [
...(options.style === "none" ? [] : [options.style]),
...(options.components ?? []),
]
await addComponents(components, fullConfig, {
// Init will always overwrite files.
overwrite: true,
silent: options.silent,
style: options.style,
isNewProject:
options.isNewProject || projectInfo?.framework.name === "next-app",
})

View File

@@ -98,7 +98,7 @@ export async function migrateIcons(config: Config) {
}
if (targetLibrary.package) {
await updateDependencies([targetLibrary.package], config, {
await updateDependencies([targetLibrary.package], [], config, {
silent: false,
})
}

View File

@@ -289,6 +289,14 @@ export async function registryResolveItemsTree(
}
}
// Sort the payload so that registry:theme is always first.
payload.sort((a, b) => {
if (a.type === "registry:theme") {
return -1
}
return 1
})
let tailwind = {}
payload.forEach((item) => {
tailwind = deepmerge(tailwind, item.tailwind ?? {})
@@ -299,6 +307,11 @@ export async function registryResolveItemsTree(
cssVars = deepmerge(cssVars, item.cssVars ?? {})
})
let css = {}
payload.forEach((item) => {
css = deepmerge(css, item.css ?? {})
})
let docs = ""
payload.forEach((item) => {
if (item.docs) {
@@ -316,6 +329,7 @@ export async function registryResolveItemsTree(
files: deepmerge.all(payload.map((item) => item.files ?? [])),
tailwind,
cssVars,
css,
docs,
})
} catch (error) {
@@ -396,6 +410,7 @@ export async function registryGetTheme(name: string, config: Config) {
},
},
cssVars: {
theme: {},
light: {
radius: "0.5rem",
},
@@ -406,9 +421,13 @@ export async function registryGetTheme(name: string, config: Config) {
if (config.tailwind.cssVariables) {
theme.tailwind.config.theme.extend.colors = {
...theme.tailwind.config.theme.extend.colors,
...buildTailwindThemeColorsFromCssVars(baseColor.cssVars.dark),
...buildTailwindThemeColorsFromCssVars(baseColor.cssVars.dark ?? {}),
}
theme.cssVars = {
theme: {
...baseColor.cssVars.theme,
...theme.cssVars.theme,
},
light: {
...baseColor.cssVars.light,
...theme.cssVars.light,
@@ -421,6 +440,10 @@ export async function registryGetTheme(name: string, config: Config) {
if (tailwindVersion === "v4" && baseColor.cssVarsV4) {
theme.cssVars = {
theme: {
...baseColor.cssVarsV4.theme,
...theme.cssVars.theme,
},
light: {
radius: "0.625rem",
...baseColor.cssVarsV4.light,
@@ -450,7 +473,7 @@ function getRegistryUrl(path: string) {
return `${REGISTRY_URL}/${path}`
}
function isUrl(path: string) {
export function isUrl(path: string) {
try {
new URL(path)
return true

View File

@@ -11,11 +11,11 @@ export const registryItemTypeSchema = z.enum([
"registry:hook",
"registry:page",
"registry:file",
"registry:theme",
"registry:style",
// Internal use only
"registry:theme",
"registry:example",
"registry:style",
"registry:internal",
])
@@ -46,12 +46,27 @@ export const registryItemTailwindSchema = z.object({
})
export const registryItemCssVarsSchema = z.object({
theme: z.record(z.string(), z.string()).optional(),
light: z.record(z.string(), z.string()).optional(),
dark: z.record(z.string(), z.string()).optional(),
})
export const registryItemCssSchema = z.record(
z.string(),
z.lazy(() =>
z.union([
z.string(),
z.record(
z.string(),
z.union([z.string(), z.record(z.string(), z.string())])
),
])
)
)
export const registryItemSchema = z.object({
$schema: z.string().optional(),
extends: z.string().optional(),
name: z.string(),
type: registryItemTypeSchema,
title: z.string().optional(),
@@ -63,6 +78,7 @@ export const registryItemSchema = z.object({
files: z.array(registryItemFileSchema).optional(),
tailwind: registryItemTailwindSchema.optional(),
cssVars: registryItemCssVarsSchema.optional(),
css: registryItemCssSchema.optional(),
meta: z.record(z.string(), z.any()).optional(),
docs: z.string().optional(),
categories: z.array(z.string()).optional(),
@@ -97,16 +113,8 @@ export const registryBaseColorSchema = z.object({
light: z.record(z.string(), z.string()),
dark: z.record(z.string(), z.string()),
}),
cssVars: z.object({
light: z.record(z.string(), z.string()),
dark: z.record(z.string(), z.string()),
}),
cssVarsV4: z
.object({
light: z.record(z.string(), z.string()),
dark: z.record(z.string(), z.string()),
})
.optional(),
cssVars: registryItemCssVarsSchema,
cssVarsV4: registryItemCssVarsSchema.optional(),
inlineColorsTemplate: z.string(),
cssVarsTemplate: z.string(),
})
@@ -117,5 +125,6 @@ export const registryResolvedItemsTreeSchema = registryItemSchema.pick({
files: true,
tailwind: true,
cssVars: true,
css: true,
docs: true,
})

View File

@@ -19,6 +19,7 @@ import { getProjectTailwindVersionFromConfig } from "@/src/utils/get-project-inf
import { handleError } from "@/src/utils/handle-error"
import { logger } from "@/src/utils/logger"
import { spinner } from "@/src/utils/spinner"
import { updateCss } from "@/src/utils/updaters/update-css"
import { updateCssVars } from "@/src/utils/updaters/update-css-vars"
import { updateDependencies } from "@/src/utils/updaters/update-dependencies"
import { updateFiles } from "@/src/utils/updaters/update-files"
@@ -32,12 +33,14 @@ export async function addComponents(
overwrite?: boolean
silent?: boolean
isNewProject?: boolean
style?: string
}
) {
options = {
overwrite: false,
silent: false,
isNewProject: false,
style: "index",
...options,
}
@@ -64,12 +67,14 @@ async function addProjectComponents(
overwrite?: boolean
silent?: boolean
isNewProject?: boolean
style?: string
}
) {
const registrySpinner = spinner(`Checking registry.`, {
silent: options.silent,
})?.start()
const tree = await registryResolveItemsTree(components, config)
if (!tree) {
registrySpinner?.fail()
return handleError(new Error("Failed to fetch components from registry."))
@@ -82,14 +87,23 @@ async function addProjectComponents(
silent: options.silent,
tailwindVersion,
})
const overwriteCssVars = await shouldOverwriteCssVars(components, config)
await updateCssVars(tree.cssVars, config, {
cleanupDefaultNextStyles: options.isNewProject,
silent: options.silent,
tailwindVersion,
tailwindConfig: tree.tailwind?.config,
overwriteCssVars,
initIndex: options.style ? options.style === "index" : false,
})
await updateDependencies(tree.dependencies, config, {
// Add CSS updater
await updateCss(tree.css, config, {
silent: options.silent,
})
await updateDependencies(tree.dependencies, tree.devDependencies, config, {
silent: options.silent,
})
await updateFiles(tree.files, config, {
@@ -111,6 +125,7 @@ async function addWorkspaceComponents(
silent?: boolean
isNewProject?: boolean
isRemote?: boolean
style?: string
}
) {
const registrySpinner = spinner(`Checking registry.`, {
@@ -175,22 +190,39 @@ async function addWorkspaceComponents(
// 2. Update css vars.
if (component.cssVars) {
const overwriteCssVars = await shouldOverwriteCssVars(components, config)
await updateCssVars(component.cssVars, targetConfig, {
silent: true,
tailwindVersion,
tailwindConfig: component.tailwind?.config,
overwriteCssVars,
})
filesUpdated.push(
path.relative(workspaceRoot, targetConfig.resolvedPaths.tailwindCss)
)
}
// 3. Update dependencies.
await updateDependencies(component.dependencies, targetConfig, {
silent: true,
})
// 3. Update CSS
if (component.css) {
await updateCss(component.css, targetConfig, {
silent: true,
})
filesUpdated.push(
path.relative(workspaceRoot, targetConfig.resolvedPaths.tailwindCss)
)
}
// 4. Update files.
// 4. Update dependencies.
await updateDependencies(
component.dependencies,
component.devDependencies,
targetConfig,
{
silent: true,
}
)
// 5. Update files.
const files = await updateFiles(component.files, targetConfig, {
overwrite: options.overwrite,
silent: true,
@@ -271,3 +303,17 @@ async function addWorkspaceComponents(
}
}
}
async function shouldOverwriteCssVars(
components: z.infer<typeof registryItemSchema>["name"][],
config: z.infer<typeof configSchema>
) {
let registryItems = await resolveRegistryItems(components, config)
let result = await fetchRegistry(registryItems)
const payload = z.array(registryItemSchema).parse(result)
return payload.some(
(component) =>
component.type === "registry:theme" || component.type === "registry:style"
)
}

View File

@@ -154,7 +154,7 @@ export async function getTailwindVersion(
cwd: string
): Promise<ProjectInfo["tailwindVersion"]> {
const [packageInfo, config] = await Promise.all([
getPackageInfo(cwd),
getPackageInfo(cwd, false),
getConfig(cwd),
])

View File

@@ -1,6 +1,10 @@
import { Transformer } from "@/src/utils/transformers"
import { SyntaxKind } from "ts-morph"
import {
TailwindVersion,
getProjectTailwindVersionFromConfig,
} from "../get-project-info"
import { splitClassName } from "./transform-css-vars"
export const transformTwPrefixes: Transformer = async ({
@@ -10,6 +14,7 @@ export const transformTwPrefixes: Transformer = async ({
if (!config.tailwind?.prefix) {
return sourceFile
}
const tailwindVersion = await getProjectTailwindVersionFromConfig(config)
// Find the cva function calls.
sourceFile
@@ -23,7 +28,8 @@ export const transformTwPrefixes: Transformer = async ({
defaultClassNames.replaceWithText(
`"${applyPrefix(
defaultClassNames.getText()?.replace(/"|'/g, ""),
config.tailwind.prefix
config.tailwind.prefix,
tailwindVersion
)}"`
)
}
@@ -47,7 +53,8 @@ export const transformTwPrefixes: Transformer = async ({
classNames?.replaceWithText(
`"${applyPrefix(
classNames.getText()?.replace(/"|'/g, ""),
config.tailwind.prefix
config.tailwind.prefix,
tailwindVersion
)}"`
)
}
@@ -66,7 +73,8 @@ export const transformTwPrefixes: Transformer = async ({
value.replaceWithText(
`"${applyPrefix(
value.getText()?.replace(/"|'/g, ""),
config.tailwind.prefix
config.tailwind.prefix,
tailwindVersion
)}"`
)
}
@@ -92,7 +100,8 @@ export const transformTwPrefixes: Transformer = async ({
node.replaceWithText(
`"${applyPrefix(
node.getText()?.replace(/"|'/g, ""),
config.tailwind.prefix
config.tailwind.prefix,
tailwindVersion
)}"`
)
})
@@ -102,7 +111,8 @@ export const transformTwPrefixes: Transformer = async ({
node.replaceWithText(
`"${applyPrefix(
node.getText()?.replace(/"|'/g, ""),
config.tailwind.prefix
config.tailwind.prefix,
tailwindVersion
)}"`
)
}
@@ -131,7 +141,8 @@ export const transformTwPrefixes: Transformer = async ({
node.replaceWithText(
`"${applyPrefix(
node.getText()?.replace(/"|'/g, ""),
config.tailwind.prefix
config.tailwind.prefix,
tailwindVersion
)}"`
)
})
@@ -141,7 +152,8 @@ export const transformTwPrefixes: Transformer = async ({
arg.replaceWithText(
`"${applyPrefix(
arg.getText()?.replace(/"|'/g, ""),
config.tailwind.prefix
config.tailwind.prefix,
tailwindVersion
)}"`
)
}
@@ -156,7 +168,8 @@ export const transformTwPrefixes: Transformer = async ({
classNames.replaceWithText(
`"${applyPrefix(
classNames.getText()?.replace(/"|'/g, ""),
config.tailwind.prefix
config.tailwind.prefix,
tailwindVersion
)}"`
)
}
@@ -170,30 +183,49 @@ export const transformTwPrefixes: Transformer = async ({
return sourceFile
}
export function applyPrefix(input: string, prefix: string = "") {
const classNames = input.split(" ")
const prefixed: string[] = []
for (let className of classNames) {
const [variant, value, modifier] = splitClassName(className)
if (variant) {
modifier
? prefixed.push(`${variant}:${prefix}${value}/${modifier}`)
: prefixed.push(`${variant}:${prefix}${value}`)
} else {
modifier
? prefixed.push(`${prefix}${value}/${modifier}`)
: prefixed.push(`${prefix}${value}`)
}
export function applyPrefix(
input: string,
prefix: string = "",
tailwindVersion: TailwindVersion
) {
if (tailwindVersion === "v3") {
return input
.split(" ")
.map((className) => {
const [variant, value, modifier] = splitClassName(className)
if (variant) {
return modifier
? `${variant}:${prefix}${value}/${modifier}`
: `${variant}:${prefix}${value}`
} else {
return modifier
? `${prefix}${value}/${modifier}`
: `${prefix}${value}`
}
})
.join(" ")
}
return prefixed.join(" ")
return input
.split(" ")
.map((className) =>
className.indexOf(`${prefix}:`) === 0
? className
: `${prefix}:${className.trim()}`
)
.join(" ")
}
export function applyPrefixesCss(css: string, prefix: string) {
export function applyPrefixesCss(
css: string,
prefix: string,
tailwindVersion: TailwindVersion
) {
const lines = css.split("\n")
for (let line of lines) {
if (line.includes("@apply")) {
const originalTWCls = line.replace("@apply", "").trim()
const prefixedTwCls = applyPrefix(originalTWCls, prefix)
const prefixedTwCls = applyPrefix(originalTWCls, prefix, tailwindVersion)
css = css.replace(originalTWCls, prefixedTwCls)
}
}

View File

@@ -20,6 +20,8 @@ export async function updateCssVars(
config: Config,
options: {
cleanupDefaultNextStyles?: boolean
overwriteCssVars?: boolean
initIndex?: boolean
silent?: boolean
tailwindVersion?: TailwindVersion
tailwindConfig?: z.infer<typeof registryItemTailwindSchema>["config"]
@@ -33,6 +35,8 @@ export async function updateCssVars(
cleanupDefaultNextStyles: false,
silent: false,
tailwindVersion: "v3",
overwriteCssVars: false,
initIndex: true,
...options,
}
const cssFilepath = config.resolvedPaths.tailwindCss
@@ -41,7 +45,7 @@ export async function updateCssVars(
cssFilepath
)
const cssVarsSpinner = spinner(
`Updating ${highlighter.info(cssFilepathRelative)}`,
`Updating CSS variables in ${highlighter.info(cssFilepathRelative)}`,
{
silent: options.silent,
}
@@ -51,6 +55,8 @@ export async function updateCssVars(
cleanupDefaultNextStyles: options.cleanupDefaultNextStyles,
tailwindVersion: options.tailwindVersion,
tailwindConfig: options.tailwindConfig,
overwriteCssVars: options.overwriteCssVars,
initIndex: options.initIndex,
})
await fs.writeFile(cssFilepath, output, "utf8")
cssVarsSpinner.succeed()
@@ -64,16 +70,22 @@ export async function transformCssVars(
cleanupDefaultNextStyles?: boolean
tailwindVersion?: TailwindVersion
tailwindConfig?: z.infer<typeof registryItemTailwindSchema>["config"]
overwriteCssVars?: boolean
initIndex?: boolean
} = {
cleanupDefaultNextStyles: false,
tailwindVersion: "v3",
tailwindConfig: undefined,
overwriteCssVars: false,
initIndex: true,
}
) {
options = {
cleanupDefaultNextStyles: false,
tailwindVersion: "v3",
tailwindConfig: undefined,
overwriteCssVars: false,
initIndex: true,
...options,
}
@@ -91,7 +103,8 @@ export async function transformCssVars(
const packageInfo = getPackageInfo(config.resolvedPaths.cwd)
if (
!packageInfo?.dependencies?.["tailwindcss-animate"] &&
!packageInfo?.devDependencies?.["tailwindcss-animate"]
!packageInfo?.devDependencies?.["tailwindcss-animate"] &&
options.initIndex
) {
plugins.push(addCustomImport({ params: "tw-animate-css" }))
}
@@ -103,7 +116,11 @@ export async function transformCssVars(
plugins.push(cleanupDefaultNextStylesPlugin())
}
plugins.push(updateCssVarsPluginV4(cssVars))
plugins.push(
updateCssVarsPluginV4(cssVars, {
overwriteCssVars: options.overwriteCssVars,
})
)
plugins.push(updateThemePlugin(cssVars))
if (options.tailwindConfig) {
@@ -113,7 +130,7 @@ export async function transformCssVars(
}
}
if (config.tailwind.cssVariables) {
if (config.tailwind.cssVariables && options.initIndex) {
plugins.push(
updateBaseLayerPlugin({ tailwindVersion: options.tailwindVersion })
)
@@ -374,13 +391,51 @@ function addOrUpdateVars(
}
function updateCssVarsPluginV4(
cssVars: z.infer<typeof registryItemCssVarsSchema>
cssVars: z.infer<typeof registryItemCssVarsSchema>,
options: {
overwriteCssVars?: boolean
}
) {
return {
postcssPlugin: "update-css-vars-v4",
Once(root: Root) {
Object.entries(cssVars).forEach(([key, vars]) => {
const selector = key === "light" ? ":root" : `.${key}`
let selector = key === "light" ? ":root" : `.${key}`
if (key === "theme") {
selector = "@theme"
const themeNode = upsertThemeNode(root)
Object.entries(vars).forEach(([key, value]) => {
const prop = `--${key.replace(/^--/, "")}`
const newDecl = postcss.decl({
prop,
value,
raws: { semicolon: true },
})
const existingDecl = themeNode?.nodes?.find(
(node): node is postcss.Declaration =>
node.type === "decl" && node.prop === prop
)
// Only overwrite if overwriteCssVars is true
// i.e for registry:theme and registry:style
// We do not want new components to overwrite existing vars.
// Keep user defined vars.
if (options.overwriteCssVars) {
if (existingDecl) {
existingDecl.replaceWith(newDecl)
} else {
themeNode?.append(newDecl)
}
} else {
if (!existingDecl) {
themeNode?.append(newDecl)
}
}
})
return
}
let ruleNode = root.nodes?.find(
(node): node is Rule =>
@@ -419,11 +474,20 @@ function updateCssVarsPluginV4(
node.type === "decl" && node.prop === prop
)
// Do not override existing declarations.
// We do not want new components to override existing vars.
// Only overwrite if overwriteCssVars is true
// i.e for registry:theme and registry:style
// We do not want new components to overwrite existing vars.
// Keep user defined vars.
if (!existingDecl) {
ruleNode?.append(newDecl)
if (options.overwriteCssVars) {
if (existingDecl) {
existingDecl.replaceWith(newDecl)
} else {
ruleNode?.append(newDecl)
}
} else {
if (!existingDecl) {
ruleNode?.append(newDecl)
}
}
})
})

View File

@@ -0,0 +1,324 @@
import { promises as fs } from "fs"
import path from "path"
import { registryItemCssSchema } from "@/src/registry/schema"
import { Config } from "@/src/utils/get-config"
import { highlighter } from "@/src/utils/highlighter"
import { spinner } from "@/src/utils/spinner"
import postcss from "postcss"
import AtRule from "postcss/lib/at-rule"
import Declaration from "postcss/lib/declaration"
import Root from "postcss/lib/root"
import Rule from "postcss/lib/rule"
import { z } from "zod"
export async function updateCss(
css: z.infer<typeof registryItemCssSchema> | undefined,
config: Config,
options: {
silent?: boolean
}
) {
if (
!config.resolvedPaths.tailwindCss ||
!css ||
Object.keys(css).length === 0
) {
return
}
options = {
silent: false,
...options,
}
const cssFilepath = config.resolvedPaths.tailwindCss
const cssFilepathRelative = path.relative(
config.resolvedPaths.cwd,
cssFilepath
)
const cssSpinner = spinner(
`Updating ${highlighter.info(cssFilepathRelative)}`,
{
silent: options.silent,
}
).start()
const raw = await fs.readFile(cssFilepath, "utf8")
let output = await transformCss(raw, css)
await fs.writeFile(cssFilepath, output, "utf8")
cssSpinner.succeed()
}
export async function transformCss(
input: string,
css: z.infer<typeof registryItemCssSchema>
) {
const plugins = [updateCssPlugin(css)]
const result = await postcss(plugins).process(input, {
from: undefined,
})
let output = result.css
output = output.replace(/\/\* ---break--- \*\//g, "")
output = output.replace(/(\n\s*\n)+/g, "\n\n")
output = output.trimEnd()
return output
}
function updateCssPlugin(css: z.infer<typeof registryItemCssSchema>) {
return {
postcssPlugin: "update-css",
Once(root: Root) {
for (const [selector, properties] of Object.entries(css)) {
if (selector.startsWith("@")) {
// Handle at-rules (@layer, @utility, etc.)
const atRuleMatch = selector.match(/@([a-zA-Z-]+)\s*(.*)/)
if (!atRuleMatch) continue
const [, name, params] = atRuleMatch
// Special handling for keyframes - place them under @theme inline
if (name === "keyframes") {
let themeInline = root.nodes?.find(
(node): node is AtRule =>
node.type === "atrule" &&
node.name === "theme" &&
node.params === "inline"
) as AtRule | undefined
if (!themeInline) {
themeInline = postcss.atRule({
name: "theme",
params: "inline",
raws: { semicolon: true, between: " ", before: "\n" },
})
root.append(themeInline)
root.insertBefore(
themeInline,
postcss.comment({ text: "---break---" })
)
}
const keyframesRule = postcss.atRule({
name: "keyframes",
params,
raws: { semicolon: true, between: " ", before: "\n " },
})
themeInline.append(keyframesRule)
if (typeof properties === "object") {
for (const [step, stepProps] of Object.entries(properties)) {
processRule(keyframesRule, step, stepProps)
}
}
}
// Special handling for utility classes to preserve property values
else if (name === "utility") {
const utilityAtRule = root.nodes?.find(
(node): node is AtRule =>
node.type === "atrule" &&
node.name === name &&
node.params === params
) as AtRule | undefined
if (!utilityAtRule) {
const atRule = postcss.atRule({
name,
params,
raws: { semicolon: true, between: " ", before: "\n" },
})
root.append(atRule)
root.insertBefore(
atRule,
postcss.comment({ text: "---break---" })
)
// Add declarations with their values preserved
if (typeof properties === "object") {
for (const [prop, value] of Object.entries(properties)) {
if (typeof value === "string") {
const decl = postcss.decl({
prop,
value: value,
raws: { semicolon: true, before: "\n " },
})
atRule.append(decl)
} else if (typeof value === "object") {
processRule(atRule, prop, value)
}
}
}
} else {
// Update existing utility class
if (typeof properties === "object") {
for (const [prop, value] of Object.entries(properties)) {
if (typeof value === "string") {
const existingDecl = utilityAtRule.nodes?.find(
(node): node is Declaration =>
node.type === "decl" && node.prop === prop
)
const decl = postcss.decl({
prop,
value: value,
raws: { semicolon: true, before: "\n " },
})
existingDecl
? existingDecl.replaceWith(decl)
: utilityAtRule.append(decl)
} else if (typeof value === "object") {
processRule(utilityAtRule, prop, value)
}
}
}
}
} else {
// Handle other at-rules normally
processAtRule(root, name, params, properties)
}
} else {
// Handle regular CSS rules
processRule(root, selector, properties)
}
}
},
}
}
function processAtRule(
root: Root | AtRule,
name: string,
params: string,
properties: any
) {
// Find or create the at-rule
let atRule = root.nodes?.find(
(node): node is AtRule =>
node.type === "atrule" && node.name === name && node.params === params
) as AtRule | undefined
if (!atRule) {
atRule = postcss.atRule({
name,
params,
raws: { semicolon: true, between: " ", before: "\n" },
})
root.append(atRule)
root.insertBefore(atRule, postcss.comment({ text: "---break---" }))
}
// Process children of this at-rule
if (typeof properties === "object") {
for (const [childSelector, childProps] of Object.entries(properties)) {
if (childSelector.startsWith("@")) {
// Nested at-rule
const nestedMatch = childSelector.match(/@([a-zA-Z-]+)\s*(.*)/)
if (nestedMatch) {
const [, nestedName, nestedParams] = nestedMatch
processAtRule(atRule, nestedName, nestedParams, childProps)
}
} else {
// CSS rule within at-rule
processRule(atRule, childSelector, childProps)
}
}
} else if (typeof properties === "string") {
// Direct string content for the at-rule
try {
// Parse the CSS string with PostCSS
const parsed = postcss.parse(`.temp{${properties}}`)
const tempRule = parsed.first as Rule
if (tempRule && tempRule.nodes) {
// Create a rule for the at-rule if needed
const rule = postcss.rule({
selector: "temp",
raws: { semicolon: true, between: " ", before: "\n " },
})
// Copy all declarations from the temp rule to our actual rule
tempRule.nodes.forEach((node) => {
if (node.type === "decl") {
const clone = node.clone()
clone.raws.before = "\n "
rule.append(clone)
}
})
// Only add the rule if it has declarations
if (rule.nodes?.length) {
atRule.append(rule)
}
}
} catch (error) {
console.error("Error parsing at-rule content:", properties, error)
throw error
}
}
}
function processRule(parent: Root | AtRule, selector: string, properties: any) {
let rule = parent.nodes?.find(
(node): node is Rule => node.type === "rule" && node.selector === selector
) as Rule | undefined
if (!rule) {
rule = postcss.rule({
selector,
raws: { semicolon: true, between: " ", before: "\n " },
})
parent.append(rule)
}
if (typeof properties === "object") {
for (const [prop, value] of Object.entries(properties)) {
if (typeof value === "string") {
const decl = postcss.decl({
prop,
value: value,
raws: { semicolon: true, before: "\n " },
})
// Replace existing property or add new one
const existingDecl = rule.nodes?.find(
(node): node is Declaration =>
node.type === "decl" && node.prop === prop
)
existingDecl ? existingDecl.replaceWith(decl) : rule.append(decl)
} else if (typeof value === "object") {
// Nested selector (including & selectors)
const nestedSelector = prop.startsWith("&")
? selector.replace(/^([^:]+)/, `$1${prop.substring(1)}`)
: prop // Use the original selector for other nested elements
processRule(parent, nestedSelector, value)
}
}
} else if (typeof properties === "string") {
// Direct string content for the rule
try {
// Parse the CSS string with PostCSS
const parsed = postcss.parse(`.temp{${properties}}`)
const tempRule = parsed.first as Rule
if (tempRule && tempRule.nodes) {
// Copy all declarations from the temp rule to our actual rule
tempRule.nodes.forEach((node) => {
if (node.type === "decl") {
const clone = node.clone()
clone.raws.before = "\n "
rule?.append(clone)
}
})
}
} catch (error) {
console.error("Error parsing rule content:", selector, properties, error)
throw error
}
}
}

View File

@@ -9,13 +9,16 @@ import prompts from "prompts"
export async function updateDependencies(
dependencies: RegistryItem["dependencies"],
devDependencies: RegistryItem["devDependencies"],
config: Config,
options: {
silent?: boolean
}
) {
dependencies = Array.from(new Set(dependencies))
if (!dependencies?.length) {
devDependencies = Array.from(new Set(devDependencies))
if (!dependencies?.length && !devDependencies?.length) {
return
}
@@ -59,23 +62,44 @@ export async function updateDependencies(
dependenciesSpinner?.start()
await execa(
packageManager,
[
packageManager === "npm" ? "install" : "add",
...(packageManager === "npm" && flag ? [`--${flag}`] : []),
...dependencies,
],
{
cwd: config.resolvedPaths.cwd,
}
)
if (dependencies?.length) {
await execa(
packageManager,
[
packageManager === "npm" ? "install" : "add",
...(packageManager === "npm" && flag ? [`--${flag}`] : []),
...(packageManager === "deno"
? dependencies.map((dep) => `npm:${dep}`)
: dependencies),
],
{
cwd: config.resolvedPaths.cwd,
}
)
}
if (devDependencies?.length) {
await execa(
packageManager,
[
packageManager === "npm" ? "install" : "add",
...(packageManager === "npm" && flag ? [`--${flag}`] : []),
"-D",
...(packageManager === "deno"
? devDependencies.map((dep) => `npm:${dep}`)
: devDependencies),
],
{
cwd: config.resolvedPaths.cwd,
}
)
}
dependenciesSpinner?.succeed()
}
function isUsingReact19(config: Config) {
const packageInfo = getPackageInfo(config.resolvedPaths.cwd)
const packageInfo = getPackageInfo(config.resolvedPaths.cwd, false)
if (!packageInfo?.dependencies?.react) {
return false

View File

@@ -1,4 +1,5 @@
import { existsSync, promises as fs } from "fs"
import { tmpdir } from "os"
import path, { basename } from "path"
import { getRegistryBaseColor } from "@/src/registry/api"
import { RegistryItem, registryItemFileSchema } from "@/src/registry/schema"
@@ -6,6 +7,7 @@ import { Config } from "@/src/utils/get-config"
import { ProjectInfo, getProjectInfo } from "@/src/utils/get-project-info"
import { highlighter } from "@/src/utils/highlighter"
import { logger } from "@/src/utils/logger"
import { resolveImport } from "@/src/utils/resolve-import"
import { spinner } from "@/src/utils/spinner"
import { transform } from "@/src/utils/transformers"
import { transformCssVars } from "@/src/utils/transformers/transform-css-vars"
@@ -14,6 +16,8 @@ import { transformImport } from "@/src/utils/transformers/transform-import"
import { transformRsc } from "@/src/utils/transformers/transform-rsc"
import { transformTwPrefixes } from "@/src/utils/transformers/transform-tw-prefix"
import prompts from "prompts"
import { Project, ScriptKind } from "ts-morph"
import { loadConfig } from "tsconfig-paths"
import { z } from "zod"
export async function updateFiles(
@@ -50,9 +54,9 @@ export async function updateFiles(
getRegistryBaseColor(config.tailwind.baseColor),
])
const filesCreated = []
const filesUpdated = []
const filesSkipped = []
let filesCreated: string[] = []
let filesUpdated: string[] = []
let filesSkipped: string[] = []
for (const file of files) {
if (!file.content) {
@@ -153,11 +157,25 @@ export async function updateFiles(
: filesCreated.push(path.relative(config.resolvedPaths.cwd, filePath))
}
const allFiles = [...filesCreated, ...filesUpdated, ...filesSkipped]
const updatedFiles = await resolveImports(allFiles, config)
// Let's update filesUpdated with the updated files.
filesUpdated.push(...updatedFiles)
// If a file is in filesCreated and filesUpdated, we should remove it from filesUpdated.
filesUpdated = filesUpdated.filter((file) => !filesCreated.includes(file))
const hasUpdatedFiles = filesCreated.length || filesUpdated.length
if (!hasUpdatedFiles && !filesSkipped.length) {
filesCreatedSpinner?.info("No files updated.")
}
// Remove duplicates.
filesCreated = Array.from(new Set(filesCreated))
filesUpdated = Array.from(new Set(filesUpdated))
filesSkipped = Array.from(new Set(filesSkipped))
if (filesCreated.length) {
filesCreatedSpinner?.succeed(
`Created ${filesCreated.length} ${
@@ -371,3 +389,227 @@ export function resolvePageTarget(
return ""
}
async function resolveImports(filePaths: string[], config: Config) {
const project = new Project({
compilerOptions: {},
})
const projectInfo = await getProjectInfo(config.resolvedPaths.cwd)
const tsConfig = await loadConfig(config.resolvedPaths.cwd)
const updatedFiles = []
if (!projectInfo || tsConfig.resultType === "failed") {
return []
}
for (const filepath of filePaths) {
const resolvedPath = path.resolve(config.resolvedPaths.cwd, filepath)
// Check if the file exists.
if (!existsSync(resolvedPath)) {
continue
}
const content = await fs.readFile(resolvedPath, "utf-8")
const dir = await fs.mkdtemp(path.join(tmpdir(), "shadcn-"))
const sourceFile = project.createSourceFile(
path.join(dir, basename(resolvedPath)),
content,
{
scriptKind: ScriptKind.TSX,
}
)
const importDeclarations = sourceFile.getImportDeclarations()
for (const importDeclaration of importDeclarations) {
const moduleSpecifier = importDeclaration.getModuleSpecifierValue()
// Filter out non-local imports.
if (
projectInfo?.aliasPrefix &&
!moduleSpecifier.startsWith(`${projectInfo.aliasPrefix}/`)
) {
continue
}
// Find the probable import file path.
// This is where we expect to find the file on disk.
const probableImportFilePath = await resolveImport(
moduleSpecifier,
tsConfig
)
if (!probableImportFilePath) {
continue
}
// Find the actual import file path.
// This is the path where the file has been installed.
const resolvedImportFilePath = resolveModuleByProbablePath(
probableImportFilePath,
filePaths,
config
)
if (!resolvedImportFilePath) {
continue
}
// Convert the resolved import file path to an aliased import.
const newImport = toAliasedImport(
resolvedImportFilePath,
config,
projectInfo
)
if (!newImport || newImport === moduleSpecifier) {
continue
}
importDeclaration.setModuleSpecifier(newImport)
// Write the updated content to the file.
await fs.writeFile(resolvedPath, sourceFile.getFullText(), "utf-8")
// Track the updated file.
updatedFiles.push(filepath)
}
}
return updatedFiles
}
/**
* Given an absolute "probable" import path (no ext),
* plus an array of absolute file paths you already know about,
* return 0N matches (best match first), and also check disk for any missing ones.
*/
export function resolveModuleByProbablePath(
probableImportFilePath: string,
files: string[],
config: Config,
extensions: string[] = [".tsx", ".ts", ".js", ".jsx", ".css"]
) {
const cwd = path.normalize(config.resolvedPaths.cwd)
// 1) Build a set of POSIX-normalized, project-relative files
const relativeFiles = files.map((f) => f.split(path.sep).join(path.posix.sep))
const fileSet = new Set(relativeFiles)
// 2) Strip any existing extension off the absolute base path
const extInPath = path.extname(probableImportFilePath)
const hasExt = extInPath !== ""
const absBase = hasExt
? probableImportFilePath.slice(0, -extInPath.length)
: probableImportFilePath
// 3) Compute the project-relative "base" directory for strong matching
const relBaseRaw = path.relative(cwd, absBase)
const relBase = relBaseRaw.split(path.sep).join(path.posix.sep)
// 4) Decide which extensions to try
const tryExts = hasExt ? [extInPath] : extensions
// 5) Collect candidates
const candidates = new Set<string>()
// 5a) Fastpath: [base + ext] and [base/index + ext]
for (const e of tryExts) {
const absCand = absBase + e
const relCand = path.posix.normalize(path.relative(cwd, absCand))
if (fileSet.has(relCand) || existsSync(absCand)) {
candidates.add(relCand)
}
const absIdx = path.join(absBase, `index${e}`)
const relIdx = path.posix.normalize(path.relative(cwd, absIdx))
if (fileSet.has(relIdx) || existsSync(absIdx)) {
candidates.add(relIdx)
}
}
// 5b) Fallback: scan known files by basename
const name = path.basename(absBase)
for (const f of relativeFiles) {
if (tryExts.some((e) => f.endsWith(`/${name}${e}`))) {
candidates.add(f)
}
}
// 6) If no matches, bail
if (candidates.size === 0) return null
// 7) Sort by (1) extension priority, then (2) "strong" base match
const sorted = Array.from(candidates).sort((a, b) => {
// a) extension order
const aExt = path.posix.extname(a)
const bExt = path.posix.extname(b)
const ord = tryExts.indexOf(aExt) - tryExts.indexOf(bExt)
if (ord !== 0) return ord
// b) strong match if path starts with relBase
const aStrong = relBase && a.startsWith(relBase) ? -1 : 1
const bStrong = relBase && b.startsWith(relBase) ? -1 : 1
return aStrong - bStrong
})
// 8) Return the first (best) candidate
return sorted[0]
}
export function toAliasedImport(
filePath: string,
config: Config,
projectInfo: ProjectInfo
): string | null {
const abs = path.normalize(path.join(config.resolvedPaths.cwd, filePath))
// 1⃣ Find the longest matching alias root in resolvedPaths
// e.g. key="ui", root="/…/components/ui" beats key="components"
const matches = Object.entries(config.resolvedPaths)
.filter(
([, root]) => root && abs.startsWith(path.normalize(root + path.sep))
)
.sort((a, b) => b[1].length - a[1].length)
if (matches.length === 0) {
return null
}
const [aliasKey, rootDir] = matches[0]
// 2⃣ Compute the path UNDER that root
let rel = path.relative(rootDir, abs)
// force POSIX-style separators
rel = rel.split(path.sep).join("/") // e.g. "button/index.tsx"
// 3⃣ Strip code-file extensions, keep others (css, json, etc.)
const ext = path.posix.extname(rel)
const codeExts = [".ts", ".tsx", ".js", ".jsx"]
const keepExt = codeExts.includes(ext) ? "" : ext
let noExt = rel.slice(0, rel.length - ext.length)
// 4⃣ Collapse "/index" to its directory
if (noExt.endsWith("/index")) {
noExt = noExt.slice(0, -"/index".length)
}
// 5⃣ Build the aliased path
// config.aliases[aliasKey] is e.g. "@/components/ui"
const aliasBase =
aliasKey === "cwd"
? projectInfo.aliasPrefix
: config.aliases[aliasKey as keyof typeof config.aliases]
if (!aliasBase) {
return null
}
// if noExt is empty (i.e. file was exactly at the root), we import the root
let suffix = noExt === "" ? "" : `/${noExt}`
// Rremove /src from suffix.
// Alias will handle this.
suffix = suffix.replace("/src", "")
// 6⃣ Prepend the prefix from projectInfo (e.g. "@") if needed
// but usually config.aliases already include it.
return `${aliasBase}${suffix}${keepExt}`
}

View File

@@ -1,7 +1,10 @@
import { promises as fs } from "fs"
import { tmpdir } from "os"
import path from "path"
import { registryItemTailwindSchema } from "@/src/registry/schema"
import {
registryItemCssVarsSchema,
registryItemTailwindSchema,
} from "@/src/registry/schema"
import { Config } from "@/src/utils/get-config"
import { TailwindVersion } from "@/src/utils/get-project-info"
import { highlighter } from "@/src/utils/highlighter"
@@ -499,7 +502,7 @@ function parseValue(node: any): any {
}
export function buildTailwindThemeColorsFromCssVars(
cssVars: Record<string, string>
cssVars: z.infer<typeof registryItemCssVarsSchema>
) {
const result: Record<string, any> = {}

View File

@@ -0,0 +1 @@
{}

0
packages/shadcn/test/fixtures/project-deno/deno.lock generated vendored Normal file
View File

View File

@@ -0,0 +1,13 @@
{
"name": "test-cli-npm-project",
"version": "1.0.0",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "npm-project",
"version": "1.0.0",
"license": "MIT"
}
}
}

View File

@@ -0,0 +1,10 @@
{
"name": "test-cli-project-npm",
"version": "1.0.0",
"main": "index.js",
"author": "shadcn",
"license": "MIT",
"dependencies": {
"react": "19.0.0"
}
}

View File

@@ -3,7 +3,7 @@
exports[`transform tailwind prefix 1`] = `
"import * as React from "react"
export function Foo() {
return <div className="tw-bg-background hover:tw-bg-muted tw-text-primary-foreground sm:focus:tw-text-accent-foreground">foo</div>
return <div className="tw:bg-background hover:tw:bg-muted tw:text-primary-foreground sm:focus:tw:text-accent-foreground">foo</div>
}
"
`;
@@ -11,7 +11,7 @@ exports[`transform tailwind prefix 1`] = `
exports[`transform tailwind prefix 2`] = `
"import * as React from "react"
export function Foo() {
return <div className="tw-bg-white hover:tw-bg-stone-100 tw-text-stone-50 sm:focus:tw-text-stone-900 dark:tw-bg-stone-950 dark:hover:tw-bg-stone-800 dark:tw-text-stone-900 dark:sm:focus:tw-text-stone-50">foo</div>
return <div className="tw:bg-white hover:tw:bg-stone-100 tw:text-stone-50 sm:focus:tw:text-stone-900 dark:tw:bg-stone-950 dark:hover:tw:bg-stone-800 dark:tw:text-stone-900 dark:sm:focus:tw:text-stone-50">foo</div>
}
"
`;
@@ -19,7 +19,7 @@ export function Foo() {
exports[`transform tailwind prefix 3`] = `
"import * as React from "react"
export function Foo() {
return <div className={cn("tw-bg-white hover:tw-bg-stone-100 dark:tw-bg-stone-950 dark:hover:tw-bg-stone-800", true && "tw-text-stone-50 sm:focus:tw-text-stone-900 dark:tw-text-stone-900 dark:sm:focus:tw-text-stone-50")}>foo</div>
return <div className={cn("tw:bg-white hover:tw:bg-stone-100 dark:tw:bg-stone-950 dark:hover:tw:bg-stone-800", true && "tw:text-stone-50 sm:focus:tw:text-stone-900 dark:tw:text-stone-900 dark:sm:focus:tw:text-stone-50")}>foo</div>
}
"
`;
@@ -27,7 +27,7 @@ export function Foo() {
exports[`transform tailwind prefix 4`] = `
"import * as React from "react"
export function Foo() {
return <div className={cn("tw-bg-background hover:tw-bg-muted", true && "tw-text-primary-foreground sm:focus:tw-text-accent-foreground")}>foo</div>
return <div className={cn("tw:bg-background hover:tw:bg-muted", true && "tw:text-primary-foreground sm:focus:tw:text-accent-foreground")}>foo</div>
}
"
`;
@@ -105,10 +105,10 @@ exports[`transform tailwind prefix 5`] = `
@layer base {
* {
@apply tw-border-border;
@apply tw::border-border;
}
body {
@apply tw-bg-background tw-text-foreground;
@apply tw::bg-background tw::text-foreground;
}
}"
`;

View File

@@ -2,7 +2,7 @@ import { describe, expect, test } from "vitest"
import { applyPrefix } from "../../src/utils/transformers/transform-tw-prefix"
describe("apply tailwind prefix", () => {
describe("apply tailwind prefix v3", () => {
test.each([
{
input: "bg-slate-800 text-gray-500",
@@ -37,6 +37,45 @@ describe("apply tailwind prefix", () => {
"tw-absolute tw-right-4 tw-top-4 tw-bg-primary tw-rounded-sm tw-opacity-70 tw-ring-offset-background tw-transition-opacity hover:tw-opacity-100 focus:tw-outline-none focus:tw-ring-2 focus:tw-ring-ring focus:tw-ring-offset-2 disabled:tw-pointer-events-none data-[state=open]:tw-bg-secondary",
},
])(`applyTwPrefix($input) -> $output`, ({ input, output }) => {
expect(applyPrefix(input, "tw-")).toBe(output)
expect(applyPrefix(input, "tw-", "v3")).toBe(output)
})
})
describe("apply tailwind prefix v4", () => {
test.each([
{
input: "bg-slate-800 text-gray-500",
output: "tw:bg-slate-800 tw:text-gray-500",
},
{
input: "hover:dark:bg-background dark:text-foreground",
output: "tw:hover:dark:bg-background tw:dark:text-foreground",
},
{
input:
"rounded-lg border border-slate-200 bg-white text-slate-950 shadow-sm dark:border-slate-800 dark:bg-slate-950 dark:text-slate-50",
output:
"tw:rounded-lg tw:border tw:border-slate-200 tw:bg-white tw:text-slate-950 tw:shadow-sm tw:dark:border-slate-800 tw:dark:bg-slate-950 tw:dark:text-slate-50",
},
{
input:
"text-red-500 border-red-500/50 dark:border-red-500 [&>svg]:text-red-500 text-red-500 dark:text-red-900 dark:border-red-900/50 dark:dark:border-red-900 dark:[&>svg]:text-red-900 dark:text-red-900",
output:
"tw:text-red-500 tw:border-red-500/50 tw:dark:border-red-500 tw:[&>svg]:text-red-500 tw:text-red-500 tw:dark:text-red-900 tw:dark:border-red-900/50 tw:dark:dark:border-red-900 tw:dark:[&>svg]:text-red-900 tw:dark:text-red-900",
},
{
input:
"flex h-full w-full items-center justify-center rounded-full bg-muted",
output:
"tw:flex tw:h-full tw:w-full tw:items-center tw:justify-center tw:rounded-full tw:bg-muted",
},
{
input:
"absolute right-4 top-4 bg-primary rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-secondary",
output:
"tw:absolute tw:right-4 tw:top-4 tw:bg-primary tw:rounded-sm tw:opacity-70 tw:ring-offset-background tw:transition-opacity tw:hover:opacity-100 tw:focus:outline-none tw:focus:ring-2 tw:focus:ring-ring tw:focus:ring-offset-2 tw:disabled:pointer-events-none tw:data-[state=open]:bg-secondary",
},
])(`applyTwPrefix($input) -> $output`, ({ input, output }) => {
expect(applyPrefix(input, "tw", "v4")).toBe(output)
})
})

View File

@@ -2,11 +2,13 @@
exports[`registryResolveItemTree > should resolve index 1`] = `
{
"css": {},
"cssVars": {
"dark": {},
"light": {
"radius": "0.5rem",
},
"theme": {},
},
"dependencies": [
"tailwindcss-animate",
@@ -86,6 +88,7 @@ export { Label }
exports[`registryResolveItemTree > should resolve items tree 1`] = `
{
"css": {},
"cssVars": {},
"dependencies": [
"@radix-ui/react-slot",
@@ -163,10 +166,11 @@ export { Button, buttonVariants }
exports[`registryResolveItemTree > should resolve multiple items tree 1`] = `
{
"css": {},
"cssVars": {},
"dependencies": [
"@radix-ui/react-slot",
"cmdk@1.0.0",
"cmdk",
"@radix-ui/react-dialog",
],
"devDependencies": [],

View File

@@ -16,7 +16,7 @@ test("transform tailwind prefix", async () => {
config: {
tailwind: {
baseColor: "stone",
prefix: "tw-",
prefix: "tw:",
},
aliases: {
components: "@/components",
@@ -39,7 +39,7 @@ export function Foo() {
tailwind: {
baseColor: "stone",
cssVariables: false,
prefix: "tw-",
prefix: "tw:",
},
aliases: {
components: "@/components",
@@ -62,7 +62,7 @@ export function Foo() {
tailwind: {
baseColor: "stone",
cssVariables: false,
prefix: "tw-",
prefix: "tw:",
},
aliases: {
components: "@/components",
@@ -85,7 +85,7 @@ export function Foo() {
tailwind: {
baseColor: "stone",
cssVariables: false,
prefix: "tw-",
prefix: "tw:",
},
aliases: {
components: "@/components",
@@ -99,7 +99,8 @@ export function Foo() {
expect(
applyPrefixesCss(
"@tailwind base;\n@tailwind components;\n@tailwind utilities;\n \n@layer base {\n :root {\n --background: 0 0% 100%;\n --foreground: 224 71.4% 4.1%;\n \n --muted: 220 14.3% 95.9%;\n --muted-foreground: 220 8.9% 46.1%;\n \n --popover: 0 0% 100%;\n --popover-foreground: 224 71.4% 4.1%;\n \n --card: 0 0% 100%;\n --card-foreground: 224 71.4% 4.1%;\n \n --border: 220 13% 91%;\n --input: 220 13% 91%;\n \n --primary: 220.9 39.3% 11%;\n --primary-foreground: 210 20% 98%;\n \n --secondary: 220 14.3% 95.9%;\n --secondary-foreground: 220.9 39.3% 11%;\n \n --accent: 220 14.3% 95.9%;\n --accent-foreground: 220.9 39.3% 11%;\n \n --destructive: 0 84.2% 60.2%;\n --destructive-foreground: 210 20% 98%;\n \n --ring: 217.9 10.6% 64.9%;\n \n --radius: 0.5rem;\n }\n \n .dark {\n --background: 224 71.4% 4.1%;\n --foreground: 210 20% 98%;\n \n --muted: 215 27.9% 16.9%;\n --muted-foreground: 217.9 10.6% 64.9%;\n \n --popover: 224 71.4% 4.1%;\n --popover-foreground: 210 20% 98%;\n \n --card: 224 71.4% 4.1%;\n --card-foreground: 210 20% 98%;\n \n --border: 215 27.9% 16.9%;\n --input: 215 27.9% 16.9%;\n \n --primary: 210 20% 98%;\n --primary-foreground: 220.9 39.3% 11%;\n \n --secondary: 215 27.9% 16.9%;\n --secondary-foreground: 210 20% 98%;\n \n --accent: 215 27.9% 16.9%;\n --accent-foreground: 210 20% 98%;\n \n --destructive: 0 62.8% 30.6%;\n --destructive-foreground: 0 85.7% 97.3%;\n \n --ring: 215 27.9% 16.9%;\n }\n}\n \n@layer base {\n * {\n @apply border-border;\n }\n body {\n @apply bg-background text-foreground;\n }\n}",
"tw-"
"tw:",
"v4"
)
).toMatchSnapshot()
})

View File

@@ -309,6 +309,12 @@ describe("transformCssVarsV4", () => {
}
`,
{
theme: {
"font-poppins": "Poppins, sans-serif",
"breakpoint-3xl": "120rem",
"shadow-2xs": "0px 1px 2px 0px rgba(0, 0, 0, 0.05)",
"animate-bounce": "bounce 1s infinite",
},
light: {
background: "215 20.2% 65.1%",
foreground: "222.2 84% 4.9%",
@@ -340,6 +346,152 @@ describe("transformCssVarsV4", () => {
@theme inline {
--color-background: var(--background);
--font-poppins: Poppins, sans-serif;
--breakpoint-3xl: 120rem;
--shadow-2xs: 0px 1px 2px 0px rgba(0, 0, 0, 0.05);
--animate-bounce: bounce 1s infinite;
--color-primary: var(--primary);
--color-foreground: var(--foreground);
}
@layer base {
* {
@apply border-border outline-ring/50;
}
body {
@apply bg-background text-foreground;
}
}
"
`)
})
test("should NOT override theme vars if overwriteCssVars is false", async () => {
expect(
await transformCssVars(
`@import "tailwindcss";
:root {
--background: hsl(210 40% 98%);
}
.dark {
--background: hsl(222.2 84% 4.9%);
}
@theme inline {
--color-background: var(--background);
--font-sans: Inter, sans-serif;
}
`,
{
theme: {
"font-sans": "Poppins, sans-serif",
"breakpoint-3xl": "120rem",
},
light: {
background: "215 20.2% 65.1%",
foreground: "222.2 84% 4.9%",
primary: "215 20.2% 65.1%",
},
dark: {
foreground: "60 9.1% 97.8%",
primary: "222.2 84% 4.9%",
},
},
{ tailwind: { cssVariables: true } },
{ tailwindVersion: "v4" }
)
).toMatchInlineSnapshot(`
"@import "tailwindcss";
@custom-variant dark (&:is(.dark *));
:root {
--background: hsl(210 40% 98%);
--foreground: hsl(222.2 84% 4.9%);
--primary: hsl(215 20.2% 65.1%);
}
.dark {
--background: hsl(222.2 84% 4.9%);
--foreground: hsl(60 9.1% 97.8%);
--primary: hsl(222.2 84% 4.9%);
}
@theme inline {
--color-background: var(--background);
--font-sans: Inter, sans-serif;
--breakpoint-3xl: 120rem;
--color-primary: var(--primary);
--color-foreground: var(--foreground);
}
@layer base {
* {
@apply border-border outline-ring/50;
}
body {
@apply bg-background text-foreground;
}
}
"
`)
})
test("should override theme vars if overwriteCssVars is true", async () => {
expect(
await transformCssVars(
`@import "tailwindcss";
:root {
--background: hsl(210 40% 98%);
}
.dark {
--background: hsl(222.2 84% 4.9%);
}
@theme inline {
--color-background: var(--background);
--font-sans: Inter, sans-serif;
}
`,
{
theme: {
"font-sans": "Poppins, sans-serif",
"breakpoint-3xl": "120rem",
},
light: {
background: "215 20.2% 65.1%",
foreground: "222.2 84% 4.9%",
primary: "215 20.2% 65.1%",
},
dark: {
foreground: "60 9.1% 97.8%",
primary: "222.2 84% 4.9%",
},
},
{ tailwind: { cssVariables: true } },
{ tailwindVersion: "v4", overwriteCssVars: true }
)
).toMatchInlineSnapshot(`
"@import "tailwindcss";
@custom-variant dark (&:is(.dark *));
:root {
--background: hsl(215 20.2% 65.1%);
--foreground: hsl(222.2 84% 4.9%);
--primary: hsl(215 20.2% 65.1%);
}
.dark {
--background: hsl(222.2 84% 4.9%);
--foreground: hsl(60 9.1% 97.8%);
--primary: hsl(222.2 84% 4.9%);
}
@theme inline {
--color-background: var(--background);
--font-sans: Poppins, sans-serif;
--breakpoint-3xl: 120rem;
--color-primary: var(--primary);
--color-foreground: var(--foreground);
}

View File

@@ -0,0 +1,331 @@
import { describe, expect, test } from "vitest"
import { transformCss } from "../../../src/utils/updaters/update-css"
describe("transformCss", () => {
test("should add utility classes", async () => {
const input = `@import "tailwindcss";`
const result = await transformCss(input, {
"@utility content-auto": {
"content-visibility": "auto",
},
})
expect(result).toMatchInlineSnapshot(`
"@import "tailwindcss";
@utility content-auto {
content-visibility: auto;
}"
`)
})
test("should add utility classes with pseudo-selectors", async () => {
const input = `@import "tailwindcss";`
const result = await transformCss(input, {
"@utility scrollbar-hidden": {
"&::-webkit-scrollbar": {
display: "none",
},
},
})
expect(result).toMatchInlineSnapshot(`
"@import "tailwindcss";
@utility scrollbar-hidden {
&::-webkit-scrollbar {
display: none;
}
}"
`)
})
test("should add parameterized utility classes", async () => {
const input = `@import "tailwindcss";`
const result = await transformCss(input, {
"@utility tab-*": {
"tab-size": "--value([integer])",
},
})
expect(result).toMatchInlineSnapshot(`
"@import "tailwindcss";
@utility tab-* {
tab-size: --value([integer]);
}"
`)
})
test("should add component styles", async () => {
const input = `@tailwind base;
@tailwind components;
@tailwind utilities;`
const result = await transformCss(input, {
"@layer components": {
".card": {
"background-color": "var(--color-white)",
"border-radius": "var(--rounded-lg)",
padding: "var(--spacing-6)",
"box-shadow": "var(--shadow-xl)",
},
},
})
expect(result).toMatchInlineSnapshot(`
"@tailwind base;
@tailwind components;
@tailwind utilities;
@layer components {
.card {
background-color: var(--color-white);
border-radius: var(--rounded-lg);
padding: var(--spacing-6);
box-shadow: var(--shadow-xl);
}
}"
`)
})
test("should add base styles", async () => {
const input = `@tailwind base;
@tailwind components;
@tailwind utilities;`
const result = await transformCss(input, {
"@layer base": {
h1: {
"font-size": "var(--text-2xl)",
},
h2: {
"font-size": "var(--text-xl)",
},
},
})
expect(result).toMatchInlineSnapshot(`
"@tailwind base;
@tailwind components;
@tailwind utilities;
@layer base {
h1 {
font-size: var(--text-2xl);
}
h2 {
font-size: var(--text-xl);
}
}"
`)
})
test("should update existing rules", async () => {
const input = `@import "tailwindcss";
@layer components {
.card {
background-color: white;
padding: 1rem;
}
}`
const result = await transformCss(input, {
"@layer components": {
".card": {
"background-color": "var(--color-white)",
"border-radius": "var(--rounded-lg)",
"box-shadow": "var(--shadow-xl)",
},
},
})
expect(result).toMatchInlineSnapshot(`
"@import "tailwindcss";
@layer components {
.card {
background-color: var(--color-white);
padding: 1rem;
border-radius: var(--rounded-lg);
box-shadow: var(--shadow-xl);
}
}"
`)
})
test("should add multiple rules and types", async () => {
const input = `@tailwind base;
@tailwind components;
@tailwind utilities;`
const result = await transformCss(input, {
"@utility content-auto": {
"content-visibility": "auto",
},
"@layer components": {
".card": {
"background-color": "var(--color-white)",
"border-radius": "var(--rounded-lg)",
},
},
"@layer base": {
h1: {
"font-size": "var(--text-2xl)",
},
},
})
expect(result).toMatchInlineSnapshot(`
"@tailwind base;
@tailwind components;
@tailwind utilities;
@utility content-auto {
content-visibility: auto;
}
@layer components {
.card {
background-color: var(--color-white);
border-radius: var(--rounded-lg);
}
}
@layer base {
h1 {
font-size: var(--text-2xl);
}
}"
`)
})
test("should handle nested selectors with &", async () => {
const input = `@tailwind base;
@tailwind components;
@tailwind utilities;`
const result = await transformCss(input, {
"@layer components": {
".button": {
"background-color": "var(--color-primary)",
"&:hover": {
"background-color": "var(--color-primary-dark)",
},
"&:active": {
"background-color": "var(--color-primary-darker)",
},
},
},
})
expect(result).toMatchInlineSnapshot(`
"@tailwind base;
@tailwind components;
@tailwind utilities;
@layer components {
.button {
background-color: var(--color-primary);
}
.button:hover {
background-color: var(--color-primary-dark);
}
.button:active {
background-color: var(--color-primary-darker);
}
}"
`)
})
test("should handle direct string content", async () => {
const input = `@tailwind base;
@tailwind components;
@tailwind utilities;`
const result = await transformCss(input, {
"@layer base": {
body: "font-family: var(--font-sans); line-height: 1.5;",
},
})
expect(result).toMatchInlineSnapshot(`
"@tailwind base;
@tailwind components;
@tailwind utilities;
@layer base {
body {
font-family: var(--font-sans);
line-height: 1.5;
}
}"
`)
})
test("should handle nested at-rules", async () => {
const input = `@tailwind base;
@tailwind components;
@tailwind utilities;`
const result = await transformCss(input, {
"@layer components": {
"@media (min-width: 768px)": {
".card": {
padding: "2rem",
},
},
},
})
expect(result).toMatchInlineSnapshot(`
"@tailwind base;
@tailwind components;
@tailwind utilities;
@layer components {
@media (min-width: 768px) {
.card {
padding: 2rem;
}
}
}"
`)
})
test("should place keyframes under @theme inline directive", async () => {
const input = `@import "tailwindcss";`
const result = await transformCss(input, {
"@keyframes spin": {
"0%": {
transform: "rotate(0deg)",
},
"100%": {
transform: "rotate(360deg)",
},
},
})
expect(result).toMatchInlineSnapshot(`
"@import "tailwindcss";
@theme inline {
@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
}"
`)
})
})

View File

@@ -0,0 +1,139 @@
import { vi, describe, afterEach, test, expect } from "vitest"
import { execa } from "execa"
import prompts from "prompts"
import { updateDependencies } from "../../../src/utils/updaters/update-dependencies"
import path from "path"
vi.mock("execa")
vi.mock("prompts")
describe("updateDependencies", () => {
afterEach(() => {
vi.restoreAllMocks()
})
test.each([
{
description: "npm without react 19 includes no additional flags",
options: { silent: true },
dependencies: ["first", "second", "third"],
devDependencies: ["fourth"],
config: {
resolvedPaths: {
cwd: path.resolve(__dirname, "../../fixtures/project-npm")
}
},
expectedPackageManager: "npm",
expectedArgs: ["install", "first", "second", "third"],
expectedDevArgs: ["install", "-D", "fourth"]
},
{
description: "npm with react 19 applies force prompt when silent",
options: { silent: true },
dependencies: ["first", "second", "third"],
devDependencies: ["fourth"],
config: {
resolvedPaths: {
cwd: path.resolve(__dirname, "../../fixtures/project-npm-react19")
}
},
expectedPackageManager: "npm",
expectedArgs: ["install", "--force", "first", "second", "third"],
expectedDevArgs: ["install", "--force", "-D", "fourth"]
},
{
description: "npm with react 19 prompts for flag when not silent",
flagPrompt: "legacy-peer-deps",
dependencies: ["first", "second", "third"],
devDependencies: ["fourth"],
config: {
resolvedPaths: {
cwd: path.resolve(__dirname, "../../fixtures/project-npm-react19")
}
},
expectedPackageManager: "npm",
expectedArgs: ["install", "--legacy-peer-deps", "first", "second", "third"],
expectedDevArgs: ["install", "--legacy-peer-deps", "-D", "fourth"]
},
{
description: "deno uses npm: package prefix",
dependencies: ["first", "second", "third"],
devDependencies: ["fourth"],
config: {
resolvedPaths: {
cwd: path.resolve(__dirname, "../../fixtures/project-deno")
}
},
expectedPackageManager: "deno",
expectedArgs: ["add", "npm:first", "npm:second", "npm:third"],
expectedDevArgs: ["add", "-D", "npm:fourth"]
},
{
description: "bun uses bun",
dependencies: ["first", "second", "third"],
devDependencies: ["fourth"],
config: {
resolvedPaths: {
cwd: path.resolve(__dirname, "../../fixtures/project-bun")
}
},
expectedPackageManager: "bun",
expectedArgs: ["add", "first", "second", "third"],
expectedDevArgs: ["add", "-D", "fourth"]
},
{
description: "pnpm uses pnpm",
dependencies: ["first", "second", "third"],
devDependencies: ["fourth"],
config: {
resolvedPaths: {
cwd: path.resolve(__dirname, "../../fixtures/project-pnpm")
}
},
expectedPackageManager: "pnpm",
expectedArgs: ["add", "first", "second", "third"],
expectedDevArgs: ["add", "-D", "fourth"]
},
{
description: "deduplicates input dependencies",
options: { silent: true },
dependencies: ["first", "first"],
devDependencies: ["second", "second"],
config: {
resolvedPaths: {
cwd: path.resolve(__dirname, "../../fixtures/project-npm")
}
},
expectedPackageManager: "npm",
expectedArgs: ["install", "first"],
expectedDevArgs: ["install", "-D", "second"]
}
])("$description", async ({ options, flagPrompt, config, dependencies, devDependencies, expectedPackageManager, expectedArgs, expectedDevArgs }) => {
vi.mocked(prompts).mockResolvedValue({ flag: flagPrompt })
await updateDependencies(
dependencies,
devDependencies,
config,
options ?? {}
)
if (flagPrompt) {
expect(prompts).toHaveBeenCalled()
}
expect(execa).toHaveBeenCalledWith(
expectedPackageManager,
expectedArgs,
{ cwd: config?.resolvedPaths.cwd }
)
expect(execa).toHaveBeenCalledWith(
expectedPackageManager,
expectedDevArgs,
{ cwd: config?.resolvedPaths.cwd }
)
})
})

View File

@@ -1,3 +1,4 @@
import { existsSync } from "fs"
import path from "path"
import { afterAll, afterEach, describe, expect, test, vi } from "vitest"
@@ -5,7 +6,9 @@ import { getConfig } from "../../../src/utils/get-config"
import {
findCommonRoot,
resolveFilePath,
resolveModuleByProbablePath,
resolveNestedFilePath,
toAliasedImport,
updateFiles,
} from "../../../src/utils/updaters/update-files"
@@ -809,3 +812,340 @@ return <div>Hello World</div>
`)
})
})
describe("resolveModuleByProbablePath", () => {
test("should resolve exact file match in provided files list", () => {
const files = [
"components/button.tsx",
"components/card.tsx",
"lib/utils.ts",
]
const config = {
resolvedPaths: {
cwd: "/foo/bar",
},
}
expect(
resolveModuleByProbablePath("/foo/bar/components/button", files, config)
).toBe("components/button.tsx")
})
test("should resolve index file", () => {
const files = ["components/button/index.tsx", "components/card.tsx"]
const config = {
resolvedPaths: {
cwd: "/foo/bar",
},
}
expect(
resolveModuleByProbablePath("/foo/bar/components/button", files, config)
).toBe("components/button/index.tsx")
})
test("should try different extensions", () => {
const files = ["components/button.jsx", "components/card.tsx"]
const config = {
resolvedPaths: {
cwd: "/foo/bar",
},
}
expect(
resolveModuleByProbablePath("/foo/bar/components/button", files, config)
).toBe("components/button.jsx")
})
test("should fallback to basename matching", () => {
const files = ["components/ui/button.tsx", "components/card.tsx"]
const config = {
resolvedPaths: {
cwd: "/foo/bar",
},
}
expect(
resolveModuleByProbablePath("/foo/bar/components/button", files, config)
).toBe("components/ui/button.tsx")
})
test("should return null when file not found", () => {
const files = ["components/card.tsx", "lib/utils.ts"]
const config = {
resolvedPaths: {
cwd: "/foo/bar",
},
}
expect(
resolveModuleByProbablePath("/foo/bar/components/button", files, config)
).toBeNull()
})
test("should sort by extension priority", () => {
const files = [
"components/button.jsx",
"components/button.tsx",
"components/button.js",
]
const config = {
resolvedPaths: {
cwd: "/foo/bar",
},
}
expect(
resolveModuleByProbablePath("/foo/bar/components/button", files, config, [
".tsx",
".jsx",
".js",
])
).toBe("components/button.tsx")
})
test("should preserve extension if specified in path", () => {
const files = ["components/button.tsx", "components/button.css"]
const config = {
resolvedPaths: {
cwd: "/foo/bar",
},
}
expect(
resolveModuleByProbablePath(
"/foo/bar/components/button.css",
files,
config
)
).toBe("components/button.css")
})
})
describe("toAliasedImport", () => {
test("should convert components path to aliased import", () => {
const filePath = "components/button.tsx"
const config = {
resolvedPaths: {
cwd: "/foo/bar",
components: "/foo/bar/components",
ui: "/foo/bar/components/ui",
lib: "/foo/bar/lib",
},
aliases: {
components: "@/components",
ui: "@/components/ui",
lib: "@/lib",
},
}
const projectInfo = {
aliasPrefix: "@",
}
expect(toAliasedImport(filePath, config, projectInfo)).toBe(
"@/components/button"
)
})
test("should convert ui path to aliased import", () => {
const filePath = "components/ui/button.tsx"
const config = {
resolvedPaths: {
cwd: "/foo/bar",
components: "/foo/bar/components",
ui: "/foo/bar/components/ui",
lib: "/foo/bar/lib",
},
aliases: {
components: "@/components",
ui: "@/components/ui",
lib: "@/lib",
},
}
const projectInfo = {
aliasPrefix: "@",
}
expect(toAliasedImport(filePath, config, projectInfo)).toBe(
"@/components/ui/button"
)
})
test("should collapse index files", () => {
const filePath = "components/ui/button/index.tsx"
const config = {
resolvedPaths: {
cwd: "/foo/bar",
components: "/foo/bar/components",
ui: "/foo/bar/components/ui",
lib: "/foo/bar/lib",
},
aliases: {
components: "@/components",
ui: "@/components/ui",
lib: "@/lib",
},
}
const projectInfo = {
aliasPrefix: "@",
}
expect(toAliasedImport(filePath, config, projectInfo)).toBe(
"@/components/ui/button"
)
})
test("should return null when no matching alias found", () => {
const filePath = "src/pages/index.tsx"
const config = {
resolvedPaths: {
cwd: "/foo/bar",
components: "/foo/bar/components",
ui: "/foo/bar/components/ui",
lib: "/foo/bar/lib",
},
aliases: {
components: "@/components",
ui: "@/components/ui",
lib: "@/lib",
},
}
const projectInfo = {
aliasPrefix: "@",
}
expect(toAliasedImport(filePath, config, projectInfo)).toBe("@/pages")
})
test("should handle nested directories", () => {
const filePath = "components/forms/inputs/text-input.tsx"
const config = {
resolvedPaths: {
cwd: "/foo/bar",
components: "/foo/bar/components",
ui: "/foo/bar/components/ui",
lib: "/foo/bar/lib",
},
aliases: {
components: "@/components",
ui: "@/components/ui",
lib: "@/lib",
},
}
const projectInfo = {
aliasPrefix: "@",
}
expect(toAliasedImport(filePath, config, projectInfo)).toBe(
"@/components/forms/inputs/text-input"
)
})
test("should keep non-code file extensions", () => {
const filePath = "components/styles/theme.css"
const config = {
resolvedPaths: {
cwd: "/foo/bar",
components: "/foo/bar/components",
ui: "/foo/bar/components/ui",
lib: "/foo/bar/lib",
},
aliases: {
components: "@/components",
ui: "@/components/ui",
lib: "@/lib",
},
}
const projectInfo = {
aliasPrefix: "@",
}
expect(toAliasedImport(filePath, config, projectInfo)).toBe(
"@/components/styles/theme.css"
)
})
test("should prefer longer matching paths", () => {
const filePath = "components/ui/button.tsx"
const config = {
resolvedPaths: {
cwd: "/foo/bar",
components: "/foo/bar/components",
ui: "/foo/bar/components/ui",
},
aliases: {
components: "@/components",
ui: "@/ui",
},
}
const projectInfo = {
aliasPrefix: "@",
}
expect(toAliasedImport(filePath, config, projectInfo)).toBe("@/ui/button")
})
test("should support tilde (~) alias prefix", () => {
const filePath = "components/button.tsx"
const config = {
resolvedPaths: {
cwd: "/foo/bar",
components: "/foo/bar/components",
},
aliases: {
components: "~components",
},
}
const projectInfo = {
aliasPrefix: "~",
}
expect(toAliasedImport(filePath, config, projectInfo)).toBe(
"~components/button"
)
})
test("should support @shadcn alias prefix", () => {
const filePath = "components/ui/button.tsx"
const config = {
resolvedPaths: {
cwd: "/foo/bar",
components: "/foo/bar/components",
ui: "/foo/bar/components/ui",
},
aliases: {
components: "@shadcn/components",
ui: "@shadcn/ui",
},
}
const projectInfo = {
aliasPrefix: "@shadcn",
}
expect(toAliasedImport(filePath, config, projectInfo)).toBe(
"@shadcn/ui/button"
)
})
test("should support ~cn alias prefix", () => {
const filePath = "lib/utils/index.tsx"
const config = {
resolvedPaths: {
cwd: "/foo/bar",
lib: "/foo/bar/lib",
},
aliases: {
lib: "~cn/lib",
},
}
const projectInfo = {
aliasPrefix: "~cn",
}
expect(toAliasedImport(filePath, config, projectInfo)).toBe("~cn/lib/utils")
})
test("should use project alias prefix when aliasKey is cwd", () => {
const filePath = "src/pages/home.tsx"
const config = {
resolvedPaths: {
cwd: "/foo/bar",
components: "/foo/bar/components",
ui: "/foo/bar/components/ui",
lib: "/foo/bar/lib",
},
aliases: {
components: "@/components",
ui: "@/components/ui",
lib: "@/lib",
},
}
const projectInfo = {
aliasPrefix: "@",
}
expect(toAliasedImport(filePath, config, projectInfo)).toBe("@/pages/home")
})
})

1445
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -14,7 +14,7 @@
"dependencies": {
"@workspace/ui": "workspace:*",
"lucide-react": "^0.475.0",
"next": "^15.2.0",
"next": "^15.2.3",
"next-themes": "^0.4.4",
"react": "^19.0.0",
"react-dom": "^19.0.0"

View File

@@ -33,8 +33,8 @@ importers:
specifier: ^0.475.0
version: 0.475.0(react@19.0.0)
next:
specifier: ^15.2.0
version: 15.2.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
specifier: ^15.2.3
version: 15.2.3(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
next-themes:
specifier: ^0.4.4
version: 0.4.4(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
@@ -356,56 +356,56 @@ packages:
'@jridgewell/trace-mapping@0.3.9':
resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==}
'@next/env@15.2.0':
resolution: {integrity: sha512-eMgJu1RBXxxqqnuRJQh5RozhskoNUDHBFybvi+Z+yK9qzKeG7dadhv/Vp1YooSZmCnegf7JxWuapV77necLZNA==}
'@next/env@15.2.3':
resolution: {integrity: sha512-a26KnbW9DFEUsSxAxKBORR/uD9THoYoKbkpFywMN/AFvboTt94b8+g/07T8J6ACsdLag8/PDU60ov4rPxRAixw==}
'@next/eslint-plugin-next@15.1.7':
resolution: {integrity: sha512-kRP7RjSxfTO13NE317ek3mSGzoZlI33nc/i5hs1KaWpK+egs85xg0DJ4p32QEiHnR0mVjuUfhRIun7awqfL7pQ==}
'@next/swc-darwin-arm64@15.2.0':
resolution: {integrity: sha512-rlp22GZwNJjFCyL7h5wz9vtpBVuCt3ZYjFWpEPBGzG712/uL1bbSkS675rVAUCRZ4hjoTJ26Q7IKhr5DfJrHDA==}
'@next/swc-darwin-arm64@15.2.3':
resolution: {integrity: sha512-uaBhA8aLbXLqwjnsHSkxs353WrRgQgiFjduDpc7YXEU0B54IKx3vU+cxQlYwPCyC8uYEEX7THhtQQsfHnvv8dw==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [darwin]
'@next/swc-darwin-x64@15.2.0':
resolution: {integrity: sha512-DiU85EqSHogCz80+sgsx90/ecygfCSGl5P3b4XDRVZpgujBm5lp4ts7YaHru7eVTyZMjHInzKr+w0/7+qDrvMA==}
'@next/swc-darwin-x64@15.2.3':
resolution: {integrity: sha512-pVwKvJ4Zk7h+4hwhqOUuMx7Ib02u3gDX3HXPKIShBi9JlYllI0nU6TWLbPT94dt7FSi6mSBhfc2JrHViwqbOdw==}
engines: {node: '>= 10'}
cpu: [x64]
os: [darwin]
'@next/swc-linux-arm64-gnu@15.2.0':
resolution: {integrity: sha512-VnpoMaGukiNWVxeqKHwi8MN47yKGyki5q+7ql/7p/3ifuU2341i/gDwGK1rivk0pVYbdv5D8z63uu9yMw0QhpQ==}
'@next/swc-linux-arm64-gnu@15.2.3':
resolution: {integrity: sha512-50ibWdn2RuFFkOEUmo9NCcQbbV9ViQOrUfG48zHBCONciHjaUKtHcYFiCwBVuzD08fzvzkWuuZkd4AqbvKO7UQ==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
'@next/swc-linux-arm64-musl@15.2.0':
resolution: {integrity: sha512-ka97/ssYE5nPH4Qs+8bd8RlYeNeUVBhcnsNUmFM6VWEob4jfN9FTr0NBhXVi1XEJpj3cMfgSRW+LdE3SUZbPrw==}
'@next/swc-linux-arm64-musl@15.2.3':
resolution: {integrity: sha512-2gAPA7P652D3HzR4cLyAuVYwYqjG0mt/3pHSWTCyKZq/N/dJcUAEoNQMyUmwTZWCJRKofB+JPuDVP2aD8w2J6Q==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
'@next/swc-linux-x64-gnu@15.2.0':
resolution: {integrity: sha512-zY1JduE4B3q0k2ZCE+DAF/1efjTXUsKP+VXRtrt/rJCTgDlUyyryx7aOgYXNc1d8gobys/Lof9P9ze8IyRDn7Q==}
'@next/swc-linux-x64-gnu@15.2.3':
resolution: {integrity: sha512-ODSKvrdMgAJOVU4qElflYy1KSZRM3M45JVbeZu42TINCMG3anp7YCBn80RkISV6bhzKwcUqLBAmOiWkaGtBA9w==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
'@next/swc-linux-x64-musl@15.2.0':
resolution: {integrity: sha512-QqvLZpurBD46RhaVaVBepkVQzh8xtlUN00RlG4Iq1sBheNugamUNPuZEH1r9X1YGQo1KqAe1iiShF0acva3jHQ==}
'@next/swc-linux-x64-musl@15.2.3':
resolution: {integrity: sha512-ZR9kLwCWrlYxwEoytqPi1jhPd1TlsSJWAc+H/CJHmHkf2nD92MQpSRIURR1iNgA/kuFSdxB8xIPt4p/T78kwsg==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
'@next/swc-win32-arm64-msvc@15.2.0':
resolution: {integrity: sha512-ODZ0r9WMyylTHAN6pLtvUtQlGXBL9voljv6ujSlcsjOxhtXPI1Ag6AhZK0SE8hEpR1374WZZ5w33ChpJd5fsjw==}
'@next/swc-win32-arm64-msvc@15.2.3':
resolution: {integrity: sha512-+G2FrDcfm2YDbhDiObDU/qPriWeiz/9cRR0yMWJeTLGGX6/x8oryO3tt7HhodA1vZ8r2ddJPCjtLcpaVl7TE2Q==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [win32]
'@next/swc-win32-x64-msvc@15.2.0':
resolution: {integrity: sha512-8+4Z3Z7xa13NdUuUAcpVNA6o76lNPniBd9Xbo02bwXQXnZgFvEopwY2at5+z7yHl47X9qbZpvwatZ2BRo3EdZw==}
'@next/swc-win32-x64-msvc@15.2.3':
resolution: {integrity: sha512-gHYS9tc+G2W0ZC8rBL+H6RdtXIyk40uLiaos0yj5US85FNhbFEndMA2nW3z47nzOWiSvXTZ5kBClc3rD0zJg0w==}
engines: {node: '>= 10'}
cpu: [x64]
os: [win32]
@@ -764,8 +764,8 @@ packages:
camel-case@3.0.0:
resolution: {integrity: sha512-+MbKztAYHXPr1jNTSKQF52VpcFjwY5RkR7fxksV8Doo4KAYc5Fl4UJRgthBbTmEx8C54DqahhbLJkDwjI3PI/w==}
caniuse-lite@1.0.30001700:
resolution: {integrity: sha512-2S6XIXwaE7K7erT8dY+kLQcpa5ms63XlRkMkReXjle+kf6c5g38vyMl+Z5y8dSxOFDhcFe+nxnn261PLxBSQsQ==}
caniuse-lite@1.0.30001707:
resolution: {integrity: sha512-3qtRjw/HQSMlDWf+X79N206fepf4SOOU6SQLMaq/0KkZLmSjPxAkBOQQ+FxbHKfHmYLZFfdWsO3KA90ceHPSnw==}
chalk@2.4.2:
resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==}
@@ -1629,6 +1629,11 @@ packages:
mute-stream@0.0.8:
resolution: {integrity: sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==}
nanoid@3.3.11:
resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==}
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
hasBin: true
nanoid@3.3.8:
resolution: {integrity: sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==}
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
@@ -1650,8 +1655,8 @@ packages:
react: ^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc
react-dom: ^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc
next@15.2.0:
resolution: {integrity: sha512-VaiM7sZYX8KIAHBrRGSFytKknkrexNfGb8GlG6e93JqueCspuGte8i4ybn8z4ww1x3f2uzY4YpTaBEW4/hvsoQ==}
next@15.2.3:
resolution: {integrity: sha512-x6eDkZxk2rPpu46E1ZVUWIBhYCLszmUY6fvHBFcbzJ9dD+qRX6vcHusaqqDlnY+VngKzKbAiG2iRCkPbmi8f7w==}
engines: {node: ^18.18.0 || ^19.8.0 || >= 20.0.0}
hasBin: true
peerDependencies:
@@ -2480,34 +2485,34 @@ snapshots:
'@jridgewell/resolve-uri': 3.1.2
'@jridgewell/sourcemap-codec': 1.5.0
'@next/env@15.2.0': {}
'@next/env@15.2.3': {}
'@next/eslint-plugin-next@15.1.7':
dependencies:
fast-glob: 3.3.1
'@next/swc-darwin-arm64@15.2.0':
'@next/swc-darwin-arm64@15.2.3':
optional: true
'@next/swc-darwin-x64@15.2.0':
'@next/swc-darwin-x64@15.2.3':
optional: true
'@next/swc-linux-arm64-gnu@15.2.0':
'@next/swc-linux-arm64-gnu@15.2.3':
optional: true
'@next/swc-linux-arm64-musl@15.2.0':
'@next/swc-linux-arm64-musl@15.2.3':
optional: true
'@next/swc-linux-x64-gnu@15.2.0':
'@next/swc-linux-x64-gnu@15.2.3':
optional: true
'@next/swc-linux-x64-musl@15.2.0':
'@next/swc-linux-x64-musl@15.2.3':
optional: true
'@next/swc-win32-arm64-msvc@15.2.0':
'@next/swc-win32-arm64-msvc@15.2.3':
optional: true
'@next/swc-win32-x64-msvc@15.2.0':
'@next/swc-win32-x64-msvc@15.2.3':
optional: true
'@nodelib/fs.scandir@2.1.5':
@@ -2925,7 +2930,7 @@ snapshots:
no-case: 2.3.2
upper-case: 1.1.3
caniuse-lite@1.0.30001700: {}
caniuse-lite@1.0.30001707: {}
chalk@2.4.2:
dependencies:
@@ -3938,6 +3943,8 @@ snapshots:
mute-stream@0.0.8: {}
nanoid@3.3.11: {}
nanoid@3.3.8: {}
natural-compare@1.4.0: {}
@@ -3951,26 +3958,26 @@ snapshots:
react: 19.0.0
react-dom: 19.0.0(react@19.0.0)
next@15.2.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0):
next@15.2.3(react-dom@19.0.0(react@19.0.0))(react@19.0.0):
dependencies:
'@next/env': 15.2.0
'@next/env': 15.2.3
'@swc/counter': 0.1.3
'@swc/helpers': 0.5.15
busboy: 1.6.0
caniuse-lite: 1.0.30001700
caniuse-lite: 1.0.30001707
postcss: 8.4.31
react: 19.0.0
react-dom: 19.0.0(react@19.0.0)
styled-jsx: 5.1.6(react@19.0.0)
optionalDependencies:
'@next/swc-darwin-arm64': 15.2.0
'@next/swc-darwin-x64': 15.2.0
'@next/swc-linux-arm64-gnu': 15.2.0
'@next/swc-linux-arm64-musl': 15.2.0
'@next/swc-linux-x64-gnu': 15.2.0
'@next/swc-linux-x64-musl': 15.2.0
'@next/swc-win32-arm64-msvc': 15.2.0
'@next/swc-win32-x64-msvc': 15.2.0
'@next/swc-darwin-arm64': 15.2.3
'@next/swc-darwin-x64': 15.2.3
'@next/swc-linux-arm64-gnu': 15.2.3
'@next/swc-linux-arm64-musl': 15.2.3
'@next/swc-linux-x64-gnu': 15.2.3
'@next/swc-linux-x64-musl': 15.2.3
'@next/swc-win32-arm64-msvc': 15.2.3
'@next/swc-win32-x64-msvc': 15.2.3
sharp: 0.33.5
transitivePeerDependencies:
- '@babel/core'
@@ -4148,7 +4155,7 @@ snapshots:
postcss@8.4.31:
dependencies:
nanoid: 3.3.8
nanoid: 3.3.11
picocolors: 1.1.1
source-map-js: 1.2.1