🌬

Typewindで型安全なTailwindを書く

2023/03/30に公開

Tailwindを採用している場面で便利そうなライブラリTypewindについての概要と、簡単な使い方をまとめてみました。

  • Typewindってなに?
  • Tailwindでクラス書く時によくタイポする
  • 型があればあるだけ良い

という方の参考になればと思います。

Typewindとは

以下公式ドキュメントから引用

Typewind is a typesafe and zero-runtime version of Tailwind CSS, a utility-first CSS framework that can be composed to build any design, directly in your markup.

要は型安全なTailwindです。

特徴

型安全

Typewindはtwというobjectを提供しており、このobjectにアクセスすることでTailwindのクラスを取得できます。

twの型定義はtailwind.config.jsから生成されるため独自で定義したカラーパレットやプラグイン等の型も含まれています。

tw.とすると補完が効き、存在しないプロパティにアクセスすると型チェックでエラーになるためstringのクラスを使用するより安全というわけです。

ゼロバンドルサイズ

使用されたすべてのスタイルをコンパイルし、ビルド時に静的なクラスに変換するのでバンドルサイズはゼロです。

プラグイン不要

Tailwindを使用する際、エディタにTailwindのプラグインをインストールして文字列の補完が出るようにしていると思いますが、前述の通りTypewindは補完が効くのでプラグインは不要になります。

対応フレームワーク

NextJS、SolidStart、Viteなど、ほとんどのJavascript/Typescriptフレームワークとビルドツールで動作します。

使用方法

ドキュメントを参考に簡単に使い方を説明します。

インストール、セットアップについては公式ドキュメント通りに進めれば簡単にできるためここでは省略します。

Normal Usage

twをインポートして、twのプロパティにアクセス、チェーンすることでTailwindのクラスを取得できます。

TypewindではTailwindのクラスのハイフン(-)をアンダースコア(_)に置き換えて使用します。

import { tw } from 'typewind';
 
export default function Button() {
  return (
    <button className={tw.bg_blue_500.text_white.rounded.py_3.px_4}>
      Click Me
    </button>
  );
}

Modifiers

hoverbefore等の疑似要素、擬似クラスも使用できます。

import { tw } from 'typewind';
 
export default function Button() {
  return (
    <button
      className={tw.bg_blue_500
        .hover(tw.bg_blue_600).first_letter(tw.text_red_500.font_bold)
        .text_white.rounded.py_3.px_4.md(tw.py_4.px_5)
        .dark(tw.bg_sky_900.hover(tw.bg_sky_800))}
    >
      Click Me
    </button>
  );
}

Important

importantをつけたい場合はtw.important()として引数に当てたいクラスを渡します。

import { tw } from 'typewind';
 
export default function Button() {
  return (
    <button
      className={tw.important(tw.text_red_500).hover(tw.important(tw.text_red_600))}
    >
      Click Me
    </button>
  );
}

Arbitrary Values

Tailwindでいうtext-[20px]のような任意の値を使用したい場合はtw.text_[20px]のように書きます。

import { tw } from 'typewind';
 
export default function App() {
  return (
    <button className={tw.text_['20px'].py_3.px_4.bg_blue_500}>Click Me</button>
  );
}

Arbitrary Variants

Tailwind v3.1で追加されたarbitrary variantsも使用できます。

tw.variant()を使用し第一引数にバリアント、第二引数に当てたいクラスを渡します。

import { tw } from 'typewind';
 
export default function App() {
  return (
    <ul>
      {Array(5)
        .fill(' ')
        .map((_, i) => (
          <li
            key={i}
            className={
              tw.variant('&:nth-child(3)', tw.underline)
                .list_disc.mx_5.text_white
            }
          >
            Item {i}
          </li>
        ))}
    </ul>
  );
}

Container Queries

Tailwind v3.2以上で使用可能なコンテナクエリのプラグインtailwindcss-container-queriesにも対応しています。

コンテナクエリについては以下の記事が参考になります。

ドキュメントのContainer Queriesをみると、そのまま使用できるように見えますがセットアップが必要です。

tailwindcss-container-queriesのインストール

yarn add @tailwindcss/container-queries

tailwind.config.jsに追記

tailwind.config.js
module.exports = {
  theme: {
    // ...
  },
  plugins: [
    require('@tailwindcss/container-queries'),
    // ...
  ],
}

typewind generateの実行

script名は各自の設定によりますが、Teypwindのドキュメント通りに設定していれば以下を実行すれば適用されます。

yarn postinstall

実際の使用方法は以下のようになります。

import { tw } from 'typewind';
 
export default function App() {
  return (
    <div className={tw.$container}>
      <div className={tw.$lg(tw.underline)}>
        Lorem ipsum dolor sit amet consectetur adipisicing elit. Animi, non.
      </div>
    </div>
  );
}

まとめ

軽く触ってみたところ、導入の手軽さの割にメリットが多く開発体験も上がるのでなかなか良いのではと感じました。

エディタにTailwindプラグインを入れていれば一応補完もしてくれますし、存在しないクラスのWarningも出してくれます。

ただ補完のスピードがイマイチだったり、たまに効かないこともあるので(私だけ?)そこにフラストレーションを感じている方や、型でガチガチに縛られたい方は導入を検討してもいいかもしれません。

参考文献

Discussion