最近、取り組んでいることについて書いていなかったので、共有したいことがたくさんあります!正直、多すぎるほどです。このアップデートを公開する主な動機は、来週さらに多くのものがリリースされる予定であり、すでにリリースしたすべてのものを共有するまで、それらを共有することは許されないように感じているからです。
水着を着て、ラウンジチェアに座って、ビタミンCSSを吸収する準備をしてください。
Headless UI v1.6 がリリースされました
数週間前、Tailwind UI に React と Vue のサポートを追加するために構築した、スタイルなしの UI ライブラリである Headless UI の新しいマイナーバージョンをリリースしました。
詳細についてはリリースノートをご覧ください。ハイライトをいくつかご紹介します。
複数選択のサポート
Combobox
コンポーネントと Listbox
コンポーネントの両方に新しい multiple
プロパティを追加し、複数のオプションを選択できるようにしました。
multiple
プロパティを追加し、value
として配列をバインドするだけで準備完了です
function MyCombobox({ items }) { const [selectedItems, setSelectedItems] = useState([]); return ( <Combobox value={selectedItems} onChange={setSelectedItems} multiple> {selectedItems.length > 0 && ( <ul> {selectedItems.map((item) => ( <li key={item}>{item}</li> ))} </ul> )} <Combobox.Input /> <Combobox.Options> {items.map((item) => ( <Combobox.Option key={item} value={item}> {item} </Combobox.Option> ))} </Combobox.Options> </Combobox> );}
詳細については、コンボボックスのドキュメントとリストボックスのドキュメントをご覧ください。
Nullable なコンボボックス
v1.6 より前は、コンボボックスの内容を削除してタブで移動すると、以前に選択したオプションが復元されていました。これは多くの場合理にかなっていますが、コンボボックスの値を本当にクリアしたい場合もあります。
これを可能にする新しい nullable
プロパティを追加しました。プロパティを追加するだけで、以前の値が復元されることなく値を削除できるようになりました
function MyCombobox({ items }) { const [selectedItem, setSelectedItem] = useState([]); return ( <Combobox value={selectedItem} onChange={setSelectedItem} nullable> <Combobox.Input /> <Combobox.Options> {items.map((item) => ( <Combobox.Option key={item} value={item}> {item} </Combobox.Option> ))} </Combobox.Options> </Combobox> );}
簡単な HTML フォームのサポート
Listbox
、Combobox
、Switch
、RadioGroup
などのフォームコンポーネントに name
プロパティを追加すると、コンポーネントの値と同期する非表示の入力が自動的に作成されるようになりました。
これにより、通常のフォーム送信や、Remix の <Form>
コンポーネントのようなもので、そのデータをサーバーに送信するのが非常に簡単になります。
<form action="/projects/1/assignee" method="post"> <Listbox value={selectedPerson} onChange={setSelectedPerson} name="assignee" > {/* ... */} </Listbox> <button>Submit</button></form>
これは数値や文字列などの単純な値だけでなく、オブジェクトでも機能します。1996 年からの角かっこ表記を使用して、複数のフィールドに自動的にシリアル化します
<input type="hidden" name="assignee[id]" value="1" /><input type="hidden" name="assignee[name]" value="Durward Reynolds" />
私が今書いたことをもう一度別のドメインで読みたい場合は、ドキュメントをご覧ください。
スクロール可能なダイアログの改善
ダイアログは文字通り地球上で最も構築が難しいものです。私たちは、厄介なスクロール問題にしばらく取り組んでおり、v1.6 でついにすべて解決したと考えています。
要点は、「外側をクリックして閉じる」の動作方法を変更したことです。以前は、実際のダイアログの背後に配置する Dialog.Overlay
コンポーネントを使用していましたが、そのコンポーネントにクリックハンドラーがあり、クリック時にダイアログを閉じました。原則として、私はこのシンプルさが本当に好きです。特に、ダイアログ内にレンダリングされ、それ自体がポータルなどに他のものをレンダリングしているものがある場合、特定の要素がクリックされたことを検出する方が、特定の要素以外のものがクリックされたことを検出するよりもはるかに癖が少ないです。
このアプローチの問題は、スクロールが必要な長いダイアログがある場合、オーバーレイがスクロールバーの上に配置され、スクロールバーをクリックしようとするとダイアログが閉じてしまうことです。これは望ましくありません!
これを非破壊的な方法で修正するために、代わりに使用できる新しい Dialog.Panel
コンポーネントを追加しました。これで、オーバーレイがクリックされたときに特に閉じるのではなく、そのコンポーネントの外側をクリックするたびにダイアログを閉じるようになりました
<Dialog open={isOpen} onClose={closeModal} className="fixed inset-0 flex items-center justify-center ..."> <Dialog.Overlay className="fixed inset-0 bg-black/25" /> <div className="fixed inset-0 bg-black/25" /> <div className="bg-white shadow-xl rounded-2xl ..."> <Dialog.Panel className="bg-white shadow-xl rounded-2xl ..."> <Dialog.Title>Payment successful</Dialog.Title> {/* ... */} </div> </Dialog.Panel></Dialog>
オーバーレイの代わりに新しいパネルコンポーネントを使用したより完全な例については、更新されたダイアログドキュメントをご覧ください。
より優れたフォーカストラップ
ダイアログが地球上で最も構築が難しいものの 1 つである多くの理由の 1 つは、フォーカストラップ 때문입니다。これに対する最初の試みでは、Tab キーをハイジャックし、次/前の要素を手動でフォーカスして、最後に到達したときにフォーカストラップの最初の項目に戻ることができるようにしました。
これは、人々がフォーカストラップ内でポータルを使用し始めるまでは問題ありません。ダイアログの概念的には内部にあるが、スタイリング上の理由でポータルにレンダリングされているため、実際には内部にない日付ピッカーなどにタブで移動できるため、管理がほぼ不可能になりました。
Robin が、非常にシンプルでクールなソリューションを思いつきました。タブの動作方法を手動で制御しようとする代わりに、フォーカストラップの先頭と末尾に非表示のフォーカス可能な要素を配置するだけです。これらのセンチネル要素のいずれかがフォーカスを受け取ると、最初または最後の要素にいるかどうか、およびユーザーが前後にタブ移動していたかどうかに基づいて、フォーカスを実際に配置する必要がある場所に移動するだけです。
このアプローチでは、Tab キーをまったくハイジャックする必要はありません。ブラウザにすべての作業を実行させ、センチネル要素のいずれかがフォーカスを受け取った場合にのみ手動でフォーカスを移動します。
これを理解した後、他のいくつかのライブラリも同じことをしていることに気づいたので、画期的でも新しいものでもありませんが、この手法を考えたことがない人にとっては非常に賢明で共有する価値があると思いました。
Tailwind UI のチーム管理機能
Tailwind UI を最初にリリースしたとき、「チーム」は私とSteveだけだったので、私たち2人だけで実際にそれをリリースするチャンスを得たいのであれば、多くのことをシンプルに保つ必要がありました。
それらの1つは、チームライセンスでした。私たちは、気の利いたチームメンバー招待フローなどは一切提供していません。Tailwind UI の資格情報をチームと共有するように人々に依頼しただけです。Tailwind UI はユーザー固有の方法では何も行わず、チームのすべてのメンバーが同じエクスペリエンスを得るため、これで事を進めるには十分でした。
さらに私たちにとって、チームの全員のメールアドレスを取得し、フォームに入力し、各人に招待メールを送信し、招待を受け入れてもらうのは、特にサインイン後にすべての人が同じエクスペリエンスを得る場合、管理上の地獄のように感じました。
しかし同時に、何かの資格情報を共有することは非常にローエンドであり、私たちが誇りに思っていた設計上の決定ではありません。私は Tailwind UI と銀行口座で同じパスワード (slayerfan1234
) を使用しています。それを誰とも共有したくありません!
そこで数週間前、私たちはそれを解決して何かを構築することにしました。

私たちがたどり着いたのは、純粋なリンクベースの招待システムでした。招待リンクをコピーして、Slack/Discord などでチームと共有し、必要に応じてリンクをリセットできます。「メンバー」または「オーナー」の権限を人に与えることもでき、これによりチームメンバーを管理したり、請求履歴を表示したりできるかどうかを制御できます。
これにより、面倒なデータ入力をせずにチームを非常に簡単に招待でき、共有パスワードを変更する代わりに、UI で誰かが退職した場合にアクセスを取り消すことができます。
これは、Tailwind UI チームアカウントをお持ちの方なら誰でも今すぐ利用できます。ドロップダウンメニューを開き、「マイチーム」をクリックしてチームに名前を付け、同僚の招待を開始してください。
Tailwind UI ウェブサイトでチームのライセンスを購入するか、個人ライセンスをお持ちで、チームで Tailwind UI の使用を開始したい場合はチームライセンスにアップグレードできます。
Tailwind UI の Vue の例を <script setup>
に更新
Tailwind UI の Vue サポートをリリースして以来、Vue 3 の新しい <script setup>
構文が、シングルファイルコンポーネントを作成するための推奨される方法になりました。
Tailwind UI のすべての Vue の例をこの新しい形式を使用するように更新しました。これにより、大量のボイラープレートが削減されます
<template> <Listbox as="div" v-model="selected"> <!-- ... --> </Listbox></template><script setup> import { ref } from "vue"; import { Listbox, ListboxButton, ListboxLabel, ListboxOption, ListboxOptions } from "@headlessui/vue"; import { CheckIcon, SelectorIcon } from "@heroicons/vue/solid"; const people = [ { id: 1, name: "Wade Cooper" }, // ... ]; const selected = ref(people[3]);</script>
私にとって、絶対的に最高の部分は、components
の下に何も明示的に登録する必要がなくなったことです。スコープ内のコンポーネントはすべて、テンプレートで自動的に使用できるようになります。
<script setup>
を使用すると、Headless UI の React フレーバーで行っているように、Listbox.Button
のような名前空間付きコンポーネントを使用することもできます。Headless UI をこの方法でコンポーネントを公開するようにまだ更新していませんが、おそらくすぐにそれを行う予定です。これにより、大量のインポートを削減できます。
VS Code 用の新しい Tailwind CSS 言語モード
Tailwind は、@tailwind
や @apply
などの多くの非標準 at-rules を使用するため、通常の CSS 言語モードを使用すると VS Code でリント警告が表示されます。
これを回避するために、私たちは常にPostCSS Language Supportプラグインを使用することをお勧めしてきました。これにより、これらの警告は削除されますが、他のすべての CSS IntelliSense サポートも削除されます。
そこで数週間前、Tailwind CSS IntelliSense 拡張機能の一部として、最初の Tailwind CSS 言語モードをリリースしました。これは、組み込みの CSS 言語モードをベースに構築されており、Tailwind 固有の構文の強調表示を追加し、通常表示されるリント警告を修正しますが、必要な CSS IntelliSense 機能を失うことはありません。

Tailwind CSS IntelliSense の最新バージョンをダウンロードし、CSS ファイルの言語モードとして「Tailwind CSS」を選択して試してみてください。
Tailwind Play の「生成された CSS」パネル
過去数か月で、Tailwind Play に多くの小さな改善を加えました。私のお気に入りは、新しい「生成された CSS」パネルです。

HTML から生成されたすべての CSS が表示され、レイヤーでフィルタリングできます。これはトラブルシューティングに非常に役立ちます。内部的には、クラスが検出されないことに関する奇妙な問題をデバッグするために、これを常に使用しています。そのため、動作させるために必要な恐ろしい正規表現手術を実行できます。
また、コードを自動的にフォーマットする(クラスをソートする!)各ペインに「Tidy」ボタン(Cmd + S)と、「コピー」ボタン(Cmd + A Cmd + C、ご存知でしょう)も追加しました。
Refactoring UI ウェブサイトの再設計
Refactoring UI を 2018 年 12 月にリリースしたとき、Steve と私は文字通り、午前 1 時頃に発売前夜に最終ランディングページを設計および構築しました。
何が起こったかというと、私たちはこのセクシーなランディングページ全体を設計しました。その後、私はメーリングリストの全員に送信する発表メールを書いていましたが、私たち両方とも「このメールの内容は素晴らしく、このランディングページのデザインにあるものよりもはるかに説得力がある」と思いました。
しかし、そのコンテンツは私たちが設計したものにはあまり適合しなかったので、土壇場で設計したものをすべて破棄し、新しいコンテンツに基づいてはるかにシンプルなページを急いで作成しました。見た目はまあまあでしたが、私たちが本当に望んでいた非常に美しいエクスペリエンスではありませんでした。
そこで数週間前、私たちはついに何か新しいものをデザインすることにしました。

私はこの本を今でも非常に誇りに思っています。おそらく、これまで作ったものの中で最も誇りに思っています。Goodreads で 4.68 の評価を受けており、1100 以上の評価と 200 近くのレビューがあり、自費出版の電子書籍としては非常に素晴らしいと感じています。
それ以降に学んだことをすべて盛り込んだ第 2 版をいつか作成することを楽しみにしています!
Tailwind CSS テンプレートが近日公開予定
Twitter で少し予告しましたが、ここ数か月間、私たちは本格的な Tailwind CSS ウェブサイトテンプレートの作成に本当に熱心に取り組んできました。
そのうちの 1 つのスニークピークを次に示します。これは、Next.js と Stripe の新しいMarkdoc ライブラリで構築されたドキュメントサイトテンプレートです

これらのリリースに非常に興奮しています。私は製品としての Tailwind UI を本当に誇りに思っていますが、コピーアンドペースト可能なコードスニペット形式の制限の 1 つは、コンポーネント化する方法、重複を最小限に抑える方法、および完全な実稼働対応のウェブサイトとしてアーキテクチャを構築する方法を実際に見せる機会がないことです。
私たちが現在取り組んでいるテンプレートは、そのギャップを埋めるのに最適です。独自のプロジェクトの出発点として美しいテンプレートを入手できるだけでなく、コードを調べて、Tailwind CSS でウェブサイトを構築する方法を正確に学習できます。
これらの正確なリリース日はまだ設定していませんが、来月何かをリリースしたいと考えています。進捗状況に応じて、さらに共有します!