はじめに
Tailwind CSSは自由度が高く、細かいスタイル調整がしやすいCSSフレームワークです。しかし、その自由度の高さゆえに「ドロップダウンメニュー」や「モーダル」「タブ」といった動きのあるUIコンポーネントを一から実装しようとすると、意外と手間がかかります。クラス名を組み合わせてレイアウトは作れても、開閉状態の管理やアクセシビリティ対応まで含めると、思った以上に工数がかさんでしまうものです。
そんな悩みを解決してくれるのが、今回紹介する「Preline UI」です。Tailwind CSSのユーティリティクラスをそのまま活かしながら、640以上のUIコンポーネントと220以上のブロックをすぐに使えるオープンソースのコンポーネントライブラリです。
説明を読むより、実際にモーダルが開閉する様子を触った方が早いと思います。先に挙動を見たい方はこちらからどうぞ。
Preline UIとは
Preline UIは、Tailwind CSSをベースにした、あらかじめデザイン・実装済みのUIコンポーネント集です。htmlstreamofficialによって開発されており、GitHub上で6.4kを超えるスターを獲得しています(MITライセンス)。ボタンやフォームといった静的なパーツだけでなく、ドロップダウン・モーダル・アコーディオン・タブといったインタラクティブな挙動を持つコンポーネントも、JavaScriptプラグインとして同梱されているのが大きな特徴です。
主な特徴
- 豊富なコンポーネント数 - ボタン、フォーム、ナビゲーション、テーブルなど640以上の無料コンポーネントと220以上のブロックを提供しています
- Tailwind CSSとの高い親和性 - 独自のCSS設計を持ち込むのではなく、Tailwindのユーティリティクラスをそのまま使ってマークアップするため、既存のTailwindプロジェクトに違和感なく組み込めます
- フレームワーク横断対応 - React、Vue、Next.js、Laravel、Django、Rubyなど、複数のフレームワーク向けに導入ガイドが用意されています
- ヘッドレスプラグイン - ドロップダウンやモーダルなどの挙動は27種類のJavaScriptプラグインとして分離されており、デザインを保ったままロジックだけを利用することも可能です
インストール
npmでPrelineパッケージをインストールします。
npm i preline
Tailwind CSS(v4系)のエントリーCSSファイルに、Prelineのソースとスタイルを読み込む設定を追加します。
@import "tailwindcss";
/* Preline UI */
@source "./node_modules/preline/dist/*.js";
@import "./node_modules/preline/variants.css";
インタラクティブなコンポーネント(ドロップダウンやモーダルなど)を動かすために、bodyタグの末尾でJavaScriptを読み込みます。
<script src="./node_modules/preline/dist/preline.js"></script>
Preline UIのサンプルを動かす
Preline UIの代表的なコンポーネントである「モーダル」を実際に動かしてみましょう。ボタンにdata-hs-overlay属性で対象要素のIDを指定するだけで、クリック時の開閉処理をJavaScript側で自前実装する必要がなくなります。裏側ではHSOverlayというPrelineのJavaScriptプラグインが、要素の表示・非表示やアウトサイドクリックでの自動クローズを担当しています。
要点だけを抜き出すと、モーダルの開閉は次のようなマークアップだけで完結します。
<!-- 開くボタン -->
<button type="button" class="btn" data-hs-overlay="#hs-modal">
モーダルを開く
</button>
<!-- モーダル本体 -->
<div id="hs-modal" class="hs-overlay hidden size-full fixed inset-0 z-[60]">
<div class="hs-overlay-open:opacity-100 opacity-0 transition-all">
<div class="bg-white rounded-xl shadow-sm">
<button type="button" data-hs-overlay="#hs-modal">閉じる</button>
<p>モーダルの中身です。</p>
</div>
</div>
</div>
実際に動かせるサンプルが下です。「モーダルを開く」ボタンを押すと#hs-preview-modalが表示され、背景クリックまたは「閉じる」ボタンで元に戻ります。CSS部分はTailwind CSS本体(CDN版)を読み込み、開閉ロジックはPreline UI本体(preline@4.2.0)を読み込んで実現しています。なお通常のプロジェクトではhs-overlay-open:opacity-100のようにPreline UI専用のTailwindバリアントを使いますが、これはビルド時にPreline UIのTailwindプラグインを登録して初めて使えるものです。このサンプルはビルド設定を持たないプレイグラウンドなので、同じ「開いたときだけ効かせる」表現をTailwind CSS標準の任意バリアント[.open_&]:opacity-100(「openクラスを持つ祖先要素の内側」を意味します)で代用しています。
data-hs-overlay="#hs-preview-modal"の部分を書き換えて別のIDを指定すれば、同じボタンから複数のモーダルを出し分けることもできます。また、モーダル本体の[.open_&]:mt-7や[.open_&]:opacity-100の値を調整すると、表示位置やフェードインの強さを変更できます(実際のプロジェクトでは同じ役割をhs-overlay-open:mt-7が担います)。JavaScript側でaddEventListenerを書かずにここまでの開閉動作が実現できる点が、Preline UIならではの手軽さです。
基本的な使い方
まずは静的なHTMLで、Prelineのドロップダウンコンポーネントを使ってみましょう。クラス名を指定するだけで、開閉の挙動はPreline側のJavaScriptが面倒を見てくれます。
<div class="hs-dropdown relative inline-flex">
<button
id="hs-dropdown-default"
type="button"
class="hs-dropdown-toggle py-3 px-4 inline-flex items-center gap-x-2 text-sm font-medium rounded-lg border border-gray-200 bg-white text-gray-800 shadow-sm hover:bg-gray-50"
>
メニューを開く
<svg class="hs-dropdown-open:rotate-180 size-4" viewBox="0 0 24 24" fill="none" stroke="currentColor">
<path d="m6 9 6 6 6-6" />
</svg>
</button>
<div
class="hs-dropdown-menu transition-[opacity,margin] duration hs-dropdown-open:opacity-100 opacity-0 hidden min-w-60 bg-white shadow-md rounded-lg mt-2 p-1"
aria-labelledby="hs-dropdown-default"
>
<a class="flex items-center gap-x-3 py-2 px-3 rounded-lg text-sm text-gray-800 hover:bg-gray-100" href="#">
プロフィール
</a>
<a class="flex items-center gap-x-3 py-2 px-3 rounded-lg text-sm text-gray-800 hover:bg-gray-100" href="#">
設定
</a>
</div>
</div>
hs-dropdownというクラスをコンテナに付与するだけで、クリック時の開閉やアウトサイドクリックでの自動クローズといった動作が有効になります。JavaScriptのイベントリスナーを自前で書く必要はありません。
実践的なユースケース
React / Next.jsでの利用
Reactを使ったSPAやNext.jsのようなフレームワークでは、Prelineが提供するJavaScriptプラグインをページ遷移のたびに再初期化する必要があります。これは、クライアントサイドルーティングによってDOMが差し替わっても、Preline側がその変化を検知できないためです。HSStaticMethods.autoInit()を呼び出すことで、新しく描画された要素にもプラグインを再適用できます。
"use client";
import { useEffect } from "react";
import { usePathname } from "next/navigation";
export function PrelineScript() {
const pathname = usePathname();
useEffect(() => {
const loadPreline = async () => {
await import("preline/preline");
window.HSStaticMethods.autoInit();
};
loadPreline();
}, [pathname]);
return null;
}
このコンポーネントをレイアウトの中で読み込んでおけば、ページ遷移のたびにドロップダウンやモーダルなどのコンポーネントが正しく動作するようになります。TypeScriptを使っている場合は、preline/prelineからIStaticMethods型をインポートし、Windowインターフェースを拡張しておくと型エラーを防げます。
import { IStaticMethods } from "preline/preline";
declare global {
interface Window {
HSStaticMethods: IStaticMethods;
}
}
Preline UIのアコーディオンで開閉パネルを作る
FAQやマニュアルのように、見出しをクリックすると本文が開閉するUIが欲しい場面では、Preline UIの「アコーディオン」コンポーネントが使えます。コンテナにhs-accordion-group、各項目にhs-accordion、開閉トリガーにhs-accordion-toggleというクラスを付けるだけで、どの項目が開いているかの状態管理をPreline UI側に任せられます。
要点は次のマークアップです。トリガーのaria-expandedや本文の表示・非表示は、Preline UIのHSAccordionプラグインが自動で切り替えます。
<div class="hs-accordion-group">
<div class="hs-accordion active" id="hs-basic-item">
<button class="hs-accordion-toggle" aria-expanded="true">
項目1
</button>
<div class="hs-accordion-content" aria-labelledby="hs-basic-item">
項目1の本文です。
</div>
</div>
</div>
下のサンプルでは3つの項目を持つアコーディオンを用意しています。見出しをクリックすると本文が開き、別の見出しを開くと直前の項目が自動的に閉じることを確認できます。
data-hs-accordion-always-open属性を外すと、1つの項目を開いたときに他の項目が自動的に閉じる「排他的な開閉」になります。逆に本サンプルのようにこの属性を付けたままにすると、複数の項目を同時に開いた状態にできます。FAQページかチェックリストか、用途に応じて使い分けるとよいでしょう。
Tailwind CSSベースのバリエーション(サイズ・カラー)を切り替える
Preline UIのコンポーネントはロジック部分をJavaScriptプラグインが担い、見た目はTailwind CSSのユーティリティクラスがそのまま反映される作りになっています。そのため、ボタンのサイズや色を変えたいときも、Preline UI独自のpropsやCSS変数を覚える必要はなく、py-2 px-3のようなサイズ系クラスやbg-blue-600のようなカラー系クラスを差し替えるだけで済みます。
サイズはpy-*・px-*・text-*、色はbg-*・hover:bg-*の組み合わせで変わります。
<!-- サイズ違い -->
<button class="py-1.5 px-3 text-xs rounded-lg bg-blue-600 text-white">Small</button>
<button class="py-2 px-4 text-sm rounded-lg bg-blue-600 text-white">Medium</button>
<button class="py-3 px-6 text-base rounded-lg bg-blue-600 text-white">Large</button>
<!-- 色違い -->
<button class="py-2 px-4 text-sm rounded-lg bg-blue-600 text-white">Primary</button>
<button class="py-2 px-4 text-sm rounded-lg bg-red-600 text-white">Danger</button>
下のサンプルでは、サイズ用と色用のセレクトボックスを操作すると、プレビュー中のPreline UIボタンに適用されるTailwindクラスがその場で切り替わります。実際のプロジェクトで、どのクラスの組み合わせがデザインに合うかを検討する際の参考にしてください。
セレクトボックスを操作するとpreviewBtn.classNameが丸ごと入れ替わり、コンソールに適用中のクラス一覧が出力されます。Preline UI自体にはサイズや色を切り替えるJavaScript APIは存在せず、あくまでTailwind CSSのクラス設計だけでバリエーションを作っている点がポイントです。独自コンポーネントを作る際も、同じ考え方でクラスの組み合わせを増やしていけます。
data属性ベースの初期化とHSStaticMethods.autoInit()
Preline UIのコンポーネントは、ページ読み込み時にDOMを走査してhs-dropdownやhs-accordionなどのクラス・data属性を見つけ、対応するJavaScriptプラグインを自動で紐付ける仕組みになっています。しかし、innerHTMLへの代入やReactの再レンダリングなどでページ読み込み後に新しい要素を追加した場合、その要素は自動では初期化されません。このようなときに使うのがHSStaticMethods.autoInit()です。
呼び出し方はシンプルで、新しい要素をDOMに追加したあとに1行呼ぶだけです。
// 新しいドロップダウンをDOMに追加したあと
container.insertAdjacentHTML('beforeend', newDropdownHtml)
// Preline UIに未初期化の要素をスキャンさせる
window.HSStaticMethods.autoInit()
下のサンプルでは、ボタンを押すとhs-dropdownを含む新しいHTML断片をinnerHTMLで動的に追加します。HSStaticMethods.autoInit()を呼んだ場合と呼ばなかった場合を比較できるようにしているので、実際にドロップダウンをクリックして違いを確認してみてください。
「autoInit()ありで追加」したドロップダウンはクリックするときちんとメニューが開閉しますが、「autoInit()なしで追加」した方はhs-dropdownクラスが付いているだけの見た目上のボタンにとどまり、クリックしても反応しません。ページ内で既存のコードをHSStaticMethods.autoInit()の呼び出しごとwindow.HSStaticMethods.autoInit(['dropdown'])のように第一引数へコンポーネント名の配列を渡す形に書き換えると、対象を特定のコンポーネント種別だけに絞って再初期化することもできます。ReactのuseEffect内で呼んでいるのも、まさにこの仕組みです。
管理画面のダッシュボード構築
Prelineにはサイドバー付きのナビゲーション、テーブル、カード型のUIブロックが豊富に用意されているため、社内向け管理画面やダッシュボードのプロトタイプを短期間で組み上げたい場面に向いています。ブロック単位でコピーして貼り付け、Tailwindのクラスを調整するだけで、統一感のあるレイアウトを構築できます。
まとめ
Preline UIは、Tailwind CSSの自由度を保ったまま、実装コストの高いインタラクティブなUIコンポーネントを一気に揃えられるライブラリです。ドロップダウンやモーダルの挙動を自作する手間から解放されるだけでなく、React・Next.jsをはじめとした主要フレームワークへの導入ガイドも整備されているため、既存プロジェクトへの組み込みもスムーズです。
まずは公式サイトのコンポーネント一覧から気になるパーツを選び、実際のプロジェクトに組み込んでみてはいかがでしょうか。Figmaのデザインシステムも無料で公開されているため、デザインとフロントエンド実装をまたいだ連携もしやすくなっています。