diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 39f01d5..49bc4a3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,6 +14,8 @@ jobs: - run: bun install --frozen-lockfile - run: bun run theme:check - run: bun run tokens:check + - name: cn keeps the type scale next to a text color + run: bun -e 'import { cn } from "./registry/winlab/lib/utils"; const out = cn("text-body text-title", "text-foreground"); if (out !== "text-title text-foreground") throw new Error(out)' - run: bun run lint - run: bun run typecheck - run: bun run format:check @@ -98,6 +100,8 @@ jobs: grep -q 'layout === "wide"' components/app-shell.tsx bunx shadcn@latest add http://localhost:8099/r/confirm-dialog.json -y -o grep -q 'useTransition' components/confirm-dialog.tsx + grep -q 'createCn' lib/utils.ts + grep -q 'from "@/lib/utils"' components/ui/button.tsx bunx shadcn@latest add http://localhost:8099/r/form-dialog.json -y -o grep -q 'fieldset' components/form-dialog.tsx bunx shadcn@latest add http://localhost:8099/r/empty-state.json -y -o diff --git a/lib/utils.ts b/lib/utils.ts index f815ded..ae4c7c5 100644 --- a/lib/utils.ts +++ b/lib/utils.ts @@ -1 +1,2 @@ -export { cn } from "cn" +// next/font and other generated code import cn from "@/lib/utils". +export { cn } from "@/registry/winlab/lib/utils" diff --git a/registry.json b/registry.json index 8cfb6e5..6229f91 100644 --- a/registry.json +++ b/registry.json @@ -187,7 +187,7 @@ "name": "utils", "type": "registry:lib", "title": "Utils", - "description": "cn re-exported at @/lib/utils.", + "description": "cn at @/lib/utils, aware of the text-title / text-body sizes.", "dependencies": [ "cn" ], @@ -225,6 +225,9 @@ "path": "registry/winlab/ui/button.tsx", "type": "registry:ui" } + ], + "registryDependencies": [ + "https://ui.winlab.tw/r/utils.json" ] }, { @@ -240,6 +243,9 @@ "path": "registry/winlab/ui/label.tsx", "type": "registry:ui" } + ], + "registryDependencies": [ + "https://ui.winlab.tw/r/utils.json" ] }, { @@ -256,6 +262,9 @@ "path": "registry/winlab/ui/input.tsx", "type": "registry:ui" } + ], + "registryDependencies": [ + "https://ui.winlab.tw/r/utils.json" ] }, { @@ -271,6 +280,9 @@ "path": "registry/winlab/ui/textarea.tsx", "type": "registry:ui" } + ], + "registryDependencies": [ + "https://ui.winlab.tw/r/utils.json" ] }, { @@ -286,6 +298,9 @@ "path": "registry/winlab/ui/skeleton.tsx", "type": "registry:ui" } + ], + "registryDependencies": [ + "https://ui.winlab.tw/r/utils.json" ] }, { @@ -303,6 +318,9 @@ "path": "registry/winlab/ui/badge.tsx", "type": "registry:ui" } + ], + "registryDependencies": [ + "https://ui.winlab.tw/r/utils.json" ] }, { @@ -317,6 +335,7 @@ "lucide-react" ], "registryDependencies": [ + "https://ui.winlab.tw/r/utils.json", "https://ui.winlab.tw/r/button.json" ], "files": [ @@ -336,6 +355,7 @@ "cn" ], "registryDependencies": [ + "https://ui.winlab.tw/r/utils.json", "https://ui.winlab.tw/r/button.json" ], "files": [ @@ -362,6 +382,7 @@ } ], "registryDependencies": [ + "https://ui.winlab.tw/r/utils.json", "https://ui.winlab.tw/r/field.json" ] }, @@ -378,6 +399,9 @@ "path": "registry/winlab/ui/table.tsx", "type": "registry:ui" } + ], + "registryDependencies": [ + "https://ui.winlab.tw/r/utils.json" ] }, { @@ -394,6 +418,9 @@ "path": "registry/winlab/ui/popover.tsx", "type": "registry:ui" } + ], + "registryDependencies": [ + "https://ui.winlab.tw/r/utils.json" ] }, { @@ -411,6 +438,9 @@ "path": "registry/winlab/ui/command.tsx", "type": "registry:ui" } + ], + "registryDependencies": [ + "https://ui.winlab.tw/r/utils.json" ] }, { @@ -428,6 +458,9 @@ "path": "registry/winlab/ui/dropdown-menu.tsx", "type": "registry:ui" } + ], + "registryDependencies": [ + "https://ui.winlab.tw/r/utils.json" ] }, { @@ -445,6 +478,9 @@ "path": "registry/winlab/ui/checkbox.tsx", "type": "registry:ui" } + ], + "registryDependencies": [ + "https://ui.winlab.tw/r/utils.json" ] }, { @@ -461,6 +497,9 @@ "path": "registry/winlab/ui/switch.tsx", "type": "registry:ui" } + ], + "registryDependencies": [ + "https://ui.winlab.tw/r/utils.json" ] }, { @@ -477,6 +516,9 @@ "path": "registry/winlab/ui/tabs.tsx", "type": "registry:ui" } + ], + "registryDependencies": [ + "https://ui.winlab.tw/r/utils.json" ] }, { @@ -493,6 +535,9 @@ "path": "registry/winlab/ui/avatar.tsx", "type": "registry:ui" } + ], + "registryDependencies": [ + "https://ui.winlab.tw/r/utils.json" ] }, { @@ -509,6 +554,9 @@ "path": "registry/winlab/ui/tooltip.tsx", "type": "registry:ui" } + ], + "registryDependencies": [ + "https://ui.winlab.tw/r/utils.json" ] }, { @@ -525,6 +573,9 @@ "path": "registry/winlab/ui/separator.tsx", "type": "registry:ui" } + ], + "registryDependencies": [ + "https://ui.winlab.tw/r/utils.json" ] }, { @@ -541,6 +592,9 @@ "path": "registry/winlab/ui/collapsible.tsx", "type": "registry:ui" } + ], + "registryDependencies": [ + "https://ui.winlab.tw/r/utils.json" ] }, { @@ -569,6 +623,7 @@ "lucide-react" ], "registryDependencies": [ + "https://ui.winlab.tw/r/utils.json", "https://ui.winlab.tw/r/field.json", "https://ui.winlab.tw/r/popover.json", "https://ui.winlab.tw/r/command.json" @@ -591,6 +646,7 @@ "react-day-picker" ], "registryDependencies": [ + "https://ui.winlab.tw/r/utils.json", "https://ui.winlab.tw/r/button.json" ], "files": [ @@ -609,6 +665,7 @@ "cn" ], "registryDependencies": [ + "https://ui.winlab.tw/r/utils.json", "https://ui.winlab.tw/r/avatar.json", "https://ui.winlab.tw/r/tooltip.json" ], @@ -756,6 +813,7 @@ "react-day-picker" ], "registryDependencies": [ + "https://ui.winlab.tw/r/utils.json", "https://ui.winlab.tw/r/field.json", "https://ui.winlab.tw/r/calendar.json", "https://ui.winlab.tw/r/popover.json" @@ -780,6 +838,9 @@ "path": "registry/winlab/blocks/action-panel/action-panel.tsx", "type": "registry:component" } + ], + "registryDependencies": [ + "https://ui.winlab.tw/r/utils.json" ] } ] diff --git a/registry/winlab/blocks/action-panel/action-panel.tsx b/registry/winlab/blocks/action-panel/action-panel.tsx index bd84a5f..1ead3fa 100644 --- a/registry/winlab/blocks/action-panel/action-panel.tsx +++ b/registry/winlab/blocks/action-panel/action-panel.tsx @@ -1,7 +1,7 @@ "use client" import * as React from "react" -import { cn } from "cn" +import { cn } from "@/registry/winlab/lib/utils" type ActionPanelProps = { /** The verb: "開門". */ diff --git a/registry/winlab/blocks/app-shell/app-shell.tsx b/registry/winlab/blocks/app-shell/app-shell.tsx index b3e9fe6..3aff6f3 100644 --- a/registry/winlab/blocks/app-shell/app-shell.tsx +++ b/registry/winlab/blocks/app-shell/app-shell.tsx @@ -3,7 +3,7 @@ import * as React from "react" import Link from "next/link" import { usePathname } from "next/navigation" -import { cn } from "cn" +import { cn } from "@/registry/winlab/lib/utils" import { Avatar, diff --git a/registry/winlab/blocks/date-picker/date-picker.tsx b/registry/winlab/blocks/date-picker/date-picker.tsx index 7a3cf56..5ced735 100644 --- a/registry/winlab/blocks/date-picker/date-picker.tsx +++ b/registry/winlab/blocks/date-picker/date-picker.tsx @@ -1,7 +1,7 @@ "use client" import * as React from "react" -import { cn } from "cn" +import { cn } from "@/registry/winlab/lib/utils" import { CalendarIcon } from "lucide-react" import type { Matcher } from "react-day-picker" diff --git a/registry/winlab/lib/utils.ts b/registry/winlab/lib/utils.ts index 57f20b5..f72cf76 100644 --- a/registry/winlab/lib/utils.ts +++ b/registry/winlab/lib/utils.ts @@ -1,3 +1,8 @@ -// next/font and other generated code import cn from "@/lib/utils"; WinLab -// components import it from the cn package directly. -export { cn } from "cn" +import { createCn } from "cn/config" + +// cn that knows the WinLab type scale. Stock tailwind-merge rules read +// text-title and text-body as colors, so cn("text-body", "text-foreground") +// would drop the size; registering them as font sizes keeps both. +export const cn = createCn({ + extend: { classGroups: { "font-size": [{ text: ["title", "body"] }] } }, +}) diff --git a/registry/winlab/ui/alert-dialog.tsx b/registry/winlab/ui/alert-dialog.tsx index 50104d5..4e1ecec 100644 --- a/registry/winlab/ui/alert-dialog.tsx +++ b/registry/winlab/ui/alert-dialog.tsx @@ -2,7 +2,7 @@ import * as React from "react" import { AlertDialog as AlertDialogPrimitive } from "@base-ui/react/alert-dialog" -import { cn } from "cn" +import { cn } from "@/registry/winlab/lib/utils" import { Button } from "@/registry/winlab/ui/button" diff --git a/registry/winlab/ui/avatar.tsx b/registry/winlab/ui/avatar.tsx index 00aed4a..99a84d0 100644 --- a/registry/winlab/ui/avatar.tsx +++ b/registry/winlab/ui/avatar.tsx @@ -1,7 +1,7 @@ "use client" import { Avatar as AvatarPrimitive } from "@base-ui/react/avatar" -import { cn } from "cn" +import { cn } from "@/registry/winlab/lib/utils" // One size, 40px: the height of a control row. function Avatar({ className, ...props }: AvatarPrimitive.Root.Props) { diff --git a/registry/winlab/ui/badge.tsx b/registry/winlab/ui/badge.tsx index 8fc1648..4305c3e 100644 --- a/registry/winlab/ui/badge.tsx +++ b/registry/winlab/ui/badge.tsx @@ -1,7 +1,7 @@ import { mergeProps } from "@base-ui/react/merge-props" import { useRender } from "@base-ui/react/use-render" import { cva, type VariantProps } from "class-variance-authority" -import { cn } from "cn" +import { cn } from "@/registry/winlab/lib/utils" // Status variants share one shape: a tinted background with text in the same color. const badgeVariants = cva( diff --git a/registry/winlab/ui/button.tsx b/registry/winlab/ui/button.tsx index 8a8df47..056154b 100644 --- a/registry/winlab/ui/button.tsx +++ b/registry/winlab/ui/button.tsx @@ -1,6 +1,6 @@ import { Button as ButtonPrimitive } from "@base-ui/react/button" import { cva, type VariantProps } from "class-variance-authority" -import { cn } from "cn" +import { cn } from "@/registry/winlab/lib/utils" // One height for text and icon buttons; variants follow what WinLab apps use. const buttonVariants = cva( diff --git a/registry/winlab/ui/calendar.tsx b/registry/winlab/ui/calendar.tsx index 06c33f5..bee6299 100644 --- a/registry/winlab/ui/calendar.tsx +++ b/registry/winlab/ui/calendar.tsx @@ -1,7 +1,7 @@ "use client" import * as React from "react" -import { cn } from "cn" +import { cn } from "@/registry/winlab/lib/utils" import { ChevronLeftIcon, ChevronRightIcon } from "lucide-react" import { DayPicker, type DayButton } from "react-day-picker" import { zhTW } from "react-day-picker/locale" diff --git a/registry/winlab/ui/checkbox.tsx b/registry/winlab/ui/checkbox.tsx index ce22ab5..44b6c66 100644 --- a/registry/winlab/ui/checkbox.tsx +++ b/registry/winlab/ui/checkbox.tsx @@ -1,7 +1,7 @@ "use client" import { Checkbox as CheckboxPrimitive } from "@base-ui/react/checkbox" -import { cn } from "cn" +import { cn } from "@/registry/winlab/lib/utils" import { CheckIcon } from "lucide-react" // Round, 20px, with a larger invisible hit area. A future radio shows a dot, diff --git a/registry/winlab/ui/collapsible.tsx b/registry/winlab/ui/collapsible.tsx index 51f03fd..0e94d07 100644 --- a/registry/winlab/ui/collapsible.tsx +++ b/registry/winlab/ui/collapsible.tsx @@ -1,7 +1,7 @@ "use client" import { Collapsible as CollapsiblePrimitive } from "@base-ui/react/collapsible" -import { cn } from "cn" +import { cn } from "@/registry/winlab/lib/utils" function Collapsible({ ...props }: CollapsiblePrimitive.Root.Props) { return diff --git a/registry/winlab/ui/combobox.tsx b/registry/winlab/ui/combobox.tsx index 669aa2c..c1160de 100644 --- a/registry/winlab/ui/combobox.tsx +++ b/registry/winlab/ui/combobox.tsx @@ -1,7 +1,7 @@ "use client" import * as React from "react" -import { cn } from "cn" +import { cn } from "@/registry/winlab/lib/utils" import { ChevronDownIcon } from "lucide-react" import { fieldTriggerClassName } from "@/registry/winlab/lib/field" diff --git a/registry/winlab/ui/command.tsx b/registry/winlab/ui/command.tsx index cfc0025..815a4df 100644 --- a/registry/winlab/ui/command.tsx +++ b/registry/winlab/ui/command.tsx @@ -2,7 +2,7 @@ import * as React from "react" import { Command as CommandPrimitive } from "cmdk" -import { cn } from "cn" +import { cn } from "@/registry/winlab/lib/utils" import { CheckIcon, SearchIcon } from "lucide-react" // Searchable list for a popover (combobox). It has no surface of its own: diff --git a/registry/winlab/ui/dialog.tsx b/registry/winlab/ui/dialog.tsx index e7627d3..bd25301 100644 --- a/registry/winlab/ui/dialog.tsx +++ b/registry/winlab/ui/dialog.tsx @@ -3,7 +3,7 @@ import * as React from "react" import { Dialog as DialogPrimitive } from "@base-ui/react/dialog" import { cva, type VariantProps } from "class-variance-authority" -import { cn } from "cn" +import { cn } from "@/registry/winlab/lib/utils" import { XIcon } from "lucide-react" import { Button } from "@/registry/winlab/ui/button" diff --git a/registry/winlab/ui/dropdown-menu.tsx b/registry/winlab/ui/dropdown-menu.tsx index 7cd59e7..0efd9ee 100644 --- a/registry/winlab/ui/dropdown-menu.tsx +++ b/registry/winlab/ui/dropdown-menu.tsx @@ -2,7 +2,7 @@ import * as React from "react" import { Menu as MenuPrimitive } from "@base-ui/react/menu" -import { cn } from "cn" +import { cn } from "@/registry/winlab/lib/utils" import { ChevronRightIcon } from "lucide-react" const menuSurface = diff --git a/registry/winlab/ui/input.tsx b/registry/winlab/ui/input.tsx index c4e539a..c79b948 100644 --- a/registry/winlab/ui/input.tsx +++ b/registry/winlab/ui/input.tsx @@ -1,6 +1,6 @@ import * as React from "react" import { Input as InputPrimitive } from "@base-ui/react/input" -import { cn } from "cn" +import { cn } from "@/registry/winlab/lib/utils" // Same height and radius as Button so they line up in a row. function Input({ className, type, ...props }: React.ComponentProps<"input">) { diff --git a/registry/winlab/ui/label.tsx b/registry/winlab/ui/label.tsx index f6fa313..193a2cf 100644 --- a/registry/winlab/ui/label.tsx +++ b/registry/winlab/ui/label.tsx @@ -1,7 +1,7 @@ "use client" import * as React from "react" -import { cn } from "cn" +import { cn } from "@/registry/winlab/lib/utils" function Label({ className, ...props }: React.ComponentProps<"label">) { return ( diff --git a/registry/winlab/ui/popover.tsx b/registry/winlab/ui/popover.tsx index c88ef00..c4d3bc7 100644 --- a/registry/winlab/ui/popover.tsx +++ b/registry/winlab/ui/popover.tsx @@ -2,7 +2,7 @@ import * as React from "react" import { Popover as PopoverPrimitive } from "@base-ui/react/popover" -import { cn } from "cn" +import { cn } from "@/registry/winlab/lib/utils" function Popover({ ...props }: PopoverPrimitive.Root.Props) { return diff --git a/registry/winlab/ui/select.tsx b/registry/winlab/ui/select.tsx index a91415f..954ece3 100644 --- a/registry/winlab/ui/select.tsx +++ b/registry/winlab/ui/select.tsx @@ -2,7 +2,7 @@ import * as React from "react" import { Select as SelectPrimitive } from "@base-ui/react/select" -import { cn } from "cn" +import { cn } from "@/registry/winlab/lib/utils" import { CheckIcon, ChevronDownIcon, ChevronUpIcon } from "lucide-react" import { fieldTriggerClassName } from "@/registry/winlab/lib/field" diff --git a/registry/winlab/ui/separator.tsx b/registry/winlab/ui/separator.tsx index d69c134..c8b3a62 100644 --- a/registry/winlab/ui/separator.tsx +++ b/registry/winlab/ui/separator.tsx @@ -1,7 +1,7 @@ "use client" import { Separator as SeparatorPrimitive } from "@base-ui/react/separator" -import { cn } from "cn" +import { cn } from "@/registry/winlab/lib/utils" function Separator({ className, diff --git a/registry/winlab/ui/skeleton.tsx b/registry/winlab/ui/skeleton.tsx index c4e5f00..575f96b 100644 --- a/registry/winlab/ui/skeleton.tsx +++ b/registry/winlab/ui/skeleton.tsx @@ -1,4 +1,4 @@ -import { cn } from "cn" +import { cn } from "@/registry/winlab/lib/utils" function Skeleton({ className, ...props }: React.ComponentProps<"div">) { return ( diff --git a/registry/winlab/ui/switch.tsx b/registry/winlab/ui/switch.tsx index 21f1851..687e48d 100644 --- a/registry/winlab/ui/switch.tsx +++ b/registry/winlab/ui/switch.tsx @@ -1,7 +1,7 @@ "use client" import { Switch as SwitchPrimitive } from "@base-ui/react/switch" -import { cn } from "cn" +import { cn } from "@/registry/winlab/lib/utils" // One size: a 40 x 24 track with 2px padding, so the 20px thumb travels 16px. function Switch({ className, ...props }: SwitchPrimitive.Root.Props) { diff --git a/registry/winlab/ui/table.tsx b/registry/winlab/ui/table.tsx index 128f273..8ba9939 100644 --- a/registry/winlab/ui/table.tsx +++ b/registry/winlab/ui/table.tsx @@ -1,5 +1,5 @@ import * as React from "react" -import { cn } from "cn" +import { cn } from "@/registry/winlab/lib/utils" // Header text is muted so rows read first; put tabular-nums on number columns. // The outer columns have no side padding so text lines up with the page edge. diff --git a/registry/winlab/ui/tabs.tsx b/registry/winlab/ui/tabs.tsx index d428832..a7ff6a3 100644 --- a/registry/winlab/ui/tabs.tsx +++ b/registry/winlab/ui/tabs.tsx @@ -1,7 +1,7 @@ "use client" import { Tabs as TabsPrimitive } from "@base-ui/react/tabs" -import { cn } from "cn" +import { cn } from "@/registry/winlab/lib/utils" function Tabs({ className, ...props }: TabsPrimitive.Root.Props) { return ( diff --git a/registry/winlab/ui/textarea.tsx b/registry/winlab/ui/textarea.tsx index 7c9d764..d634020 100644 --- a/registry/winlab/ui/textarea.tsx +++ b/registry/winlab/ui/textarea.tsx @@ -1,5 +1,5 @@ import * as React from "react" -import { cn } from "cn" +import { cn } from "@/registry/winlab/lib/utils" function Textarea({ className, ...props }: React.ComponentProps<"textarea">) { return ( diff --git a/registry/winlab/ui/tooltip.tsx b/registry/winlab/ui/tooltip.tsx index 9777bf7..500bd01 100644 --- a/registry/winlab/ui/tooltip.tsx +++ b/registry/winlab/ui/tooltip.tsx @@ -1,7 +1,7 @@ "use client" import { Tooltip as TooltipPrimitive } from "@base-ui/react/tooltip" -import { cn } from "cn" +import { cn } from "@/registry/winlab/lib/utils" function TooltipProvider({ delay = 0,