Completely unstyled, fully accessible UI components, designed to integrate beautifully with Tailwind CSS.

  • By Tailwind Labs
  • Last update: Dec 31, 2022
  • Comments: 16

Headless UI

A set of completely unstyled, fully accessible UI components, designed to integrate beautifully with Tailwind CSS.


Documentation

For full documentation, visit headlessui.dev.

Packages

Name Version Downloads
@headlessui/react npm version npm downloads
@headlessui/vue npm version npm downloads

Community

For help, discussion about best practices, or any other conversation that would benefit from being searchable:

Discuss Headless UI on GitHub

For casual chit-chat with others using the library:

Join the Tailwind CSS Discord Server

Github

https://github.com/tailwindlabs/headlessui

Comments(16)

  • 1

    [Bug]: Uncaught RangeError: Maximum call stack size exceeded. focusElement of Modal

    What package within Headless UI are you using?

    @headlessui/react

    What version of that package are you using?

    1.4.1

    What browser are you using?

    chrome

    Reproduction repository

    Not now.

    Describe your issue

    while we create Modal multiple (2+) it will throw me

    focus-management.esm.js?d1e3:99 Uncaught RangeError: Maximum call stack size exceeded.
        at focusElement (focus-management.esm.js?d1e3:99)
        at eval (use-focus-trap.esm.js?25af:110)
        at handler (use-window-event.esm.js?b5e3:8)
        at focusElement (focus-management.esm.js?d1e3:99)
        at eval (use-focus-trap.esm.js?25af:110)
        at handler (use-window-event.esm.js?b5e3:8)
        at focusElement (focus-management.esm.js?d1e3:99)
        at eval (use-focus-trap.esm.js?25af:110)
        at handler (use-window-event.esm.js?b5e3:8)
        at focusElement (focus-management.esm.js?d1e3:99)
        at eval (use-focus-trap.esm.js?25af:110)
        at handler (use-window-event.esm.js?b5e3:8)
        at focusElement (focus-management.esm.js?d1e3:99)
        at eval (use-focus-trap.esm.js?25af:110)
        at handler (use-window-event.esm.js?b5e3:8)
        at focusElement (focus-management.esm.js?d1e3:99)
        at eval (use-focus-trap.esm.js?25af:110)
        at handler (use-window-event.esm.js?b5e3:8)
        at focusElement (focus-management.esm.js?d1e3:99)
        at eval (use-focus-trap.esm.js?25af:110)
    

    Screenshot here

    BTW, can Modal need to provider flag props to disabled focusElement feature, in most case we don't need this, i think.

  • 2

    Multi-Listbox

    Hey there,

    Just a first attempt/POC for a multi-select Listbox (see #181).

    React-only at this stage.

    https://user-images.githubusercontent.com/690667/123712986-af5edb00-d873-11eb-8085-10dbf11b9fc5.mov

    It involves a few changes, mostly:

    • Tracking the value in theListbox state, to be able to detect selected options while the Listbox is open
    • Checking here and there if value is an array
    • Focus handling

    All in all, it seems to work ok, the only thing I can't get around is that when you use clickable items within Listbox.button (ex: badges with a remove action), the button gain focus on click, and it kind of messes things up.

    I've added a basic test case - just a start.

    Let me know if you'd like me to explore / improve this further, or if you prefer to handle this yourself :)

  • 3

    HeadlessUI is not working with Nuxt 3

    What package within Headless UI are you using?

    @headlessui/vue

    What version of that package are you using?

    "devDependencies": {
        "autoprefixer": "^10.4.0",
        "nuxt3": "latest",
        "postcss": "^8.4.4",
        "tailwindcss": "^2.2.19"
      },
      "dependencies": {
        "@headlessui/vue": "^1.4.2",
        "@heroicons/vue": "^1.0.5",
        "@tailwindcss/forms": "^0.3.4"
      }
    

    Describe your issue

    It works fine with development but when I build nuxt (nuxi build) and start (yarn start) it gives error. I've tried to import headless ui component from @headlessui/vue/dist/index.js instead of @headlessui/vue but its still same.

    TypeError: Cannot read property 'buttonRef' of undefined
        at setup (D:\projects\headless-test\nuxt3-app\.output\server\node_modules\@headlessui\vue\dist\headlessui.cjs.development.js:2622:15)
        at callWithErrorHandling (file:///D:/projects/headless-test/nuxt3-app/.output/server/chunks/index.mjs:7158:22)
        at setupStatefulComponent (file:///D:/projects/headless-test/nuxt3-app/.output/server/chunks/index.mjs:6874:29)
        at setupComponent (file:///D:/projects/headless-test/nuxt3-app/.output/server/chunks/index.mjs:6855:11)
        at renderComponentVNode (file:///D:/projects/headless-test/nuxt3-app/.output/server/chunks/index.mjs:9757:17)
        at renderVNode (file:///D:/projects/headless-test/nuxt3-app/.output/server/chunks/index.mjs:9863:22)
        at renderVNodeChildren (file:///D:/projects/headless-test/nuxt3-app/.output/server/chunks/index.mjs:9878:9)
        at renderElementVNode (file:///D:/projects/headless-test/nuxt3-app/.output/server/chunks/index.mjs:9929:17)
        at renderVNode (file:///D:/projects/headless-test/nuxt3-app/.output/server/chunks/index.mjs:9860:17)
        at renderVNodeChildren (file:///D:/projects/headless-test/nuxt3-app/.output/server/chunks/index.mjs:9878:9)
    [Vue warn]: inject() can only be used inside setup() or functional components.
    TypeError: Cannot read property 'menuState' of undefined
        at Proxy.render$1 (D:\projects\headless-test\nuxt3-app\.output\server\node_modules\@headlessui\vue\dist\headlessui.cjs.development.js:2522:17)
        at renderComponentRoot (file:///D:/projects/headless-test/nuxt3-app/.output/server/chunks/index.mjs:2015:44)
        at renderComponentSubTree (file:///D:/projects/headless-test/nuxt3-app/.output/server/chunks/index.mjs:9828:51)
        at renderComponentVNode (file:///D:/projects/headless-test/nuxt3-app/.output/server/chunks/index.mjs:9773:16)
        at renderVNode (file:///D:/projects/headless-test/nuxt3-app/.output/server/chunks/index.mjs:9863:22)
        at renderVNodeChildren (file:///D:/projects/headless-test/nuxt3-app/.output/server/chunks/index.mjs:9878:9)
        at renderElementVNode (file:///D:/projects/headless-test/nuxt3-app/.output/server/chunks/index.mjs:9929:17)
        at renderVNode (file:///D:/projects/headless-test/nuxt3-app/.output/server/chunks/index.mjs:9860:17)
        at renderVNodeChildren (file:///D:/projects/headless-test/nuxt3-app/.output/server/chunks/index.mjs:9878:9)
        at renderElementVNode (file:///D:/projects/headless-test/nuxt3-app/.output/server/chunks/index.mjs:9929:17)
    [Vue warn]: inject() can only be used inside setup() or functional components.
    [Vue warn]: Unhandled error during execution of watcher callback
    TypeError: Cannot read property 'itemsRef' of undefined
        at ReactiveEffect.fn (D:\projects\headless-test\nuxt3-app\.output\server\node_modules\@headlessui\vue\dist\headlessui.cjs.development.js:2685:24)
        at ReactiveEffect.run (D:\projects\headless-test\nuxt3-app\.output\server\node_modules\@vue\reactivity\dist\reactivity.cjs.js:164:29)
        at ComputedRefImpl.get value [as value] (D:\projects\headless-test\nuxt3-app\.output\server\node_modules\@vue\reactivity\dist\reactivity.cjs.js:1075:39)
        at D:\projects\headless-test\nuxt3-app\.output\server\node_modules\@headlessui\vue\dist\headlessui.cjs.development.js:2336:26
        at callWithErrorHandling (D:\projects\headless-test\nuxt3-app\.output\server\node_modules\@vue\runtime-core\dist\runtime-core.cjs.js:6615:22)
        at callWithAsyncErrorHandling (D:\projects\headless-test\nuxt3-app\.output\server\node_modules\@vue\runtime-core\dist\runtime-core.cjs.js:6624:21)
        at ReactiveEffect.getter [as fn] (D:\projects\headless-test\nuxt3-app\.output\server\node_modules\@vue\runtime-core\dist\runtime-core.cjs.js:6970:24)
        at ReactiveEffect.run (D:\projects\headless-test\nuxt3-app\.output\server\node_modules\@vue\reactivity\dist\reactivity.cjs.js:164:29)
        at doWatch (D:\projects\headless-test\nuxt3-app\.output\server\node_modules\@vue\runtime-core\dist\runtime-core.cjs.js:7078:16)
        at Object.watchEffect (D:\projects\headless-test\nuxt3-app\.output\server\node_modules\@vue\runtime-core\dist\runtime-core.cjs.js:6890:12)
    [Vue warn]: inject() can only be used inside setup() or functional components.
    TypeError: Cannot read property 'menuState' of undefined
        at Proxy.render$1 (D:\projects\headless-test\nuxt3-app\.output\server\node_modules\@headlessui\vue\dist\headlessui.cjs.development.js:2656:17)
        at renderComponentRoot (file:///D:/projects/headless-test/nuxt3-app/.output/server/chunks/index.mjs:2015:44)
        at renderComponentSubTree (file:///D:/projects/headless-test/nuxt3-app/.output/server/chunks/index.mjs:9828:51)
        at renderComponentVNode (file:///D:/projects/headless-test/nuxt3-app/.output/server/chunks/index.mjs:9773:16)
        at renderVNode (file:///D:/projects/headless-test/nuxt3-app/.output/server/chunks/index.mjs:9863:22)
        at renderComponentSubTree (file:///D:/projects/headless-test/nuxt3-app/.output/server/chunks/index.mjs:9828:13)
        at renderComponentVNode (file:///D:/projects/headless-test/nuxt3-app/.output/server/chunks/index.mjs:9773:16)
        at renderVNode (file:///D:/projects/headless-test/nuxt3-app/.output/server/chunks/index.mjs:9863:22)
        at renderComponentSubTree (file:///D:/projects/headless-test/nuxt3-app/.output/server/chunks/index.mjs:9780:9)
        at renderComponentVNode (file:///D:/projects/headless-test/nuxt3-app/.output/server/chunks/index.mjs:9773:16)
    
    
  • 4

    [Bug]: Dialog: There are no focusable elements inside the

    What package within Headless UI are you using?

    @headlessui/react

    What version of that package are you using?

    1.0.0

    What browser are you using?

    Chrome

    Reproduction repository

    https://codesandbox.io/s/gallant-butterfly-wnxjb

    Description

    When there is no focusable element inside modal, it fails with error There are no focusable elements inside the <FocusTrap />. I don't know if it's a feature or a bug, but there isn't said in the documentation that Dialog must include at least one focusable element and I totally see a use-case when there are modals without any focusable element. I think it at least should fail with warning instead of fatal error.

    Anyways I would like to thank you for the great job you all are doing with the tailwind ecosystem, we really appreciate your work!

  • 5

    [Bug]: Interacting with third-party components that use portals inside a Dialog closes the Dialog

    What package within Headless UI are you using?

    @headlessui/vue

    What version of that package are you using?

    1.0.0

    What browser are you using?

    Firefox

    Reproduction repository

    https://codesandbox.io/s/pensive-fast-yxokp?file=/src/App.vue

    Describe your issue

    When any component inside a Dialog renders itself elsewhere in the DOM using a Portal/Teleport, clicking in it closes the entire Dialog. The example above uses vue-flatpickr-component to create a date picker inside a modal, but because it renders the actual flatpickr instance somewhere else in the DOM, clicking any date closes the entire modal.

    The docs on this say:

    When a Dialog is rendered, clicking the DialogOverlay will close the Dialog.

    But that isn't exactly the caseβ€”it's not clicking the DialogOverlay specifically that closes the Dialog, but clicking anything that is not a child of the Dialog in the DOM.

    I believe the relevant part of the code is here: https://github.com/tailwindlabs/headlessui/blob/main/packages/%40headlessui-vue/src/components/dialog/dialog.ts#L174-L184

    What do you think about adding a way to disable that global mousedown handler, or adding an option to actually only close the Dialog based on clicks directly on the DialogOverlay component?

  • 6

    Transition not working in NextJs

    It appears like Transition classes are not there initially on any div(<Transition>) Btw I'm using ( tailwindcss(^1.8.4) , styled-components & twin-macro)

    Sample code I'm trying to test on my NextJs WebApp image

  • 7

    Regression: No transitions on open dialog [v1.6.5]

    What package within Headless UI are you using?

    @headlessui/vue

    What version of that package are you using?

    1.6.5

    What browser are you using?

    Chrome

    Reproduction URL

    https://headlessui.dev/vue/dialog

    Describe your issue

    No transitions take place when dialog initially appears or after closing/opening it again. Transition on close seems to work though. It used to work in v1.6.4.

  • 8

    [Bug]: Performance issues with Select Custom Dropdown on big lists

    What package within Headless UI are you using?

    @headlessui/react

    What version of that package are you using?

    v1.1.1

    What browser are you using?

    Firefox

    Reproduction repository

    https://github.com/khuang7/headlessui-listbox-issue (yarn install then yarn start)

    Describe your issue

    I am trying to populate a pretty big list (500 elements) into the Simple Custom component found in TailwindUI for my project. The hover behavior when trying to select an option becomes quite slow when you try to move the mouse around on all the options. Also generating the list can sometimes be slow as well.

    My workaround this problem is to replace Listbox.Options and Listbox.Option to ul and li html elements so that I can just call 'hover:' on the li elements which improves performance significantly. But I would prefer to keep using the HeadlessUI instead of HTML because I would lose a lot of the ARIA accessibility features.

    I was wondering if there is a way to deal with the performance issues in the HeadlessUI. I could only pinpoint the issue being related to ListboxOptions and ListboxOption.

  • 9

    Feature: close popover manually

    What package within Headless UI are you using?

    @headlessui/vue

    What version of that package are you using?

    v1.0.0

    What browser are you using?

    Chrome

    Reproduction repository

    Describe your issue

    At the moment it is not possible to manually close the popover. Let's say I want to build an editable table. The editable content should be shown in the popover. When I hit the save button it should also close the popover. This is not possible, because the slots does not expose the function togglePopover.

  • 10

    Dialog throws error if there are no focusable elements

    I get the following error if I create a Dialog with no interactive components (just text): Error: There are no focusable elements inside the <FocusTrap />

  • 11

    Control whether Menu closes on Menu.Item click

    Hi there! I have a use case for the Menu component where I don't necessarily want the Menu to close when I click on a Menu.Item. Or, in other words, I would like to be able to control when the menu closes in some way.

  • 12

    overwrites the className of the child component in SSR

    What package within Headless UI are you using?

    @headlessui/react

    What version of that package are you using?

    v1.7.7

    What browser are you using?

    Chrome

    Reproduction URL

    https://codesandbox.io/s/suspicious-mountain-zroyy1?file=/pages/test.jsx

    Describe your issue

    The transition component is rendering strangely and there is a mismatch between the server and the client due to this.

    When the options are as={Fragment}, show={true} and appear=true, in CSR (by clicking next/link in root page) it is working correctly merging child className and the transition props like enterFrom, enter, enterTo. But in SSR, the Transition component replaces the className of the child. In the reproduction URL I attached, the transition component replaced className="relative w-full h-full" with className="opacity-100 translate-x-0".

    Warning: Prop className did not match. Server: "transition duration-500 delay-[500ms] opacity-0 translate-x-5" Client: "relative w-full h-full".

    I went back to version 1.6.0 but the bug continued, and from 1.5.0 onwards, the component disappeared from the html tree (probably I did something wrong)

  • 13

    Focus is not trapped in `Dialog` when opened

    What package within Headless UI are you using?

    @headlessui/react

    What version of that package are you using?

    v1.7.7

    What browser are you using?

    Chrome 108

    Reproduction URL

    https://stackblitz.com/edit/vitejs-vite-2fput4?file=src/App.tsx

    Describe your issue

    Focus is not trapped in Dialog when opened, it can be tabbed to any element else even the browser itself.

    The example is just exactly the same as the official one (copy and pasted). It's working in the official site but not in my local or reprod link

  • 14

    Combobox loses focus when option is selected with mouse click

    What package within Headless UI are you using?

    @headlessui/vue

    What version of that package are you using?

    v1.7.7

    What browser are you using?

    Chrome

    Reproduction URL

    https://user-images.githubusercontent.com/19432263/211062562-4c56cc35-8e83-4198-a8ff-97fed93f1845.mov

    Describe your issue

    Focus behaviour after an option is selected is different for keyboard and mouse interaction. I think when user selects option with mouse click the focus should stay on the input. At the moment the focus seems to go to the first tabbable element in the body.
    There is also a discussion on this problem: https://github.com/tailwindlabs/headlessui/discussions/1984

  • 15

    Improve Combobox accessibility

    Active WIP

    This PR improves the Combobox because there are some differences compared to the Listbox that we didn't properly account for.

    Big thanks to @afercia for providing us with a wonderful issue report that talks about the differences.

    This commit fixes a few issues:

    • The aria-selected value was always based on the selected value (which is correct for the Listbox) but it should be based on the visually selected value (in our case, this is the active value).
    • We dropped an incorrect aria-activedescendant attribute from the ComboboxOptions, it should only exist on the ComboboxInput.
    • We added a missing aria-autocomplete to the ComboboxInput, with a value of list so that assistive technology better understands what kind of Combobox we are dealing with.
    • This also includes a VoiceOver bug fix where the announcements aren't properly happening unless a change to the ComboboxInput is made. Now we will trigger a change to the input when we open the Combobox, we will set the value to '' and re-set to whatever value was present. We also make sure that the cursor position / selected range is reset so that the end user doesn't feel any differences (except for a better working VoiceOver on macOS).

    Some additional findings:

    I also think that this is impossible to get right, so we have to chose the "best" way we think. Should we start announcing these changes in state manually instead of relying on on VoiceOver? πŸ€”

    Some fun differences between macOS Safari + VoiceOver and macOS Chrome + VoiceOver:

    So while using the active state for aria-selected gives you at least consistent announcements, it also means that there is no way to differentiate between selected and active.

    Single Value Mode

    | Scenario | Implementation | Chrome Result | Expected? | Safari Result | Expected? | | :-------------------------------------------------------- | :------------------------------------------- | :-------------------------------------------------------------------------------------- | :-------: | :------------------------------------------------------------------------------------------------------------------ | :-------: | | Use aria-selected based on the visual selected value. | aria-selected: active ? true : undefined | Reads out every single value as "selected". | ❌ | Reads out every single value as "selected". | ❌ | | Β  | aria-selected: active | Reads out every single value as "selected". | ❌ | Reads out every single value as "selected". | ❌ | | Use aria-selected based on the actual selected value. | aria-selected: selected ? true : undefined | Reads out every single value as "selected". | ❌ | Reads out every single value as "selected", but stops announcing values listed after the actual selected value. | ❌ | | Β  | aria-selected: selected | Reads out every single value as "not selected", except for the actual selected value. | βœ… | Reads out every single value as "selected", but stops announcing values listed after the actual selected value. | ❌ | | Use aria-checked based on the visual selected value. | aria-checked: active ? true : undefined | Reads out every single value as "selected". | ❌ | Reads out every single value as "selected". | ❌ | | Β  | aria-checked: active | Reads out every single value as "selected". | ❌ | Reads out every single value as "selected". | ❌ | | Use aria-checked based on the actual selected value. | aria-checked: selected ? true : undefined | Reads out every single value as "selected". | ❌ | Reads out every single value as "selected". | ❌ | | Β  | aria-selected: selected | Reads out every single value as "selected". | ❌ | Reads out every single value as "selected". | ❌ |

    Multi Value Mode

    | Scenario | Implementation | Chrome Result | Expected? | Safari Result | Expected? | | :-------------------------------------------------------- | :------------------------------------------- | :--------------------------------------------------------------------------------------- | :-------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :-------: | | Use aria-selected based on the visual selected value. | aria-selected: active ? true : undefined | Reads out every single value as "selected". | ❌ | Initially doesn't read out anything, but if you type something and undo that, then it reads out every single value as "selected". | ❌ | | Β  | aria-selected: active | Reads out every single value as "selected". | ❌ | Initially doesn't read out anything, but if you type something and undo that, then it reads out every single value as "selected". | ❌ | | Use aria-selected based on the actual selected value. | aria-selected: selected ? true : undefined | Reads out every single value as "selected". | ❌ | Initially doesn't read out anything, but if you type something and undo that, then it reads out every single value as "selected", but stops announcing values listed after the first actual selected value. | ❌ | | Β  | aria-selected: selected | Reads out every single value as "not selected", except for the actual selected values. | βœ… | Initially doesn't read out anything, but if you type something and undo that, then it reads out every single value as "selected", but stops announcing values listed after the first actual selected value. | ❌ | | Use aria-checked based on the visual selected value. | aria-checked: active ? true : undefined | Reads out every single value as "selected". | ❌ | Initially doesn't read out anything, but if you type something and undo that, then it reads out every single value as "selected". | ❌ | | Β  | aria-checked: active | Reads out every single value as "selected". | ❌ | Initially doesn't read out anything, but if you type something and undo that, then it reads out every single value as "selected". | ❌ | | Use aria-checked based on the actual selected value. | aria-checked: selected ? true : undefined | Reads out every single value as "selected". | ❌ | Initially doesn't read out anything, but if you type something and undo that, then it reads out every single value as "selected". | ❌ | | Β  | aria-selected: selected | Reads out every single value as "selected". | ❌ | Initially doesn't read out anything, but if you type something and undo that, then it reads out every single value as "selected". | ❌ |

    Fixes: #2129 Co-authored-by: Andrea Fercia [email protected]

  • 16

    VoiceOver doesn't read the Combobox.Option (single value selection)

    What package within Headless UI are you using?

    @headlessui/react

    What version of that package are you using?

    v1.7.1

    What browser are you using?

    Safari and VoiceOver (macOS)

    Reproduction URL

    Demo page https://headlessui.com/react/combobox or run the playground and go to http://localhost:3000/combobox/combobox-with-pure-tailwind

    Describe your issue

    Hello. Thank you for making headlessui components as accessible as possible, much appreciated.

    I read some related issues / PRs and it seems to me there's a bit of misunderstanding about the usage of aria-selected on the combobox listbox. See for example https://github.com/tailwindlabs/headlessui/pull/1812, https://github.com/tailwindlabs/headlessui/issues/1599, and https://github.com/tailwindlabs/headlessui/pull/1243.

    Safari and VoiceOver (macOS) don't read the Combobox.Option because the aria-selected usage in a combobox listbox is supposed to be a bit different from the one in a standard listbox. It's a subtle difference but in all the comboboxes I've helped to build in the past it always turned out that other screen readers (NVDA, JAWS) are happy with aria-activedescendant and do not care much about aria-selected. Instead, VoiceOver refuses to read the options if aria-selected is omitted or used incorrectly.

    In the ARIA authoring practices, the example that is closest to the headlessui Combobox (single selection) is the Editable Combobox with List Autocomplete.

    Quoting from the Listbox Popup Role, Property, State section of the W3C combobox example:

    • [aria-selected="true"] Specified on an option in the listbox when it is visually highlighted as selected.
    • [aria-selected="true"] Occurs only when an option in the list is referenced by aria-activedescendant.

    That is: in this pattern, aria-selected needs to be only set on the currently 'highlighted' option. It doesn't have anything to do with the 'selection' state, which is a concept that is extraneous to the W3C combobox pattern.

    This is also mentioned in the main page of the Combobox pattern: https://www.w3.org/WAI/ARIA/apg/patterns/combobox/#wai-aria-roles-states-and-properties-6

    For a combobox that controls a listbox, grid, or tree popup, when a suggested value is visually indicated as the currently selected value, the option, gridcell, row, or treeitem containing that value has aria-selected set to true.

    This is different from the (stand alone) Listbox pattern, where aria-selected is indeed used for the selection state.

    In short: in the Combobox Listbox, aria-selected should be based on the active prop rather than the selected prop. Of course, the multiselectable pattern needs to be handled differently as well as the stand-alone Listbox.

    Note: in https://github.com/tailwindlabs/headlessui/issues/1599#issuecomment-1235421018 it is mentioned that this woudl make VoiceOver announce every active option as selected. While slightly annoying, this is up to the screen reader implementation and it's in a way the expected behavior. Other screen readers opted to not announce selected, for example NVDA doesn't, but again this is a vendor's choice. Regardless, aria-selected=true needs to be set only on the highlighted option referenced by aria-activedescendant.

    To reproduce the bug:

    • Go to the Combobox demo page https://headlessui.com/react/combobox or run the playground and go to http://localhost:3000/combobox/combobox-with-pure-tailwind
    • Use Safari and VoiceOver (macOS).
    • Note: to make sure VoiceOver runs with default preferences:
      • Open VoiceOver Utility.
      • In the macOS menu bar, select File > Reset All VoiceOver Preferences... then press Reset in the confirmation dialog.
    • Focus the combobox input field.
    • Press the Down Arrow key to open the listbox.
    • Press the Down Arrow key to navigate through the options.
    • Observe that VoiceOver doesn't read out the options.
    • Clear the current value and type 'co'.
    • Press the Down Arrow key to navigate through the options.
    • Observe that VoiceOver doesn't read out the options.
    • Clear the current value and enter another string, delete some characters, add new ones, etc..
    • Press the Down Arrow key to navigate through the options.
    • Observe that VoiceOver doesn't read out the options.

    W3C example:

    • Go to the W3C example at https://www.w3.org/WAI/ARIA/apg/example-index/combobox/combobox-autocomplete-list.html
    • Focus the combobox input field.
    • Type 'ne'.
      • Note: the W3C example is a bit buggy, you may need to delete 'ne' and type it again while the listbox is open.
    • Press the Down Arrow key to navigate through the options.
    • Observe that VoiceOver does read out the options.