Closed23

[キャッチアップ] TailwindCSS

shingo.sasakishingo.sasaki

スクラップについて

なんとなく食わず嫌いしてた tailwindcss について、使いたい場面が出てきたので改めて確認する。
ユーティリティクラスが色々デフォルトで入ってるCSSフレームワークで、ゼロランタイムに出来てカスタマイズ性が凄いぐらいの認識からのスタート。
https://tailwindcss.com/

shingo.sasakishingo.sasaki

サンドボックス環境作る

今回は Vite + React + TS で。

$ yarn create vite
# React + TS で作る
$ yarn add tailwindcss
$ npx tailwindcss  init

tailwind 用の設定ファイルが作られる。この辺いじるのがだいぶ先だと思うけど。

/** @type {import('tailwindcss').Config} */
export default {
  content: [],
  theme: {
    extend: {},
  },
  plugins: [],
}
shingo.sasakishingo.sasaki

Tailwind はゼロランタイム

Tailwind CSS works by scanning all of your HTML files, JavaScript components, and any other templates for class names, generating the corresponding styles and then writing them to a static CSS file.
It's fast, flexible, and reliable — with zero-runtime.

ということらしい。膨大なクラスを読み込むんじゃなくて、実際に使用されているクラスのみをプリプロセスで読み込むんだ。

具体的には PostCSS のプラグインとして機能するらしい。だから webpack なら postcss-loader とかなのかな。

shingo.sasakishingo.sasaki

対象コードを指定する

設定ファイルでスキャン対象ファイルを指定。

/** @type {import('tailwindcss').Config} */
export default {
  content: ["index.html", "./src/**/*.{html,js,ts,jsx,tsx}"],
  theme: {
    extend: {},
  },
  plugins: [],
};
shingo.sasakishingo.sasaki

入力ファイルの設定

tailwind に読ませるルートファイルを用意

src/input.css
@tailwind base;
@tailwind components;
@tailwind utilities;
shingo.sasakishingo.sasaki
  • base がリセットCSSみたいなもの
  • utilisites が各ファイルで使用されているユーティリティクラス
  • components はなんか tailwind 用のカスタムコンポーネントのやつらしい
shingo.sasakishingo.sasaki

CLI で 出力する

PostCSS プラグインとして機能するって書いてたけど、普通に CLI でもいけるみたい。

$ npx tailwindcss -i ./src/input.css -o ./dist/output.css --watch

掲載は省略するけど、リセットCSSっぽいのだけ出力された。

出力ファイルを読み込むタグを HTML に追加する。

<link href="/dist/output.css" rel="stylesheet">
shingo.sasakishingo.sasaki

HTML で使ってみる

こんなタグを追加する。

<h1 class="text-3xl font-bold underline">Hello, Tailwind</h1>

tailwind が再ビルドされて以下みたいな定義が追加された。

.text-3xl {
  font-size: 1.875rem;
  line-height: 2.25rem;
}

.font-bold {
  font-weight: 700;
}

.underline {
  text-decoration-line: underline;
}

逆にタグを消して再ビルドすると、ちゃんとCSS定義も消える。

shingo.sasakishingo.sasaki

React コンポーネントで使ってみる

 <h1 className="text-3xl font-bold underline">Vite + React</h1>

なんか追加設定いるかと思ったけど普通に適用された。

shingo.sasakishingo.sasaki

vscode 拡張を設定する

Tailwind は @tailwind @apply みたいな拡張ルールがあったり、ユーティリティクラスがたくさんあったりするので、それを補完できるようにする。

これをインストールする
https://marketplace.visualstudio.com/items?itemName=bradlc.vscode-tailwindcss

補完が効くとか当たり前機能もよいけど、ユーティリティクラスに対してプレビュー入るのイケてる。

Prettier プラグイン入れるとクラス名の順番揃えられるらしい。
https://github.com/tailwindlabs/prettier-plugin-tailwindcss

shingo.sasakishingo.sasaki

ユーティリティファーストな考え

https://tailwindcss.com/docs/utility-first

従来のCSSじゃなくてユーティリティファーストなクラスを使うことで

  • クラス名を考えるのに苦労せずに済む
    • 特にレイアウト用途だけのラッパー要素など (sidebar-inner-wrapper)
  • CSS の誇大化を防げる
    • 機能拡張ごとにCSSも増えていってしまう
      • これはコンポーネント指向してけば回避できるかも?
  • CSS のスコープを考慮せずに変更できる
    • これもコンポーネントに閉じたスタイリングできれば大丈夫

ならユーティリティクラスじゃなくてインラインスタイルを直接書けばよいのでは説があるが、ユーティリティクラスを使うことで

  • デザインの一貫性を担保できる
    • 特にデザインシステム構築に強力
  • メディアクエリはインラインでは描けないが、ユーティリティクラスなら使える
  • ホバー、フォーカスといった擬似クラスも扱える\
shingo.sasakishingo.sasaki

こりゃ便利そうだ。

.hover\:font-bold:hover {
  font-weight: 700;
}

@media (min-width: 640px) {
  .sm\:text-left {
    text-align: left;
  }
}
shingo.sasakishingo.sasaki
shingo.sasakishingo.sasaki
<li class="flex py-4 first:pt-0 last:pb-0">

こういうの便利ね。先頭と末尾には余計な空白入れないみたいなの。
flex で重ねて gap 指定にすれば良いのではってのはあるけど。

shingo.sasakishingo.sasaki
    <input type="text" value="tbone" disabled class="mt-1 block w-full px-3 py-2 bg-white border border-slate-300 rounded-md text-sm shadow-sm placeholder-slate-400
      focus:outline-none focus:border-sky-500 focus:ring-1 focus:ring-sky-500
      disabled:bg-slate-50 disabled:text-slate-500 disabled:border-slate-200 disabled:shadow-none
      invalid:border-pink-500 invalid:text-pink-600
      focus:invalid:border-pink-500 focus:invalid:ring-pink-500
    "/>

こういうのを見ると流石にウッとなるけど、普通にCSS書いたとしてもそれなりに長く複雑になるからええか。

shingo.sasakishingo.sasaki

親要素の状態に基づいて子要素のスタイルを変えるの凄い!

<a href="#" class="group block max-w-xs mx-auto rounded-lg p-6 bg-white ring-1 ring-slate-900/5 shadow-lg space-y-3 hover:bg-sky-500 hover:ring-sky-500">
  <div class="flex items-center space-x-3">
    <svg class="h-6 w-6 stroke-sky-500 group-hover:stroke-white" fill="none" viewBox="0 0 24 24"><!-- ... --></svg>
    <h3 class="text-slate-900 group-hover:text-white text-sm font-semibold">New project</h3>
  </div>
  <p class="text-slate-500 group-hover:text-white text-sm">Create a new project from a variety of starting templates.</p>
</a>
shingo.sasakishingo.sasaki

レスポンシブデザイン

https://tailwindcss.com/docs/responsive-design

こういうやつ

<!-- Width of 16 by default, 32 on medium screens, and 48 on large screens -->
<img class="w-16 md:w-32 lg:w-48" src="...">

By default, Tailwind uses a mobile-first breakpoint system, similar to what you might be used to in other frameworks like Bootstrap.

What this means is that unprefixed utilities (like uppercase) take effect on all screen sizes, while prefixed utilities (like md:uppercase) only take effect at the specified breakpoint and above.

なんでそれがモバイルファーストなんだろうって思ったけど、ビューポートが広い場合の指定を明示しなければならない=デフォルトではモバイルビューにも適用される=モバイルファーストに実装する必要があるってことか。

モバイル向けの個別対応をするんじゃなくて、モバイルがデフォルトで、PCが個別対応ってことだ。

shingo.sasakishingo.sasaki

スタイルの再利用

https://tailwindcss.com/docs/reusing-styles

マルチカーソルを活用して最適化しろなんてガイド始めてみたわ。

@layer@apply を活用して再利用可能なスタイルの定義が可能。なるほどぉ。

@layer components {
  .btn-primary {
    @apply py-2 px-4 bg-blue-500 text-white font-semibold rounded-lg shadow-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-400 focus:ring-opacity-75;
  }
}

これも .btn-primary が使用されてないと生成されないみたい。

でもこういう共通化・抽象化はなるべくしないほうが Tailwind の良さは引き出せるよとのこと。
まぁコンポーネントで切り出せば良いでしょとは思う。

shingo.sasakishingo.sasaki

カスタムスタイル

https://tailwindcss.com/docs/adding-custom-styles

設定ファイル内でテーマの各種値を差し替えることができる

/** @type {import('tailwindcss').Config} */
module.exports = {
  theme: {
    screens: {
      sm: '480px',
      md: '768px',
      lg: '976px',
      xl: '1440px',
    },
    colors: {
      'blue': '#1fb6ff',
      'pink': '#ff49db',
      'orange': '#ff7849',
      'green': '#13ce66',
      'gray-dark': '#273444',
      'gray': '#8492a6',
      'gray-light': '#d3dce6',
    },
    fontFamily: {
      sans: ['Graphik', 'sans-serif'],
      serif: ['Merriweather', 'serif'],
    },
    extend: {
      spacing: {
        '128': '32rem',
        '144': '36rem',
      },
      borderRadius: {
        '4xl': '2rem',
      }
    }
  }
}
このスクラップは2023/10/21にクローズされました