UIをどうつくっていくか?
リンクアンドモチベーションでエンジニアをしています、宮田と申します。
自分はプロダクトのWebのUIを開発する時によくこんなことを考えていました。
「一からUIコンポーネントを開発する余裕はないなぁ」
「けどmuiのようなUIライブラリをそのまま使いたくない。ユースケースによってはUIに独自性を持たせたいなぁ」
最近だと「Tailwind CSSを使いたいなぁ」という考えも加わりました。
企業によっては強力なUIライブラリを社内で公開していて、それを使うことができるケースもあります。 ないケースや個人開発時は、毎回色々試行錯誤をしていたのですが、最近自分の中での最適解を見つけられた気がしたので紹介します。
自分の試行錯誤の変遷
※ 本記事ではReactを使う前提で書いています。
※ 1つのプロジェクトのみで試行錯誤しているのではなくて複数のプロジェクトで試行錯誤しています。
1. MUIのような多くのコンポーネントを内蔵したライブラリを使う
楽です。ですがカスタマイズが難しくデザインに拘りたい時にはカスタマイズが難儀です。 自分の実装力の向上に伴い使うことがなくなっていきました。
2. Tailwind CSS と Tailwind UI を使う
賛否がありますが、Tailwind CSSを好んで使います。
好きな理由は以下です。
Tailwind CSSの開発元の Tailwind Labsでは、Tailwind UIというTailwind CSSを使ったテンプレートを有償で販売しています。
Personalプランで$299
、Teamsプランで$799
しますが、良いUIをつくれるなら安いものだと購入(会社負担)して、Tailwind UIをTailwind CSSで拡張しながら使っていました。
しかしながら、Tailwindの責務は見た目のみで振る舞いについては自分自身で実装する必要があります。
最初は意気揚々とコンポーネントを開発していたのですが段々と車輪の再発明が面倒になってきました。
3. Tailwind CSS と MantineUI を使う
Tailwind CSSで拡張できる良い感じのUIライブラリないかなと探しているとMantineUIというものを見つけました。
MantineUIはStyles APIを提供しており、コンポーネントの任意の要素のスタイルを上書きすることができます。
MantineUIはデフォルトのスタイルが洗練されており気に入っていたのですが、Tailwind CSSのリセットCSSとMantineが競合してしまう問題があったり、メンテナがTailwind CSSを好きではないのが懸念ポイントでした。
I'm not planning to migrate Mantine to tw. It is not something that you or anybody else can convince me to do. I just do not like it – no matter how much tool is fast and popular, it still does not worth it if I would feel unhappy about working on the project with it.
https://github.com/mantinedev/mantine/issues/2815#issuecomment-1510283315
4. Tailwind CSS と shadcn/ui を使う
いくつかのライブラリを試した後に、自分が欲しかったものを整理してみました。
自分が求めていたものは、
でした。
それを叶えてくれるものがshadcn/uiでした。
ちなみに開発者のshadcnさんは Design EngineerとしてVercelに入社しているようです。
I’m joining @vercel as a Design Engineer to continue building customizable UI components for the open source community.
— shadcn (@shadcn) 2023年8月8日
Something incredible coming your way! Can’t wait to get started! pic.twitter.com/fta2ws9YLc
shadcn/ui の特徴
自分はTailwind CSSと相性の良いヘッドレスUIが欲しかったのだと思います。 shadcn/uiはまさにそれで、代表的なヘッドレスUIであるRadix UIに依存しています。
shadcn/uiのイントロダクションの最初には以下のように書いてあります。
Introduction Re-usable components built using Radix UI and Tailwind CSS. This is NOT a component library. It's a collection of re-usable components that you can copy and paste into your apps. What do you mean by not a component library? I mean you do not install it as a dependency. It is not available or distributed via npm. Pick the components you need. Copy and paste the code into your project and customize to your needs. The code is yours. Use this as a reference to build your own component libraries.
重要な特徴は、
だと思います。
※ Tailwindの開発元が出しているHeadless UIやMUIが出しているBase UIという選択肢もあったのですが提供コンポーネントが多くなく採用は見送りました。
shadcn/ui の使い方
shadcn/uiはドキュメントも非常に丁寧に書かれている印象です。
インストールも設定も公式ドキュメント通りで困ることはなかったです。
shadcn/uiの面白いところはコンポーネントを個別にインストールできることと、インストール先がnode_modules
配下ではないことです。
例えばButtonコンポーネントをインストールするとします。
npx shadcn-ui@latest add button
するとインストール先は、例えばNext.jsのデフォルトだと、app/components/ui/になります。
├── app │ ├── layout.tsx │ └── page.tsx ├── components │ ├── ui │ │ ├── button.tsx │ │ └── ...
app/components/ui/button.tsxのファイルは以下のようになっており。Tailwind CSSでスタイリングされています。
自由にスタイルは変更できるし、Propsを追加することも自由です。素晴らしいです!
import * as React from "react" import { Slot } from "@radix-ui/react-slot" import { cva, type VariantProps } from "class-variance-authority" import { cn } from "@/lib/utils" const buttonVariants = cva( "inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50", { variants: { variant: { default: "bg-primary text-primary-foreground shadow hover:bg-primary/90", destructive: "bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90", outline: "border border-input bg-transparent shadow-sm hover:bg-accent hover:text-accent-foreground", secondary: "bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80", ghost: "hover:bg-accent hover:text-accent-foreground", link: "text-primary underline-offset-4 hover:underline", }, size: { default: "h-9 px-4 py-2", sm: "h-8 rounded-md px-3 text-xs", lg: "h-10 rounded-md px-8", icon: "h-9 w-9", }, }, defaultVariants: { variant: "default", size: "default", }, } ) export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement>, VariantProps<typeof buttonVariants> { asChild?: boolean } const Button = React.forwardRef<HTMLButtonElement, ButtonProps>( ({ className, variant, size, asChild = false, ...props }, ref) => { const Comp = asChild ? Slot : "button" return ( <Comp className={cn(buttonVariants({ variant, size, className }))} ref={ref} {...props} /> ) } ) Button.displayName = "Button" export { Button, buttonVariants }
今後のUIはどうつくられていくのか?
shadcn/uiを使っていると下手にデザインシステムを構築するよりも、高機能なUIをすぐに実装できるなと感じています。アクセシビリティも一定担保してくていますし巨人の肩に乗っている感じがすごいです。
Tailwind CSSの設定も丁寧に行えばデザイントークンが崩れることも少ないです。
もちろん複雑なユースケースには対応できませんが、ベースラインを整えるのは楽になったと感じます。まだWeb業界に入って9年目ですが、どんどん効率化していることを感じます。
最近はfigmaからTailwind CSSベースのコンポーネントを生成してくれたり、
チャット形式でUIをオーダーできたりするツール(Private Alpha版なので使えないです)も出てきています。
Webが生まれた時はおそらくエンジニアとデザイナーが分かれていなかったはずです。UIの開発が簡単になった結果、また2つが統合したりするんですかね?
技術の進化を楽しみつつ、未来を妄想しつつ、変化を楽しんで行けたら良いなと思っています。