
物事を改善する方法を見つける上で、実際に自分のツールを使って何か現実的なものを構築することに勝るものはありません。
ここ数ヶ月間、Catalystに取り組んできた中で、Headless UIに数多くの改善を加え、コードをさらに少なく記述できるようにし、開発者体験をさらに向上させました。
そして今回、このすべての作業の集大成であるHeadless UI v2.0 for Reactをリリースしました。
注目の新機能をご紹介します。
- 組み込みのアンカー位置指定
- 新しいチェックボックスコンポーネント
- HTMLフォームコンポーネント
- ホバー、フォーカス、アクティブ状態の検出を改善
- コンボボックスのリスト仮想化
- 新しいウェブサイトとドキュメントの改善
npmから@headlessui/react
の最新バージョンをインストールして、プロジェクトに追加してください。
npm install @headlessui/react@latest
v1.xからアップグレードする場合は、アップグレードガイドで変更点をご確認ください。
組み込みのアンカー位置指定
Floating UIをHeadless UIに直接統合したため、ドロップダウンがビューから外れたり、画面上の他の要素によって隠れたりする心配はもうありません。
Menu
、Popover
、Combobox
、Listbox
コンポーネントの新しいanchor
プロパティを使用してアンカー位置を指定し、--anchor-gap
や--anchor-padding
などのCSS変数で配置を微調整します。
スクロールアップ・ダウンして、ドロップダウンの位置が変化するのを確認してください。
import { Menu, MenuButton, MenuItem, MenuItems } from "@headlessui/react";function Example() { return ( <Menu> <MenuButton>Options</MenuButton> <MenuItems anchor="bottom start" className="[--anchor-gap:8px] [--anchor-padding:8px]" > <MenuItem> <button>Edit</button> </MenuItem> <MenuItem> <button>Duplicate</button> </MenuItem> <hr /> <MenuItem> <button>Archive</button> </MenuItem> <MenuItem> <button>Delete</button> </MenuItem> </MenuItems> </Menu> );}
このAPIの本当に素晴らしい点は、sm:[--anchor-gap:4px]
のようなユーティリティクラスを使用してCSS変数を変更することで、異なるブレークポイントでスタイルを調整できることです。
詳細については、各コンポーネントのアンカー位置指定に関するドキュメントをご確認ください。
新しいチェックボックスコンポーネント
既存のRadioGroup
コンポーネントを補完する新しいheadlessなCheckbox
コンポーネントを追加し、完全にカスタムなチェックボックスコントロールを簡単に構築できるようにしました。
これにより、開発中の素晴らしい新機能にいち早くアクセスできます。
import { Checkbox, Description, Field, Label } from "@headlessui/react";import { CheckmarkIcon } from "./icons/checkmark";import clsx from "clsx";function Example() { return ( <Field> <Checkbox defaultChecked className={clsx( "size-4 rounded border bg-white dark:bg-white/5", "data-[checked]:border-transparent data-[checked]:bg-blue-500", "focus:outline-none data-[focus]:outline-2 data-[focus]:outline-offset-2 data-[focus]:outline-blue-500", )} > <CheckmarkIcon className="stroke-white opacity-0 group-data-[checked]:opacity-100" /> </Checkbox> <div> <Label>Enable beta features</Label> <Description>This will give you early access to any awesome new features we're developing.</Description> </div> </Field> );}
チェックボックスは、制御することも、非制御にすることもでき、非表示のinputと状態を自動的に同期させて、HTMLフォームとうまく連携させることができます。
詳細については、Checkboxドキュメントをご覧ください。
HTMLフォームコンポーネント
ネイティブのフォームコントロールをラップするだけのまったく新しいコンポーネントセットを追加しましたが、IDとaria-*
属性を自動的に配線するという面倒な作業をすべて実行します。
以前は、適切に関連付けられた<label>
と説明を持つ単純な<input>
フィールドを構築すると、このようになっていました。
<div> <label id="name-label" for="name-input"> Name </label> <input id="name-input" aria-labelledby="name-label" aria-describedby="name-description" /> <p id="name-description">Use your real name so people will recognize you.</p></div>
そして、Headless UI v2.0のこれらの新しいコンポーネントを使用すると、このようになります。
import { Description, Field, Input, Label } from "@headlessui/react";function Example() { return ( <Field> <Label>Name</Label> <Input name="your_name" /> <Description>Use your real name so people will recognize you.</Description> </Field> );}
新しいField
およびFieldset
コンポーネントは、ネイティブの<fieldset>
要素のようにdisabled状態をカスケードするため、コントロールのグループ全体を一度に簡単に無効にできます。
国を選択して、地域フィールドが有効になるのを確認してください。
import { Button, Description, Field, Fieldset, Input, Label, Legend, Select } from "@headlessui/react";import { regions } from "./countries";export function Example() { const [country, setCountry] = useState(null); return ( <form action="/shipping"> <Fieldset> <Legend>Shipping details</Legend> <Field> <Label>Street address</Label> <Input name="address" /> </Field> <Field> <Label>Country</Label> <Description>We currently only ship to North America.</Description> <Select name="country" value={country} onChange={(event) => setCountry(event.target.value)}> <option></option> <option>Canada</option> <option>Mexico</option> <option>United States</option> </Select> </Field> <Field disabled={!country}> <Label className="data-[disabled]:opacity-40">State/province</Label> <Select name="region" className="data-[disabled]:opacity-50"> <option></option> {country && regions[country].map((region) => <option>{region}</option>)} </Select> </Field> <Button>Submit</Button> </Fieldset> </form> );}
レンダリングされたHTMLでdata-disabled
属性を使用してdisabled状態を公開しています。これにより、関連付けられた<label>
要素のように、ネイティブのdisabled
属性をサポートしない要素でもdisabled状態を公開できるため、各要素のdisabledスタイルを非常に簡単に微調整できます。
全体として、ここでは8つの新しいコンポーネント、Fieldset
、Legend
、Field
、Label
、Description
、Input
、Select
、Textarea
を追加しました。
詳細については、Fieldsetドキュメントから始めて、残りのドキュメントに進んでください。
ホバー、フォーカス、アクティブ状態の検出を改善
内部的には素晴らしいReact Ariaライブラリのフックを使用することで、Headless UIは、ネイティブのCSS疑似クラスよりもさまざまなデバイス間でより一貫して動作する、よりスマートなdata-*
状態属性をコントロールに追加するようになりました。
data-active
—:active
と同様ですが、要素からドラッグオフすると削除されます。data-hover
—:hover
と同様ですが、タッチデバイスではスティッキーなホバー状態を避けるために無視されます。-
data-focus
—:focus-visible
と同様ですが、命令的なフォーカスによる誤検出はありません。
ボタンをクリック、ホバー、フォーカス、ドラッグして、data属性が適用されるのを確認してください。
JavaScriptを使用してこれらのスタイルを適用することがなぜ重要なのかについて詳しく知るには、Devon Govett氏によるこのトピックに関する優れたブログシリーズを読むことを強くお勧めします。
Webは、実際に素晴らしいものを作るためにどれだけの努力が必要なのか、常に驚かされます。
コンボボックスのリスト仮想化
コンボボックスに10万個のアイテムを配置する必要がある場合(ボスにそうするように言われた場合)、リストの仮想化をサポートするために、TanStack VirtualをHeadless UIに統合しました。
新しいvirtual
プロパティを使用してすべてのアイテムを渡し、ComboboxOptions
レンダープロパティを使用して個々のオプションのテンプレートを提供します。
コンボボックスを開き、1,000個のオプションをスクロールしてください。
import { Combobox, ComboboxButton, ComboboxInput, ComboboxOption, ComboboxOptions } from "@headlessui/react";import { ChevronDownIcon } from "@heroicons/react/20/solid";import { useState } from "react";const people = [ { id: 1, name: "Rossie Abernathy" }, { id: 2, name: "Juana Abshire" }, { id: 3, name: "Leonel Abshire" }, { id: 4, name: "Llewellyn Abshire" }, { id: 5, name: "Ramon Abshire" }, // ...up to 1000 people];function Example() { const [query, setQuery] = useState(""); const [selected, setSelected] = useState(people[0]); const filteredPeople = query === "" ? people : people.filter((person) => { return person.name.toLowerCase().includes(query.toLowerCase()); }); return ( <Combobox value={selected} virtual={{ options: filteredPeople }} onChange={(value) => setSelected(value)} onClose={() => setQuery("")} > <div> <ComboboxInput displayValue={(person) => person?.name} onChange={(event) => setQuery(event.target.value)} /> <ComboboxButton> <ChevronDownIcon /> </ComboboxButton> </div> <ComboboxOptions> {({ option: person }) => ( <ComboboxOption key={person.id} value={person}> {person.name} </ComboboxOption> )} </ComboboxOptions> </Combobox> );}
詳細については、新しい仮想スクロールのドキュメントをご覧ください。
新しいウェブサイトとドキュメントの改善
今回のメジャーリリースに伴い、ドキュメントも大幅に刷新し、ウェブサイトも一新しました。

新しいheadlessui.comにアクセスして、ご確認ください。