揭露

一份簡單、易於無障礙使用的基礎,用於建立自訂 UI 來顯示和隱藏內容,例如可切換的手風琴面板。

首先,透過 npm 安裝 Headless UI

npm install @headlessui/react

揭露是利用 DisclosureDisclosure.ButtonDisclosure.Panel 元件建立的。

按一下按鈕時,系統會自動開啟/關閉面板,所有元件都會接收合適的 aria-* 相關屬性,例如 aria-expandedaria-controls

import { Disclosure } from '@headlessui/react' function MyDisclosure() { return ( <Disclosure> <Disclosure.Button className="py-2"> Is team pricing available? </Disclosure.Button> <Disclosure.Panel className="text-gray-500"> Yes! You can purchase a license that you can share with your entire team. </Disclosure.Panel> </Disclosure> ) }

Headless UI 會追蹤每個元件的許多狀態,例如目前選取的是哪個清單方塊選項、快顯視窗是開啟還是關閉、或揭露中的哪個項目目前使用鍵盤啟用。

但是,由於元件是 headless 且在預設上完全沒有樣式,因此在你自行提供所需的各狀態樣式之前,你無法在你的 UI 中看到這些資訊。

每個元件都透過你可以用來有條件套用不同樣式或渲染不同內容的 渲染 prop 公開目前狀態資訊。

例如,Disclosure 元件公開了一個 open 狀態,用來說明揭露目前是否開啟。

import { Disclosure } from '@headlessui/react' import { ChevronRightIcon } from '@heroicons/react/20/solid' function MyDisclosure() { return ( <Disclosure>
{({ open }) => (
/* Use the `open` state to conditionally change the direction of an icon. */ <> <Disclosure.Button> Do you offer technical support?
<ChevronRightIcon className={open ? 'rotate-90 transform' : ''} />
</Disclosure.Button> <Disclosure.Panel>No</Disclosure.Panel> </> )} </Disclosure> ) }

每個元件的完整渲染 prop API,請參閱 元件 API 文件

每個元件也會透過 data-headlessui-state 屬性顯示其目前狀態的資訊,你可以使用這些資訊來有條件套用不同的樣式。

呈現 props API 中的任何狀態為 true,它們會以空白分隔字串的形式列在這個屬性中,因此你可以使用 [attr~=value] 形式的 CSS 屬性選擇器 來鎖定它們。

例如,以下顯示的是 Disclosure 元件在公開時呈現的狀態

<!-- Rendered `Disclosure` --> <div data-headlessui-state="open"> <button data-headlessui-state="open">Do you offer technical support?</button> <div data-headlessui-state="open">No</div> </div>

如果你使用 Tailwind CSS,則可以使用 @headlessui/tailwindcss 外掛程式以 ui-open:* 等變數來鎖定這個屬性。

import { Disclosure } from '@headlessui/react' import { ChevronRightIcon } from '@heroicons/react/20/solid' function MyDisclosure() { return ( <Disclosure> <Disclosure.Button> Do you offer technical support?
<ChevronRightIcon className="ui-open:rotate-90 ui-open:transform" />
</Disclosure.Button> <Disclosure.Panel>No</Disclosure.Panel> </Disclosure> ) }

若要手動關閉其面板的子項目時公開,請將此子項目呈現為 Disclosure.Button。你可以使用 as props 來自訂正在呈現的元素。

import { Disclosure } from '@headlessui/react' import MyLink from './MyLink' function MyDisclosure() { return ( <Disclosure> <Disclosure.Button>Open mobile menu</Disclosure.Button> <Disclosure.Panel>
<Disclosure.Button as={MyLink} href="/home">
Home
</Disclosure.Button>
{/* ... */} </Disclosure.Panel> </Disclosure> ) }

當使用於包含連結且希望在導覽到下一個頁面時會關閉公開的行動裝置選單等時,這項功能特別有用。

或者,DisclosureDisclosure.Panel 會顯示 close() 呈現 props,你可以使用它來強制關閉面板,例如在執行異步動作之後

import { Disclosure } from '@headlessui/react' function MyDisclosure() { return ( <Disclosure> <Disclosure.Button>Terms</Disclosure.Button> <Disclosure.Panel>
{({ close }) => (
<button onClick={async () => {
await fetch('/accept-terms', { method: 'POST' })
close()
}}
>
Read and accept </button> )} </Disclosure.Panel> </Disclosure> ) }

預設情況下,在呼叫 close() 之後,Disclosure.Button 會接收焦點,但你可以透過將參照傳遞到 close(ref) 來變更此設定。

若要對選單面板的開啟/關閉動畫化,請使用提供的 Transition 元件。你只需要將 Disclosure.Panel 包在 <Transition> 中,系統就會自動套用轉換。

import { Disclosure, Transition } from '@headlessui/react' function MyDisclosure() { return ( <Disclosure> <Disclosure.Button>Is team pricing available?</Disclosure.Button>
<Transition
enter="transition duration-100 ease-out"
enterFrom="transform scale-95 opacity-0"
enterTo="transform scale-100 opacity-100"
leave="transition duration-75 ease-out"
leaveFrom="transform scale-100 opacity-100"
leaveTo="transform scale-95 opacity-0"
>
<Disclosure.Panel>
Yes! You can purchase a license that you can share with your entire
team. </Disclosure.Panel> </Transition> </Disclosure> ) }

預設情況下,我們的內建 Transition 元件會自動與 Disclosure 元件通訊,來處理已開啟/已關閉狀態。不過,如果你需要對這種行為有更多控制權,則可以明確定義它

import { Disclosure, Transition } from '@headlessui/react' function MyDisclosure() { return ( <Disclosure>
{({ open }) => (
<>
<Disclosure.Button>Is team pricing available?</Disclosure.Button> {/* Use the `Transition` + `open` render prop argument to add transitions. */}
<Transition
show={open}
enter="transition duration-100 ease-out"
enterFrom="transform scale-95 opacity-0"
enterTo="transform scale-100 opacity-100"
leave="transition duration-75 ease-out"
leaveFrom="transform scale-100 opacity-100"
leaveTo="transform scale-95 opacity-0"
>
{/* Don't forget to add `static` to your `Disclosure.Panel`! */}
<Disclosure.Panel static>
Yes! You can purchase a license that you can share with your
entire team.
</Disclosure.Panel>
</Transition> </> )} </Disclosure> ) }

由於 Headless UI 元件無需呈現,因此也可以與 React ecosystem 中的其他動畫函式庫,例如 Framer MotionReact Spring 搭配得很好。

Disclosure 和其子組件各自會呈現適於該組件的預設元素:Button 會呈現 <button>Panel 會呈現 <div>。相對地,根部 Disclosure 組件不會呈現任何元素,而是在預設情況下直接呈現其下層元素。

使用 as prop 可將組件呈現為其他元素或您自訂的組件,並確保您的自訂組件傳送 ref,讓 Headless UI 能正確地聯繫事物。

import { forwardRef, Fragment } from 'react' import { Disclosure } from '@headlessui/react'
let MyCustomButton = forwardRef(function (props, ref) {
return <button className="..." ref={ref} {...props} />
})
function MyDisclosure() { return (
<Disclosure as="div">
<Disclosure.Button as={MyCustomButton}>
What languages do you support? </Disclosure.Button>
<Disclosure.Panel as="ul">
<li>HTML</li> <li>CSS</li> <li>JavaScript</li> </Disclosure.Panel> </Disclosure> ) }

點選 Disclosure.Button 會切換 Disclosure 的 panel 打開和關閉。

指令說明

Enter or SpaceDisclosure.Button 聚焦時。

切換 panel

所有相關的 ARIA 屬性均自動管理。

主要的 Disclosure 組件。

prop預設值說明
asFragment
字串 | 組件

Disclosure 應呈現為該元素或組件。

defaultOpenfalse
布林值

Disclosure 組件是否應在預設情況下開啟。

Render Prop說明
open

布林值

disclosure 是否開啟。

close

(ref?: ref | HTMLElement) => void

關閉 disclosure 並重新聚焦 Disclosure.Button。選擇性傳入 refHTMLElement 以聚焦該元素。

切換 Disclosure 的觸發器組件。

prop預設值說明
asbutton
字串 | 組件

Disclosure.Button 應呈現為該元素或組件。

Render Prop說明
open

布林值

disclosure 是否開啟。

這個元件包含你的揭露內容。

prop預設值說明
asdiv
字串 | 組件

Disclosure.Panel 應該用作元素或元件。

staticfalse
布林值

這個元素是否應該忽略內部管理的開啟/關閉狀態。

注意:staticunmount 不能同時使用。假如你嘗試這麼做,你會收到一個 TypeScript 錯誤。

unmounttrue
布林值

這個元素是否應該根據開啟/關閉狀態解除掛載或隱藏。

注意:staticunmount 不能同時使用。假如你嘗試這麼做,你會收到一個 TypeScript 錯誤。

Render Prop說明
open

布林值

disclosure 是否開啟。

close

(ref?: ref | HTMLElement) => void

關閉 disclosure 並重新聚焦 Disclosure.Button。選擇性傳入 refHTMLElement 以聚焦該元素。

如果你有興趣使用 Headless UI 和 Tailwind CSS 預先設計的元件範例,可以查看 Tailwind UI,這是我們打造的一系列設計精美且製作精良的元件。

這是支援我們對這種開源專案工作的絕佳方式,也讓我們可以改進這些專案,並持續維持良好維護。