はじめに
こんにちは!
レコチョクでWebアプリ開発のフロントエンド領域を担当している山本です。
最近、Next.jsにTailwind CSSというCSSフレームワークを導入して開発する機会がありました。
CSSフレームワークを導入することで、1から全てスタイルを当てることなく統一感のあるデザインを作成することができます。
しかし、デザイナーが指定しているフォントのサイズ感や余白感が導入したCSSフレームワークの定数と異なることは多々あるかと思います。
また、命名が汎用的すぎて逆に分かりにくいということもあるのではないでしょうか。
Tailwind CSSのクラスをカスタマイズして開発を行なっていたので、その設定方法をご紹介します。
やりたいこと
Tailwind CSSには以下のような汎用的なクラスが用意されています。
これを以下のようなデザイナーが指定したデザインシステムに合わせてカスタマイズしたいと思います。
フォントサイズ
色
余白
事前準備
公式のドキュメントを参考に、Next.jsにTailwind CSSを導入します。
(ここでは導入方法は省略します。)
開発環境
パッケージ名 | バージョン |
---|---|
Next.js | v15.1.5 |
Tailwind CSS | v3.4.1 |
TypeScript | v5.5.4 |
対応方法
1. カスタムしたい定数を設定
- globals.cssにカスタムプロパティを宣言
@tailwind base; @tailwind components; @tailwind utilities; :root { --color-scale-apple-600: #cf222e; --color-scale-banana-600: #e19900; --color-scale-jade-400: #00d3ba; --color-scale-orange-400: #fb8f44; --color-scale-rose-400: #ff5e89; --color-scale-sky-400: #54aeff; --color-scale-sky-600: #0969da; --color-scale-stone-100: #f6f8fa; --color-scale-stone-1100: #0d0f14; --color-scale-stone-200: #eaeef2; --color-scale-stone-300: #d0d7de; --color-scale-stone-400: #afb8c1; --color-scale-stone-50: #fcfcfc; --color-scale-stone-500: #8c959f; --color-scale-stone-600: #6e7781; --dimension-scale-0: 0px; --dimension-scale-4: 4px; --dimension-scale-8: 8px; --dimension-scale-16: 16px; --dimension-scale-24: 24px; --dimension-scale-40: 40px; --dimension-scale-64: 64px; } |
- tailwind.config.ts にカスタムプロパティを設定
import type { Config } from 'tailwindcss'; const config: Config = { content: [ './src/pages/**/*.{js,ts,jsx,tsx,mdx}', './src/components/**/*.{js,ts,jsx,tsx,mdx}', './src/app/**/*.{js,ts,jsx,tsx,mdx}', ], theme: { extend: { // 色 colors: { 'color-background-level0': 'var(--color-scale-stone-50)', 'color-background-level1': 'var(--color-scale-stone-200)', 'color-semantic-action-destructive': 'var(--color-scale-apple-600)', 'color-semantic-action-muted': 'var(--color-scale-stone-600)', 'color-semantic-action-neutral': 'var(--color-scale-stone-1100)', 'color-semantic-action-primary': 'var(--color-scale-banana-600)', 'color-semantic-stroke-explicit': 'var(--color-scale-stone-400)', 'color-semantic-stroke-implicit': 'var(--color-scale-stone-300)', 'color-semantic-stroke-selected': 'var(--color-scale-sky-600)', 'color-semantic-text-destructive': 'var(--color-scale-apple-600)', 'color-semantic-text-link': 'var(--color-scale-sky-600)', 'color-semantic-text-muted': 'var(--color-scale-stone-500)', 'color-semantic-text-neutral': 'var(--color-scale-stone-1100)', 'color-semantic-text-neutral-inverse': 'var(--color-scale-stone-100)', 'color-semantic-text-selected': 'var(--color-scale-sky-600)', 'color-semantic-category-column': 'var(--color-scale-sky-400)', 'color-semantic-category-interview': 'var(--color-scale-jade-400)', 'color-semantic-category-report': 'var(--color-scale-rose-400)', 'color-semantic-category-news': 'var(--color-scale-orange-400)', }, // 余白 spacing: { 'dimension-space-zero': 'var(--dimension-scale-0)', 'dimension-space-xs': 'var(--dimension-scale-4)', 'dimension-space-s': 'var(--dimension-scale-8)', 'dimension-space-m': 'var(--dimension-scale-16)', 'dimension-space-l': 'var(--dimension-scale-24)', 'dimension-space-xl': 'var(--dimension-scale-40)', 'dimension-space-xxl': 'var(--dimension-scale-64)', }, // フォントサイズ fontSize: { 'body-medium-comfort': ['14px', { lineHeight: '28px' }], 'body-medium-normal': ['14px', { lineHeight: '24px' }], 'body-medium-dense': ['14px', { lineHeight: '20px' }], 'body-small-comfort': ['13px', { lineHeight: '28px' }], 'body-small-normal': ['13px', { lineHeight: '20px' }], 'body-small-dense': ['13px', { lineHeight: '20px' }], 'body-extra-small-comfort': ['11px', { lineHeight: '24px' }], 'body-extra-small-normal': ['11px', { lineHeight: '20px' }], 'body-extra-small-dense': ['11px', { lineHeight: '16px' }], 'display-large': ['64px', { lineHeight: '96px' }], 'display-medium': ['43px', { lineHeight: '68px' }], 'headline-extra-large': ['43px', { lineHeight: '68px' }], 'headline-large': ['32px', { lineHeight: '48px', letterSpacing: '0.03em' }], 'headline-medium': ['26px', { lineHeight: '40px', letterSpacing: '0.03em' }], 'headline-small': ['21px', { lineHeight: '32px', letterSpacing: '0.03em' }], 'title-medium': ['16px', { lineHeight: '24px', letterSpacing: '0.03em' }], 'title-small': ['14px', { lineHeight: '24px', letterSpacing: '0.03em' }], }, }, }, plugins: [require('@tailwindcss/typography')], }; export default config; }; |
3. カスタムクラスを使用
- カスタムクラスを当てる前の状態
<div> <p>Hello, World!</p> <p>こんにちは!</p> </div> |
- カスタムクラスを反映
<div className="bg-color-background-level0 py-dimension-space-l px-dimension-space-m"> <p className="text-color-semantic-text-neutral text-title-medium">Hello, World!</p> <p className="text-color-semantic-text-muted mt-dimension-space-m text-body-small-normal">こんにちは!</p> </div> |
すると、設定したカスタムクラスが適用されています。
画面サイズによってクラスを変えたい時
モバイルとタブレットで指定した値を変えたい時もあるかと思います。
今回は、モバイルとタブレットでフォントサイズを変えたかったので、globals.cssに以下のように記載しました。
@layer utilities { @media (max-width: 767px) { .text-headline-extra-large { font-size: 32px; line-height: 48px; } .text-headline-large { font-size: 26px; line-height: 40px; } .text-headline-medium { font-size: 21px; line-height: 32px; } .text-headline-small { font-size: 18px; line-height: 28px; } } } |
これで画面サイズによってフォントサイズを変えることができます。
Tailwind CSSのクラスを動的に変えたい時
Tailwind CSSはビルド時に使用しているクラスを解析し、未使用のスタイルを削除してしまいます。
そのため、動的にクラスを変える際は別途設定が必要です。
例
カテゴリーごとに背景色を変えたい
const ArticleCategory = ({ category }: { category: Category }) => { return ( <div> <p>{category.name}</p> </div> ); }; |
このように書いても、背景色が反映されませんでした。
解決方法
以下のように記載することで tailwind.config.js で必ず読み込むクラスを指定しておくことで、ビルド時に削除されることなくスタイルが当たるようになります。
const config: Config = { // 必ず読み込むクラスを指定 safelist: [ 'bg-color-semantic-category-column', 'bg-color-semantic-category-interview', 'bg-color-semantic-category-report', 'bg-color-semantic-category-news', ...(略) ], }; |
おわりに
このように、Tailwind CSSのクラスをカスタマイズすることで、独自に設定したクラス名で開発を行うことができました。
個人的に、
m-2 や
text-sm といった命名だと、どのようなスタイルが適用されているのかわかりにくかったり、デザイナーが考慮してくれた数値が本当にあたっているのか確認しにくいと感じます。
カスタムクラスの命名をデザインシステムに則った形式にカスタマイズすることで、コーディングするときもレビューするときも、意図したCSSがあたっているか確認しやすくなるかと思います。
最後まで読んでいただきありがとうございました!!