前言
如果翻閱 Shadcn 元件集的元件會發現大量使用到一個輔助函式 cn
如下:
import { type ClassValue, clsx } from 'clsx'import { twMerge } from 'tailwind-merge'
export function cn(...inputs: ClassValue[]) { return twMerge(clsx(inputs))}
它其實是兩套函式庫(tailwind-merge、clsx)的組成,用於更方便建構元件當中 Tailwind 的樣式。為什麼會需要額外的庫和方法來建構元件樣式呢?
Tailwind Merge
如果將 Tailwind 和基於元件的 UI 渲染器(如 React 或 Vue)一起使用會遇到樣式蓋不過去的問題,不管最終樣式字串如何組合,Tailwind 會在生成 utility class 過程中把所有可能用到的 class 輸出成一份靜態 CSS,其中 定義順序攸關樣式的優先層級。
function MyGenericInput(props) { const className = `px-2 py-1 ${props.className || ''}` return <input {...props} className={className} />}
function MyOneOffInput(props) { return ( <MyGenericInput {...props} className="p-3" // ← 傳入 class 想修改 padding 樣式但蓋不過去 /> )}
要解決這個問題有兩種可能方法:
以上兩種方法都有各自的問題,所以才需要 Tailwind Merge 協助:
twMerge('p-5 p-2 p-4') // → 'p-4'
tailwind-merge 依靠 ~7 kB
的設定檔決定如何融合 Tailwind 相關的樣式字串,具體融合規則可以參考合併規則文件。
clsx
如果將 Tailwind 和基於元件的 UI 渲染器(如 React 或 Vue)一起使用,常常要依照條件動態切換 CSS class,原生寫法很快就變得又長又難讀:
<div className={ {/* 留意 Class 間空格留白需組合正確 */} "p-2 " + (isActive ? "bg-blue-500" : "bg-gray-300") + (isDisabled ? " opacity-50" : "") }/>
clsx 既可以簡化樣式開發流程,也能避免錯誤的 falsy
狀態被組合進去:
<div className={clsx("p-2", { "bg-blue-500": isActive, "opacity-50": isDisabled, })}/>
規則如官方 Usage 文件:
// Strings (variadic)clsx('foo', true && 'bar', 'baz');//=> 'foo bar baz'
// Objectsclsx({ foo:true, bar:false, baz:isTrue() });//=> 'foo baz'
// Objects (variadic)clsx({ foo:true }, { bar:false }, null, { '--foobar':'hello' });//=> 'foo --foobar'
// Arraysclsx(['foo', 0, false, 'bar']);//=> 'foo bar'
// Arrays (variadic)clsx(['foo'], ['', 0, false, 'bar'], [['baz', [['hello'], 'there']]]);//=> 'foo bar baz hello there'
// Kitchen sink (with nesting)clsx('foo', [1 && 'bar', { baz:false, bat:null }, ['hello', ['world']]], 'cya');//=> 'foo bar hello world cya'
總結
雖然以上套件都著重在非常簡單的小問題,但在大型 UI 專案可用於提升開發體驗。最後我對於這些縮寫有很大的疑問,寫完這篇文章才大致猜出縮寫的意涵:
- twMerge = Tailwind Merge
- clsx = Class Mix
- cn = Class Name
延伸閱讀
- cn() - Every Tailwind Coder Needs It (clsx + twMerge) - Byte Grad
- What is it for - tailwind-merge GitHub
- 01__clsx__function__main__.pdf - clsx-Documentation