Refined UI

An opinionated, customizable superset of shadcn/ui

AgentsPrimitivesSkillsSkins
Guides
  • Overview
  • Get started
  • Shadcn compatibility
  • Architecture
  • Agent surface
Primitives
  • Accordion
  • Alert
  • Alert dialog
  • Aspect ratio
  • Autocomplete
  • Avatar
  • Badge
  • Button
  • Button group
  • Calendar
  • Card
  • Checkbox
  • Checkbox Group
  • Code
  • Code Diff
  • Collapsible
  • Color Picker
  • Combobox
  • Command
  • Date Picker
  • Dialog
  • Drawer
  • Empty
  • Field
  • Form
  • Gradient Editor
  • Hover card
  • Input
  • Input group
  • Input OTP
  • Item
  • Kbd
  • Markdown
  • Menu
  • Menubar
  • Meter
  • Native select
  • Navigation menu
  • Number Field
  • Pagination
  • Popover
  • Progress
  • Radio group
  • Resizable
  • Scroll area
  • Select
  • Sidebar
  • Skeleton
  • Slider
  • Spinner
  • Switch
  • Table
  • Table of contents
  • Tabs
  • Textarea
  • Toast
  • Toggle
  • Toolbar
  • Tooltip
  • Tree
  • Trigger Menu
  • Typography
Hooks
  • Use Chat Message
  • Use Chat Input
  • Use User Ask
  • Use Audio Recorder
  • Use Environment Variables
  • Use Copy To Clipboard
  • Use Tool Call
  • Use Sidebar Resize
Utils
  • cn
  • Skin
  • Contracts
  • Serialize
Extensions
  • Control Effects
  • View Transition
  • Send Aurora
  1. Reference
  2. cn
Utils

cn

Tailwind class-name merge helper.

Installed to components/refined-ui/lib/cn.ts with every Refined UI component, so the shared helper stays in one place.

Source

Installed util source

Class-name helper
src/registry/lib/cn.ts
import { type ClassValue, clsx } from "clsx";
import { extendTailwindMerge } from "tailwind-merge";
​
// type scale = custom `text-*` utils (text-label, text-body, …, see theme.css); tw-merge only knows stock
// sizes, misreads ours as color group — size+color in same cn() then collide in one group, one silently
// dropped (code-diff grid lost text-label, fell to inherited 16px); registering as font-size keeps them independent
const textScale = [
"micro",
"caption",
"label",
"body",
"body-lg",
"heading-1",
"heading-2",
"heading-3",
"heading-4",
// legacy aliases kept until their call sites migrate to the canonical rungs above
"meta",
"title-sm",
"title-md",
"title-lg",
"display",
];
​
// elevation tiers = custom `shadow-*` utils (shadow-pop/soft/modal, see theme.css); unregistered, tw-merge reads
// them as shadow COLOR and lets them ride alongside a later box-shadow util — a skin's `shadow-(--x)`/`shadow-none`
// then loses to the recipe's `shadow-pop` by CSS order, not argument order (content-surface kept its popover lift)
const shadowScale = ["pop", "soft", "modal"];
​
const twMerge = extendTailwindMerge({
extend: {
classGroups: {
"font-size": [{ text: textScale }],
shadow: [{ shadow: shadowScale }],
},
},
// stock Tailwind couples line-height into `text-*` size, so twMerge lets font-size clear a preceding `leading-*`
// our scale is size-only and default sizes are never used here, so drop that conflict — explicit `leading-*` stands regardless of order
// w/o this, `leading-5 … text-label` silently drops the leading (code grid fell to 18px row height)
override: {
conflictingClassGroups: {
"font-size": [],
},
},
});
​
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}
​

On this page

  • Installation
  • Source