💾

Shadcn/UIの中身を詳しくチェック

2024/11/12に公開

なんでパッケージではなくソースコード?

ユーザーがコンポーネントのビルドとスタイルを自由に決定できるようにするためです。
基本設定から始めて、好きなようにコンポーネントをカスタマイズすることができます。
npmパッケージの場合、スタイルが固定されているため変更が難しいですが、Shadcn/UIはソースコードで提供されているため、より柔軟にカスタマイズが可能です。

Shadcn/UIは、「コンポーネントのデザインと実装は分離されるべきだ」というコアな原則に基づいて作られています。そのため、すべてのコンポーネントは2つのレイヤー構造で構成されています。

構造と動作レイヤー

  • Headlessな実装: Shadcn/UIのコンポーネントは「headless」な形で実装されています。「headless」とは、UIの機能部分だけを提供し、具体的なスタイリングは含まないことを意味します。
  • Radix UIの活用: Shadcn/UIは、AccordionPopoverTabsなどの複雑な動作を実装するために、Radix UIという有名な「headless UIライブラリ」を使用しています。このライブラリを使うことで、スタイルなしでも多様な動作を簡単に適用できます。
    https://www.radix-ui.com/

スタイルレイヤー

  • Tailwind CSSの活用: Shadcn/UIのスタイリングにはTailwind CSSが中心に使われています。Tailwind CSSは柔軟で強力なスタイリングツールであり、コンポーネントに迅速にスタイルを適用することができます。
  • Figmaとの連携: デザインツールとしてFigmaを使用している場合、このアプローチにより、Tailwind CSSとFigmaの変数を連動させてデザインシステムを管理できます。つまり、Figmaで定義したデザイン要素(色やフォントなど)をTailwind CSSで簡単に追跡し、反映させることができます。
    https://www.figma.com/community/plugin/1222415071406554904/tokens-to-tailwind-css

コンポーネントの実装詳細

shadcn/ui Badge

https://ui.shadcn.com/docs/components/badge
BadgeコンポーネントはTailwind CSSを使ってスタイリングされたコンポーネントです。主なコンセプトは、スタイルを簡単に適用および変更できるようにすることです。

import * as React from "react";
import { cva, type VariantProps } from "class-variance-authority";
 
import { cn } from "@/lib/utils";
 
const badgeVariants = cva(
  "inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
  {
    variants: {
      variant: {
        default: "border-transparent bg-primary text-primary-foreground hover:bg-primary/80",
        secondary: "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
        destructive: "border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80",
        outline: "text-foreground",
      },
    },
    defaultVariants: {
      variant: "default",
    },
  }
);
 
export interface BadgeProps extends React.HTMLAttributes<HTMLDivElement>, VariantProps<typeof badgeVariants> {}
 
function Badge({ className, variant, ...props }: BadgeProps) {
  return <div className={cn(badgeVariants({ variant }), className)} {...props} />;
}
 
export { Badge, badgeVariants };

cva関数

この関数は、さまざまなスタイルのバリエーションを定義するために使用されます。
例えば、「デフォルト(Default)」や「セカンダリー(Secondary)」といったスタイルを定義し、どのスタイルをデフォルトに設定するかを決めることができます。
Badgeコンポーネントに共通するスタイルと各バリエーションスタイルがここで指定されています。

BadgePropsインターフェース

Badgeコンポーネントが受け取るプロパティを定義しています。基本的にHTMLの<div>要素の属性と、Badge独自のプロパティであるvariantを使用できるようにします。

Badgeコンポーネント

Badgeコンポーネントは<div>要素を使ってバッジを作成します。variantというプロパティに応じてスタイルが変わり、このプロパティをbadgeVariants関数に渡して、どのスタイルを適用するかを決定します。classNameプロパティは、追加のスタイルを簡単に追加できるようにするものです。

Badgeコンポーネントの重要な部分の1つは、cnというユーティリティ関数です。この関数は、clsxtailwind-mergeという2つのライブラリを活用しています。

  • clsx: 条件に応じてクラスを追加したり削除したりすることができます。例えば、"isActive"という条件がtrueの場合に特定の色のクラスを追加することができます。
  • tailwind-merge: Tailwind CSSのスタイルが重複した場合に、正しいスタイルを維持するようにしてくれます。例えば、既存のスタイルが上書きされるときに、スタイルの衝突を防ぎます。

例えば、LinkというコンポーネントでisActivetrueの場合、テキストの色を青に変更したいとします。clsxだけを使用すると衝突が発生することがありますが、tailwind-mergeを組み合わせることで、問題なくスタイルが変更されます。

Discussion