Parity
This page shows the state of each Vase UI family against Base UI. A family is one folder of Base UI that Vase UI ports, and the column Component shows which families are components. A component that Base UI does not have is not a family. The site build makes this page from the parity folder of the repository, with the functions of pnpm parity:check. The build fails when that check fails.
Status: 38 of 38 components. The guarantee starts with the first full release.
| Tracked commit | Described as | Date | Release |
|---|---|---|---|
5aacdbb | v1.9.0-149-g5aacdbb7 | 2026-11-10 | 1.9.0 |
| Family | Component | Matched commit | Sources ported | Cases matched | Queued changes |
|---|---|---|---|---|---|
| accordion | Yes | 5aacdbb 2026-11-10 | 16 of 16 | 46 of 47 | 0 |
| alert-dialog | Yes | 5aacdbb 2026-11-10 | 6 of 6 | 10 of 10 | 0 |
| autocomplete | Yes | 5aacdbb 2026-11-10 | 12 of 12 | 104 of 107 | 0 |
| avatar | Yes | 5aacdbb 2026-11-10 | 9 of 9 | 44 of 44 | 0 |
| button | Yes | 5aacdbb 2026-11-10 | 3 of 3 | 9 of 9 | 0 |
| checkbox | Yes | 5aacdbb 2026-11-10 | 8 of 8 | 101 of 106 | 0 |
| checkbox-group | Yes | 5aacdbb 2026-11-10 | 5 of 5 | 85 of 89 | 0 |
| collapsible | Yes | 5aacdbb 2026-11-10 | 13 of 13 | 53 of 55 | 0 |
| combobox | Yes | 5aacdbb 2026-11-10 | 60 of 60 | 730 of 739 | 0 |
| common | No | 5aacdbb 2026-11-10 | 33 of 61 | 60 of 175 | 0 |
| context-menu | Yes | 5aacdbb 2026-11-10 | 9 of 9 | 28 of 32 | 0 |
| csp-provider | No | 5aacdbb 2026-11-10 | 4 of 5 | 4 of 4 | 0 |
| delay-group | No | 5aacdbb 2026-11-10 | 1 of 1 | 6 of 6 | 0 |
| dialog | Yes | 5aacdbb 2026-11-10 | 24 of 24 | 118 of 124 | 0 |
| direction-provider | No | 5aacdbb 2026-11-10 | 4 of 5 | 2 of 2 | 0 |
| drawer | Yes | 5aacdbb 2026-11-10 | 31 of 31 | 275 of 279 | 0 |
| field | Yes | 5aacdbb 2026-11-10 | 18 of 18 | 164 of 169 | 0 |
| fieldset | Yes | 5aacdbb 2026-11-10 | 6 of 6 | 10 of 11 | 0 |
| filter-dropdown | No | 5aacdbb 2026-11-10 | 13 of 13 | 15 of 15 | 0 |
| focus | No | 5aacdbb 2026-11-10 | 6 of 6 | 113 of 113 | 0 |
| form | Yes | 5aacdbb 2026-11-10 | 4 of 4 | 36 of 36 | 0 |
| handbook-animation | No | 5aacdbb 2026-11-10 | 0 of 0 | 0 of 0 | 0 |
| handbook-forms | No | 5aacdbb 2026-11-10 | 0 of 0 | 0 of 0 | 0 |
| input | Yes | 5aacdbb 2026-11-10 | 3 of 3 | 0 of 0 | 0 |
| interactions | No | 5aacdbb 2026-11-10 | 15 of 15 | 194 of 199 | 0 |
| internals | No | 5aacdbb 2026-11-10 | 75 of 80 | 248 of 275 | 0 |
| menu | Yes | 5aacdbb 2026-11-10 | 76 of 76 | 558 of 572 | 0 |
| menubar | Yes | 5aacdbb 2026-11-10 | 4 of 4 | 44 of 44 | 0 |
| merge-props | No | 5aacdbb 2026-11-10 | 2 of 2 | 30 of 30 | 0 |
| meter | Yes | 5aacdbb 2026-11-10 | 8 of 8 | 25 of 25 | 0 |
| navigation-menu | Yes | 5aacdbb 2026-11-10 | 33 of 33 | 122 of 129 | 0 |
| number-field | Yes | 5aacdbb 2026-11-10 | 25 of 25 | 473 of 475 | 0 |
| otp-field | Yes | 5aacdbb 2026-11-10 | 9 of 9 | 159 of 163 | 0 |
| popover | Yes | 5aacdbb 2026-11-10 | 29 of 29 | 160 of 163 | 0 |
| popups | No | 5aacdbb 2026-11-10 | 32 of 32 | 264 of 265 | 0 |
| portal | No | 5aacdbb 2026-11-10 | 2 of 2 | 14 of 17 | 0 |
| preview-card | Yes | 5aacdbb 2026-11-10 | 24 of 24 | 59 of 59 | 0 |
| progress | Yes | 5aacdbb 2026-11-10 | 14 of 14 | 25 of 25 | 0 |
| radio | Yes | 5aacdbb 2026-11-10 | 8 of 8 | 18 of 19 | 0 |
| radio-group | Yes | 5aacdbb 2026-11-10 | 4 of 4 | 83 of 88 | 0 |
| scroll-area | Yes | 5aacdbb 2026-11-10 | 22 of 22 | 97 of 98 | 0 |
| select | Yes | 5aacdbb 2026-11-10 | 40 of 40 | 327 of 332 | 0 |
| separator | Yes | 5aacdbb 2026-11-10 | 3 of 3 | 2 of 2 | 0 |
| slider | Yes | 5aacdbb 2026-11-10 | 26 of 27 | 235 of 237 | 0 |
| switch | Yes | 5aacdbb 2026-11-10 | 8 of 8 | 67 of 69 | 0 |
| tabs | Yes | 5aacdbb 2026-11-10 | 17 of 18 | 138 of 142 | 0 |
| toast | Yes | 5aacdbb 2026-11-10 | 36 of 36 | 218 of 224 | 0 |
| toggle | Yes | 5aacdbb 2026-11-10 | 3 of 3 | 7 of 7 | 0 |
| toggle-group | Yes | 5aacdbb 2026-11-10 | 4 of 4 | 23 of 23 | 0 |
| toolbar | Yes | 5aacdbb 2026-11-10 | 16 of 16 | 57 of 57 | 0 |
| tooltip | Yes | 5aacdbb 2026-11-10 | 24 of 24 | 120 of 121 | 0 |
| tree | No | 5aacdbb 2026-11-10 | 3 of 3 | 6 of 6 | 0 |
| unstable-use-media-query | No | 5aacdbb 2026-11-10 | 1 of 1 | 2 of 4 | 0 |
| use-render | No | 5aacdbb 2026-11-10 | 2 of 2 | 14 of 14 | 0 |
| utils | No | 5aacdbb 2026-11-10 | 28 of 30 | 46 of 49 | 0 |
| viewport | No | 5aacdbb 2026-11-10 | 5 of 5 | 13 of 13 | 0 |
Families
Each source file, test and API row of Base UI needs a counterpart in Vase UI, or an exception row. A row has a reason and one of four kinds:
replaced: another Vase UI test covers the thing. The row names that test.not-applicable: the thing is for something that only React has. The kind also covers an upstream file that no Base UI component imports.engine: the case does not run in one browser engine because of the browser. The row names the engine and links to a browser bug or an upstream issue.waits-on: the thing waits for an upstream file or folder that Vase UI does not have yet. The row names that path. A row that names a source file waits for the file itself.
A function that returns a different type from the type that Base UI documents has a row with the Vase UI type and the reason.
accordion
Matches the Base UI commit 5aacdbb of 2026-11-10.
Queued changes: 0.
Vase UI rows with no upstream row: 17.
| Files | Total | Ported | Replaced | Not applicable | Waits on |
|---|---|---|---|---|---|
| Sources | 16 | 16 | 0 | 0 | 0 |
| Type tests | 1 | 1 | 0 | 0 | 0 |
| Tests | Total | Matched | Replaced | Not applicable | Waits on | Engine Chromium | Engine Firefox | Engine WebKit |
|---|---|---|---|---|---|---|---|---|
| Cases | 47 | 46 | 1 | 0 | 0 | 0 | 0 | 0 |
| Suite calls | 5 | 5 | 0 | 0 | 0 | 0 | 0 | 0 |
| API | Total | Matched | Excepted |
|---|---|---|---|
| Entries | 11 | 11 | 0 |
| Prop rows | 30 | 25 | 5 |
Exception rows: 6.
| Thing | Kind | Reason | Detail |
|---|---|---|---|
| packages/react/src/accordion/panel/AccordionPanel.test.tsx, block "<Accordion.Panel /> > server-side rendering" | replaced | A server render runs in Node with no DOM. The case is in the test file that the Node project runs, with the same title. | Test: packages/vue/src/accordion/panel/AccordionPanel.node.test.tsx |
| docs/src/app/(docs)/react/components/accordion/types.md, entry "Root", prop "loopFocus" | not-applicable | Base UI deprecated the prop, and it has no effect. parity/scope.json lists it as not ported. | |
| docs/src/app/(docs)/react/components/accordion/types.md, entry "Root", prop "orientation" | not-applicable | Base UI deprecated the prop, and it does not change the keyboard. parity/scope.json lists it as not ported. The state keeps orientation with the default value of Base UI, vertical, so each part has data-orientation="vertical". | |
| docs/src/app/(docs)/react/components/accordion/types.md, entry "Root", prop "keepMounted" | not-applicable | The part must tell an absent keepMounted from keepMounted={false}, because only the explicit false gets a warning when hiddenUntilFound is set. A Boolean prop that is absent is false in Vue, so the declared default is undefined. The part uses false for an absent prop, which is the default that Base UI documents. | |
| docs/src/app/(docs)/react/components/accordion/types.md, entry "Panel", prop "hiddenUntilFound" | not-applicable | An absent hiddenUntilFound takes the value of Accordion.Root, and hiddenUntilFound={false} overrides it. A Boolean prop that is absent is false in Vue, so the declared default is undefined. With no value on the root, the part uses false, which is the default that Base UI documents. | |
| docs/src/app/(docs)/react/components/accordion/types.md, entry "Panel", prop "keepMounted" | not-applicable | An absent keepMounted takes the value of Accordion.Root, and only an explicit keepMounted={false} gets a warning when hiddenUntilFound is set. A Boolean prop that is absent is false in Vue, so the declared default is undefined. With no value on the root, the part uses false, which is the default that Base UI documents. |
Additions to the Base UI API: 3.
| Entry | Kind | Name | Reason |
|---|---|---|---|
| Trigger | prop | disabled | The part reads disabled for its state and for useButton, so it is a declared prop. Base UI reads it from the native attributes of the trigger, and types.md has no row for a native attribute. |
| Trigger | prop | id | The part reads id to replace an absent or empty id with the generated id, and to give it to the item for aria-labelledby of the panel. It is thus a declared prop. Base UI reads it from the native attributes of the trigger, and types.md has no row for a native attribute. |
| Panel | prop | id | The part reads id to give it to the item for aria-controls of the trigger, so it is a declared prop. Base UI reads it from the native attributes of the panel, and types.md has no row for a native attribute. |
Return types that differ from Base UI: 0.
alert-dialog
Matches the Base UI commit 5aacdbb of 2026-11-10.
Queued changes: 0.
Vase UI rows with no upstream row: 8.
| Files | Total | Ported | Replaced | Not applicable | Waits on |
|---|---|---|---|---|---|
| Sources | 6 | 6 | 0 | 0 | 0 |
| Type tests | 1 | 1 | 0 | 0 | 0 |
| Tests | Total | Matched | Replaced | Not applicable | Waits on | Engine Chromium | Engine Firefox | Engine WebKit |
|---|---|---|---|---|---|---|---|---|
| Cases | 10 | 10 | 0 | 0 | 0 | 0 | 0 | 0 |
| Suite calls | 3 | 3 | 0 | 0 | 0 | 0 | 0 | 0 |
| API | Total | Matched | Excepted |
|---|---|---|---|
| Entries | 17 | 17 | 0 |
| Prop rows | 43 | 43 | 0 |
Exception rows: 0.
Additions to the Base UI API: 4.
| Entry | Kind | Name | Reason |
|---|---|---|---|
| Trigger | prop | disabled | The part gives disabled to Dialog.Trigger, which reads it for its state and for useButton, so it is a declared prop. Base UI reads it from the native attributes of the trigger, and types.md has no row for a native attribute. |
| Title | prop | id | The part gives id to the root for aria-labelledby of the popup and follows a change of it, so it is a declared prop. Base UI reads it from the native attributes of the title, and types.md has no row for a native attribute. |
| Description | prop | id | The part gives id to the root for aria-describedby of the popup and follows a change of it, so it is a declared prop. Base UI reads it from the native attributes of the description, and types.md has no row for a native attribute. |
| Close | prop | disabled | The part reads disabled for its state and for useButton, so it is a declared prop. Base UI reads it from the native attributes of the button, and types.md has no row for a native attribute. |
Return types that differ from Base UI: 1.
| Entry | Vase UI returns | Reason |
|---|---|---|
| createHandle | AlertDialogHandle<Payload> | AlertDialog.Handle is the class AlertDialogHandle. The declaration of the function names the class, and Base UI documents the same class by its name in the namespace. |
autocomplete
Matches the Base UI commit 5aacdbb of 2026-11-10.
Queued changes: 0.
Vase UI rows with no upstream row: 17.
| Files | Total | Ported | Replaced | Not applicable | Waits on |
|---|---|---|---|---|---|
| Sources | 12 | 12 | 0 | 0 | 0 |
| Type tests | 1 | 1 | 0 | 0 | 0 |
| Tests | Total | Matched | Replaced | Not applicable | Waits on | Engine Chromium | Engine Firefox | Engine WebKit |
|---|---|---|---|---|---|---|---|---|
| Cases | 107 | 104 | 3 | 0 | 0 | 0 | 0 | 0 |
| Suite calls | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| API | Total | Matched | Excepted |
|---|---|---|---|
| Entries | 34 | 34 | 0 |
| Prop rows | 121 | 121 | 0 |
Exception rows: 3.
| Thing | Kind | Reason | Detail |
|---|---|---|---|
| packages/react/src/autocomplete/root/AutocompleteRoot.test.tsx, case "<Autocomplete.Root /> > Form > server-renders only the inline input name for native FormData" | replaced | A server render runs in Node with no DOM. The case is in the test file that the Node project runs, with the same title. | Test: packages/vue/src/autocomplete/root/AutocompleteRoot.node.test.tsx |
| packages/react/src/autocomplete/root/AutocompleteRoot.test.tsx, case "<Autocomplete.Root /> > Form > server-renders only the outside-popup input name for native FormData" | replaced | A server render runs in Node with no DOM. The case is in the test file that the Node project runs, with the same title. | Test: packages/vue/src/autocomplete/root/AutocompleteRoot.node.test.tsx |
| packages/react/src/autocomplete/root/AutocompleteRoot.test.tsx, case "<Autocomplete.Root /> > Form > server-renders the default popup input value without a form name before hydration" | replaced | A server render runs in Node with no DOM. The case is in the test file that the Node project runs, with the same title. | Test: packages/vue/src/autocomplete/root/AutocompleteRoot.node.test.tsx |
Additions to the Base UI API: 3.
| Entry | Kind | Name | Reason |
|---|---|---|---|
| Trigger | prop | id | The part reads id to name its element and, while the input is inside the popup, to register its control, so it is a declared prop. Base UI reads it from the native attributes of the trigger, and types.md has no row for a native attribute. |
| List | slot | item | The slot renders each filtered item with { item, index }. It replaces the function child of the list in Base UI. |
| GroupLabel | prop | id | The part registers id with its group for aria-labelledby, so it is a declared prop. Base UI reads it from the native attributes of the label, and types.md has no row for a native attribute. |
Return types that differ from Base UI: 2.
| Entry | Vase UI returns | Reason |
|---|---|---|
| useFilter | Filter | Base UI documents the return type as AutocompleteFilter. The composable returns Filter, and AutocompleteFilter is that type under another name. |
| useFilteredItems | ComputedRef<T[]> | setup runs one time, so a plain array does not follow a change of the filtered items. The composable thus returns a ComputedRef of the array. |
avatar
Matches the Base UI commit 5aacdbb of 2026-11-10.
Queued changes: 0.
Vase UI rows with no upstream row: 3.
| Files | Total | Ported | Replaced | Not applicable | Waits on |
|---|---|---|---|---|---|
| Sources | 9 | 9 | 0 | 0 | 0 |
| Type tests | 1 | 1 | 0 | 0 | 0 |
| Tests | Total | Matched | Replaced | Not applicable | Waits on | Engine Chromium | Engine Firefox | Engine WebKit |
|---|---|---|---|---|---|---|---|---|
| Cases | 44 | 44 | 0 | 0 | 0 | 0 | 0 | 0 |
| Suite calls | 3 | 3 | 0 | 0 | 0 | 0 | 0 | 0 |
| API | Total | Matched | Excepted |
|---|---|---|---|
| Entries | 4 | 4 | 0 |
| Prop rows | 12 | 12 | 0 |
Exception rows: 0.
Additions to the Base UI API: 5.
| Entry | Kind | Name | Reason |
|---|---|---|---|
| Image | prop | src | The part reads src to load the image before it renders the element, so it is a declared prop. Base UI reads it from the native attributes of the image, and types.md has no row for a native attribute. |
| Image | prop | srcset | The part reads srcset to load the image before it renders the element, so it is a declared prop. Base UI reads it from the native attributes of the image, and types.md has no row for a native attribute. |
| Image | prop | sizes | The part reads sizes to load the image before it renders the element, so it is a declared prop. Base UI reads it from the native attributes of the image, and types.md has no row for a native attribute. |
| Image | prop | crossorigin | The part reads crossorigin to load the image before it renders the element, so it is a declared prop. Base UI reads it from the native attributes of the image, and types.md has no row for a native attribute. |
| Image | prop | referrerpolicy | The part reads referrerpolicy to load the image before it renders the element, so it is a declared prop. Base UI reads it from the native attributes of the image, and types.md has no row for a native attribute. |
Return types that differ from Base UI: 0.
button
Matches the Base UI commit 5aacdbb of 2026-11-10.
Queued changes: 0.
Vase UI rows with no upstream row: 4.
| Files | Total | Ported | Replaced | Not applicable | Waits on |
|---|---|---|---|---|---|
| Sources | 3 | 3 | 0 | 0 | 0 |
| Type tests | 1 | 1 | 0 | 0 | 0 |
| Tests | Total | Matched | Replaced | Not applicable | Waits on | Engine Chromium | Engine Firefox | Engine WebKit |
|---|---|---|---|---|---|---|---|---|
| Cases | 9 | 9 | 0 | 0 | 0 | 0 | 0 | 0 |
| Suite calls | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 |
| API | Total | Matched | Excepted |
|---|---|---|---|
| Entries | 2 | 2 | 0 |
| Prop rows | 5 | 5 | 0 |
Exception rows: 0.
Additions to the Base UI API: 1.
| Entry | Kind | Name | Reason |
|---|---|---|---|
| Button | prop | disabled | The part reads disabled for its state and for useButton, so it is a declared prop. Base UI reads it from the native attributes of the button, and types.md has no row for a native attribute. |
Return types that differ from Base UI: 0.
checkbox
Matches the Base UI commit 5aacdbb of 2026-11-10.
Queued changes: 0.
Vase UI rows with no upstream row: 47.
| Files | Total | Ported | Replaced | Not applicable | Waits on |
|---|---|---|---|---|---|
| Sources | 8 | 8 | 0 | 0 | 0 |
| Type tests | 0 | 0 | 0 | 0 | 0 |
| Tests | Total | Matched | Replaced | Not applicable | Waits on | Engine Chromium | Engine Firefox | Engine WebKit |
|---|---|---|---|---|---|---|---|---|
| Cases | 106 | 101 | 0 | 5 | 0 | 0 | 0 | 0 |
| Suite calls | 3 | 3 | 0 | 0 | 0 | 0 | 0 | 0 |
| API | Total | Matched | Excepted |
|---|---|---|---|
| Entries | 4 | 4 | 0 |
| Prop rows | 22 | 22 | 0 |
Exception rows: 2.
| Thing | Kind | Reason | Detail |
|---|---|---|---|
| packages/react/src/checkbox/root/CheckboxRoot.react17.test.tsx | not-applicable | The file tests the id paths for React 17. It mocks SafeReact, so that React has no useId. Vue useId gives the ids. | |
| packages/react/src/checkbox/root/CheckboxRoot.test.tsx, case "<Checkbox.Root /> > prefers a render element's `aria-label` over an associated label" | not-applicable | The case gives the aria-label as a prop of a React element in render. Base UI reads that prop with React.isValidElement. In Vue, an element with props of its own is the #render slot, and a part does not see an attribute that the slot adds. Base UI also does not see an attribute that a render function adds. |
Additions to the Base UI API: 2.
| Entry | Kind | Name | Reason |
|---|---|---|---|
| Root | prop | ariaLabel | The part reads aria-label to find out if a label names the checkbox, and $attrs is not reactive, so it is a declared prop. Base UI reads it from the native attributes of the element, and types.md has no row for a native attribute. |
| Root | prop | ariaLabelledby | The part reads aria-labelledby to find out if a label names the checkbox, and $attrs is not reactive, so it is a declared prop. Base UI reads it from the native attributes of the element, and types.md has no row for a native attribute. |
Return types that differ from Base UI: 0.
checkbox-group
Matches the Base UI commit 5aacdbb of 2026-11-10.
Queued changes: 0.
Vase UI rows with no upstream row: 36.
| Files | Total | Ported | Replaced | Not applicable | Waits on |
|---|---|---|---|---|---|
| Sources | 5 | 5 | 0 | 0 | 0 |
| Type tests | 0 | 0 | 0 | 0 | 0 |
| Tests | Total | Matched | Replaced | Not applicable | Waits on | Engine Chromium | Engine Firefox | Engine WebKit |
|---|---|---|---|---|---|---|---|---|
| Cases | 89 | 85 | 4 | 0 | 0 | 0 | 0 | 0 |
| Suite calls | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 |
| API | Total | Matched | Excepted |
|---|---|---|---|
| Entries | 2 | 2 | 0 |
| Prop rows | 8 | 8 | 0 |
Exception rows: 4.
| Thing | Kind | Reason | Detail |
|---|---|---|---|
| packages/react/src/checkbox-group/CheckboxGroup.test.tsx, case "<CheckboxGroup /> > Field.Label > keeps checkbox ids unique in a shared Field.Root during SSR (nativeButton=%s)" | replaced | A server render runs in Node with no DOM. The case is in the test file that the Node project runs, with the same title. | Test: packages/vue/src/checkbox-group/CheckboxGroup.node.test.tsx |
| packages/react/src/checkbox-group/CheckboxGroup.test.tsx, case "<CheckboxGroup /> > Field.Label > associates Field.Label with a grouped Checkbox during SSR (nativeButton=$nativeButton, parent=$parent)" | replaced | A server render runs in Node with no DOM. The case is in the test file that the Node project runs, with the same title. | Test: packages/vue/src/checkbox-group/CheckboxGroup.node.test.tsx |
| packages/react/src/checkbox-group/CheckboxGroup.test.tsx, case "<CheckboxGroup /> > Form > validates an inputless controlled group on change with validationMode=onChange" | replaced | The case reads the second argument of validate, and it expects the field group there. Base UI keeps the fields of a page that has no form in a variable of the module. Vase UI keeps no such variable, so a field with no form sees no other field, and the argument is an empty object. The case "validates an inputless controlled group on change" in the block "a field with no form" has the same steps and asserts that argument. | Test: packages/vue/src/checkbox-group/CheckboxGroup.test.tsx |
| packages/react/src/checkbox-group/CheckboxGroup.test.tsx, case "<CheckboxGroup /> > Form > validates an initially empty group through the imperative action" | replaced | The case reads the second argument of validate, and it expects the field group there. Base UI keeps the fields of a page that has no form in a variable of the module. Vase UI keeps no such variable, so a field with no form sees no other field, and the argument is an empty object. The case "validates an initially empty group through the exposed validate" in the block "a field with no form" has the same steps and asserts that argument. | Test: packages/vue/src/checkbox-group/CheckboxGroup.test.tsx |
Additions to the Base UI API: 1.
| Entry | Kind | Name | Reason |
|---|---|---|---|
| CheckboxGroup | prop | id | The part reads id to register its control with the field, and $attrs is not reactive, so it is a declared prop. Base UI reads it from the native attributes of the element, and types.md has no row for a native attribute. |
Return types that differ from Base UI: 0.
collapsible
Matches the Base UI commit 5aacdbb of 2026-11-10.
Queued changes: 0.
Vase UI rows with no upstream row: 13.
| Files | Total | Ported | Replaced | Not applicable | Waits on |
|---|---|---|---|---|---|
| Sources | 13 | 13 | 0 | 0 | 0 |
| Type tests | 1 | 1 | 0 | 0 | 0 |
| Tests | Total | Matched | Replaced | Not applicable | Waits on | Engine Chromium | Engine Firefox | Engine WebKit |
|---|---|---|---|---|---|---|---|---|
| Cases | 55 | 53 | 2 | 0 | 0 | 0 | 0 | 0 |
| Suite calls | 3 | 3 | 0 | 0 | 0 | 0 | 0 | 0 |
| API | Total | Matched | Excepted |
|---|---|---|---|
| Entries | 7 | 7 | 0 |
| Prop rows | 16 | 15 | 1 |
Exception rows: 2.
| Thing | Kind | Reason | Detail |
|---|---|---|---|
| packages/react/src/collapsible/panel/CollapsiblePanel.test.tsx, block "<Collapsible.Panel /> > server-side rendering" | replaced | A server render runs in Node with no DOM. The two cases are in the test file that the Node project runs, with the same titles. | Test: packages/vue/src/collapsible/panel/CollapsiblePanel.node.test.tsx |
| docs/src/app/(docs)/react/components/collapsible/types.md, entry "Panel", prop "keepMounted" | not-applicable | The part must tell an absent keepMounted from keepMounted={false}, because only the explicit false gets a warning when hiddenUntilFound is set. A Boolean prop that is absent is false in Vue, so the declared default is undefined. The part uses false for an absent prop, which is the default that Base UI documents. |
Additions to the Base UI API: 2.
| Entry | Kind | Name | Reason |
|---|---|---|---|
| Trigger | prop | disabled | The part reads disabled for its state and for useButton, so it is a declared prop. Base UI reads it from the native attributes of the trigger, and types.md has no row for a native attribute. |
| Panel | prop | id | The part reads id to give it to the root for aria-controls, so it is a declared prop. Base UI reads it from the native attributes of the panel, and types.md has no row for a native attribute. |
Return types that differ from Base UI: 0.
combobox
Matches the Base UI commit 5aacdbb of 2026-11-10.
Queued changes: 0.
Vase UI rows with no upstream row: 158.
| Files | Total | Ported | Replaced | Not applicable | Waits on |
|---|---|---|---|---|---|
| Sources | 60 | 60 | 0 | 0 | 0 |
| Type tests | 1 | 1 | 0 | 0 | 0 |
| Tests | Total | Matched | Replaced | Not applicable | Waits on | Engine Chromium | Engine Firefox | Engine WebKit |
|---|---|---|---|---|---|---|---|---|
| Cases | 739 | 730 | 5 | 4 | 0 | 0 | 0 | 0 |
| Suite calls | 26 | 26 | 0 | 0 | 0 | 0 | 0 | 0 |
| API | Total | Matched | Excepted |
|---|---|---|---|
| Entries | 42 | 42 | 0 |
| Prop rows | 144 | 143 | 1 |
Exception rows: 10.
| Thing | Kind | Reason | Detail |
|---|---|---|---|
| docs/src/app/(docs)/react/components/combobox/types.md, entry "Root", prop "multiple" | not-applicable | The row gives the default false. The prop has no declared default, because withDefaults refuses a default for a prop of the generic type Multiple & boolean (TS2322). Vue gives an absent Boolean prop the value false, and the JSDoc has @default false for the API reference. | |
| packages/react/src/combobox/chips/ComboboxChips.test.tsx, case "<Combobox.Chips /> > does not focus the input when pressing content in a nested React portal" | not-applicable | React sends the press of a portal up the React tree, so the chips get it. A Vue press goes up the DOM only, and a Teleport puts its content outside the chips. The chips thus get no such press. | |
| packages/react/src/combobox/input-group/ComboboxInputGroup.test.tsx, case "<Combobox.InputGroup /> > does not focus the input when pressing content in a nested React portal" | not-applicable | React sends the press of a portal up the React tree, so the input group gets it. A Vue press goes up the DOM only, and a Teleport puts its content outside the input group. The group thus gets no such press. | |
| packages/react/src/combobox/items/createItems.test.tsx, case "Combobox.createItems > integration > resolves a server-rendered defaultValue label from the external window" | replaced | A server render runs in Node with no DOM. The case is in the test file that the Node project runs, with the same title. | Test: packages/vue/src/combobox/items/createItems.node.test.tsx |
| packages/react/src/combobox/label/ComboboxLabel.test.tsx, case "<Combobox.Label /> > does not run the development warning in production" | replaced | The case needs a production build. Vitest sets process.env.NODE_ENV when it transforms a file for a browser, so only the Node project can change it. | Test: packages/vue/src/combobox/label/ComboboxLabel.node.test.tsx |
| packages/react/src/combobox/root/ComboboxRoot.test.tsx, case "<Combobox.Root /> > server-side rendering > sets combobox aria attributes on the input" | replaced | A server render runs in Node with no DOM. The case is in the test file that the Node project runs, with the same title. | Test: packages/vue/src/combobox/root/ComboboxRoot.node.test.tsx |
| packages/react/src/combobox/root/ComboboxRoot.test.tsx, case "<Combobox.Root /> > server-side rendering > sets combobox aria attributes on the trigger when input is inside popup" | replaced | A server render runs in Node with no DOM. The case is in the test file that the Node project runs, with the same title. | Test: packages/vue/src/combobox/root/ComboboxRoot.node.test.tsx |
| packages/react/src/combobox/root/ComboboxRoot.test.tsx, case "<Combobox.Root /> > server-side rendering > does not link Combobox.Label to trigger before hydration" | replaced | The case renders on a server only. Vase UI links the label in the server HTML (VUE-51). The case "VUE-51: names the trigger with a label that comes before it" asserts that link. The case "names the trigger that comes before it" of ComboboxLabel.node.test.tsx asserts it for a label after the trigger. | Test: packages/vue/src/combobox/root/ComboboxRoot.node.test.tsx |
| packages/react/src/combobox/root/ComboboxRoot.test.tsx, case "<Combobox.Root /> > does not dismiss when pressing portalled content inside the popup but outside the list" | not-applicable | React sends the press of a portal up the React tree, so the popup knows that the press is inside it. A Vue press goes up the DOM only, and a Teleport puts its content outside the popup. The press is thus a press outside the popup, and it closes the popup. | |
| packages/react/src/combobox/root/ComboboxRoot.test.tsx, case "<Combobox.Root /> > does not navigate the list with arrow keys from portalled controls inside the popup" | not-applicable | React sends the key of a portal up the React tree, so the list could get it. A Vue key goes up the DOM only, and a Teleport puts its content outside the list. The list thus gets no such key. |
Additions to the Base UI API: 4.
| Entry | Kind | Name | Reason |
|---|---|---|---|
| Trigger | prop | id | The part reads id to name its element. While the input is inside the popup, the part also registers id as the id of the control of the field, so it is a declared prop. Base UI reads id from the native attributes of the trigger, and types.md has no row for a native attribute. |
| List | slot | item | The slot renders each filtered item with { item, index }. It replaces the function child of the list in Base UI. |
| GroupLabel | prop | id | The part gives id to the group for aria-labelledby and follows a change of it, so it is a declared prop. Base UI reads it from the native attributes of the group label, and types.md has no row for a native attribute. |
| ChipRemove | prop | disabled | The part reads disabled and disables its button when the prop or the root says so, so it is a declared prop. Base UI reads it from the native attributes of the button, and types.md has no row for a native attribute. |
Return types that differ from Base UI: 3.
| Entry | Vase UI returns | Reason |
|---|---|---|
| createItems | ComboboxItemCollection<Item, Value> | Base UI documents the second type argument of the result as ComboboxPrimitiveValue. The function is generic in Value, as the Base UI source is, and the result has the type argument Value of the call. |
| useFilter | Filter | Base UI documents the return type as ComboboxFilter. The composable returns Filter, and ComboboxFilter is that type under another name. |
| useFilteredItems | ComputedRef<T[]> | setup runs one time, so a plain array does not follow a change of the filtered items. The composable thus returns a ComputedRef of the array. |
common
Matches the Base UI commit 5aacdbb of 2026-11-10.
Queued changes: 0.
Vase UI rows with no upstream row: 4.
| Files | Total | Ported | Replaced | Not applicable | Waits on |
|---|---|---|---|---|---|
| Sources | 61 | 33 | 0 | 28 | 0 |
| Type tests | 6 | 2 | 0 | 4 | 0 |
| Tests | Total | Matched | Replaced | Not applicable | Waits on | Engine Chromium | Engine Firefox | Engine WebKit |
|---|---|---|---|---|---|---|---|---|
| Cases | 175 | 60 | 2 | 113 | 0 | 0 | 0 | 0 |
| Suite calls | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
Exception rows: 48.
| Thing | Kind | Reason | Detail |
|---|---|---|---|
| packages/utils/src/fastHooks.ts | not-applicable | The file joins the store subscriptions of one React component. A Vue component tracks each reactive read, so it needs no such wrapper. | |
| packages/utils/src/store/ReactStore.ts | not-applicable | The methods of the class call React hooks. ReactiveStore of packages/vue/src/utils/popups/ReactiveStore.ts holds the state, the context and the selectors on Vue reactivity. | |
| packages/utils/src/store/Store.ts | not-applicable | The class keeps a list of subscribers and gives each one the new state object. A write to shallowReactive state tells the readers, so ReactiveStore of packages/vue/src/utils/popups/ReactiveStore.ts has no such list. | |
| packages/utils/src/store/index.ts | not-applicable | The file exports the store classes and hooks for React. The store of Vase UI is in packages/vue/src/utils/popups/ReactiveStore.ts. | |
| packages/utils/src/store/useStore.ts | not-applicable | The hook subscribes a React component to a store through useSyncExternalStore. A Vue computed that calls select of the store does this work. | |
| packages/utils/src/lruMemoize.ts | not-applicable | No Base UI component imports the selector factories, and Vue computed does this work. lruMemoize is a re-export of reselect that only createSelectorMemoized uses. | |
| packages/utils/src/store/createSelector.ts | not-applicable | No Base UI component imports the selector factories, and Vue computed does this work. lruMemoize is a re-export of reselect that only createSelectorMemoized uses. | |
| packages/utils/src/store/createSelectorMemoized.ts | not-applicable | No Base UI component imports the selector factories, and Vue computed does this work. lruMemoize is a re-export of reselect that only createSelectorMemoized uses. | |
| packages/utils/src/store/StoreInspector.tsx | not-applicable | A React development panel. No component imports it. | |
| packages/utils/src/getReactElementRef.ts | not-applicable | The file exists only to support old React versions. | |
| packages/utils/src/inertValue.ts | not-applicable | The file exists only to support old React versions. | |
| packages/utils/src/reactVersion.ts | not-applicable | The file exists only to support old React versions. | |
| packages/utils/src/safeReact.ts | not-applicable | The file exists only to support old React versions. | |
| packages/utils/src/useStableCallback.ts | not-applicable | Vue runs setup one time, so a function made there keeps its identity. It reads the current state when it runs. | |
| packages/utils/src/useIsoLayoutEffect.ts | not-applicable | Vue has onMounted, onBeforeUnmount and watch with flush: 'post'. The two hooks do not run on a server. A watch callback with flush: 'post' runs on a server only when it has immediate. | |
| packages/utils/src/useOnFirstRender.ts | not-applicable | Vue runs setup one time. A value made there is made one time, and code there runs one time. | |
| packages/utils/src/useRefWithInit.ts | not-applicable | Vue runs setup one time. A value made there is made one time, and code there runs one time. | |
| packages/utils/src/useValueAsRef.ts | not-applicable | A getter or a ref gives the current value. A read outside an effect adds no dependency. | |
| packages/utils/src/useOnMount.ts | not-applicable | onMounted and onScopeDispose do this work. | |
| packages/utils/src/useForcedRerendering.ts | not-applicable | A change of reactive state renders the component again, and triggerRef forces it. | |
| packages/utils/src/useMergedRefs.ts | not-applicable | A part has one ref setter function, which gives the element to each internal user. Vue has no ref objects to merge. | |
| packages/utils/src/usePreviousValue.ts | not-applicable | A Vue watch callback gets the old value of its source, with the same Object.is comparison. | |
| packages/utils/src/useId.ts | not-applicable | The file wraps React useId and holds a fallback for React 17. Vue useId gives the ids. | |
| packages/utils/src/fastObjectShallowCompare.ts | not-applicable | No Base UI component imports the file at the matched commit. | |
| packages/utils/src/useIdleCallback.ts | not-applicable | No Base UI component imports the file at the matched commit. | |
| packages/utils/src/isMouseWithinBounds.ts | not-applicable | Base UI marks it as deprecated, and no component imports it. The components use isMouseWithinBounds of packages/react/src/utils/getPseudoElementBounds.ts. | |
| packages/utils/src/isHTMLElement.ts | not-applicable | A re-export of @floating-ui/utils/dom. Base UI components import isHTMLElement from that package, and Vase UI does the same. | |
| packages/utils/src/formatErrorMessage.ts | not-applicable | Only code that the Base UI build generates calls it: a Babel plugin replaces each error message with a code. The Vase UI build keeps the full messages. | |
| packages/utils/src/store/Store.test.ts | not-applicable | The cases test the subscriber list of the Store class and Store.create. packages/vue/src/utils/popups/ReactiveStore.test.tsx tests the writes of the Vue store. | |
| packages/utils/src/store/Store.spec.ts | not-applicable | It tests the type of Store.create, which the Vue store does not have. | |
| packages/utils/src/store/ReactStore.test.tsx | not-applicable | The cases test the React hooks of ReactStore and its observe method. A Vue computed and a watch do this work. | |
| packages/utils/src/store/ReactStore.spec.ts | not-applicable | It tests the types of the React hooks of ReactStore. packages/vue/src/utils/popups/ReactiveStore.spec.ts tests the types of select, set and update. | |
| packages/utils/src/store/createSelector.test.ts | not-applicable | No Base UI component imports the selector factories. Vue computed does this work. | |
| packages/utils/src/store/createSelector.spec.ts | not-applicable | No Base UI component imports the selector factories. Vue computed does this work. | |
| packages/utils/src/store/createSelectorMemoized.test.ts | not-applicable | No Base UI component imports the selector factories. Vue computed does this work. | |
| packages/utils/src/store/createSelectorMemoized.spec.ts | not-applicable | No Base UI component imports the selector factories. Vue computed does this work. | |
| packages/utils/src/store/StoreInspector.test.tsx | not-applicable | It tests a React development panel that no component imports. | |
| packages/utils/src/getReactElementRef.test.tsx | not-applicable | It tests a helper that reads the ref of a React element in React 17, 18 and 19. | |
| packages/utils/src/useId.test.tsx | not-applicable | It tests a wrapper of React useId. Vue useId gives the ids. | |
| packages/utils/src/useId.react17.test.tsx | not-applicable | It tests the id fallback for React 17. | |
| packages/utils/src/useIdleCallback.test.tsx | not-applicable | No Base UI component imports useIdleCallback at the matched commit. | |
| packages/utils/src/useIdleCallback.fallback.test.ts | not-applicable | No Base UI component imports useIdleCallback at the matched commit. | |
| packages/utils/src/useMergedRefs.test.tsx | not-applicable | It tests the merge of React refs. A Vue part has one ref setter function. | |
| packages/utils/src/usePreviousValue.test.tsx | not-applicable | It tests a React state hook. A Vue watch callback gets the old value of its source, with the same Object.is comparison. | |
| packages/utils/src/formatErrorMessage.test.ts | not-applicable | Only code that the Base UI build generates calls formatErrorMessage. The Vase UI build keeps the full messages. | |
| packages/utils/src/hydration.test.tsx, block "hydration > React 17 paths" | not-applicable | The cases test the code path for React 17. | |
| packages/utils/src/hydration.test.tsx, case "hydration > useIsHydrated > does not report hydrated while rendering on the server" | replaced | A server render runs in Node with no DOM. The case is in the test file that the Node project runs, with the same title. | Test: packages/vue/src/common/hydration.node.test.tsx |
| packages/utils/src/hydration.test.tsx, case "hydration > useIsHydrating > reports hydrating while rendering on the server" | replaced | A server render runs in Node with no DOM. The case is in the test file that the Node project runs, with the same title. | Test: packages/vue/src/common/hydration.node.test.tsx |
Additions to the Base UI API: 0.
Return types that differ from Base UI: 0.
context-menu
Matches the Base UI commit 5aacdbb of 2026-11-10.
Queued changes: 0.
Vase UI rows with no upstream row: 34.
| Files | Total | Ported | Replaced | Not applicable | Waits on |
|---|---|---|---|---|---|
| Sources | 9 | 9 | 0 | 0 | 0 |
| Type tests | 0 | 0 | 0 | 0 | 0 |
| Tests | Total | Matched | Replaced | Not applicable | Waits on | Engine Chromium | Engine Firefox | Engine WebKit |
|---|---|---|---|---|---|---|---|---|
| Cases | 32 | 28 | 0 | 0 | 4 | 0 | 0 | 0 |
| Suite calls | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 |
| API | Total | Matched | Excepted |
|---|---|---|---|
| Entries | 33 | 33 | 0 |
| Prop rows | 126 | 125 | 1 |
Exception rows: 5.
| Thing | Kind | Reason | Detail |
|---|---|---|---|
| packages/react/src/context-menu/root/ContextMenuRoot.non-mac.test.tsx | waits-on | The other rows of this family that wait on parts of Menu name this file as their target. The file thus stays absent until the last of those parts exists. Its case needs only ContextMenu.Item. | Waits on: packages/react/src/context-menu/root/ContextMenuRoot.non-mac.test.tsx |
| packages/react/src/context-menu/root/ContextMenuRoot.test.tsx, case "<ContextMenu.Root /> > returns focus to the focused surface when closing with Shift+Tab" | waits-on | The case closes the menu with the Tab key. The list navigation of the menu root closes a menu for Tab and for Shift+Tab, and it gives the focus back to the trigger. Menu.Root has no list navigation yet, so the key does not close the menu. | Waits on: packages/react/src/context-menu/root/ContextMenuRoot.non-mac.test.tsx |
| packages/react/src/context-menu/root/ContextMenuRoot.test.tsx, case "<ContextMenu.Root /> > returns focus to the focused surface when closing with Tab" | waits-on | The case closes the menu with the Tab key. The list navigation of the menu root closes a menu for Tab and for Shift+Tab, and it gives the focus back to the trigger. Menu.Root has no list navigation yet, so the key does not close the menu. | Waits on: packages/react/src/context-menu/root/ContextMenuRoot.non-mac.test.tsx |
| packages/react/src/context-menu/trigger/ContextMenuTrigger.test.tsx, case "<ContextMenu.Trigger /> > keeps the root menu open when the context-menu gesture ends in a portaled submenu" | waits-on | The trigger lacks the check of the root owner id at the mouseup, which needs findRootOwnerId of Menu. Vase UI does not have that file yet, so a mouseup in a submenu closes the menu. | Waits on: packages/react/src/context-menu/root/ContextMenuRoot.non-mac.test.tsx |
| docs/src/app/(docs)/react/components/context-menu/types.md, entry "Positioner", prop "side" | not-applicable | The row gives the default 'bottom'. The part has no Vue default: a prop that is absent lets Menu.Positioner take 'bottom' for a root context menu and 'inline-end' for a submenu, as in Base UI. Only an absent side gives a root context menu the offsets -5 and 2. With a Vue default, a submenu opens below its trigger and a root context menu loses those offsets. |
Additions to the Base UI API: 7.
| Entry | Kind | Name | Reason |
|---|---|---|---|
| Popup | prop | id | The part replaces an absent id with a generated id and gives the id to the root, which the trigger names in aria-controls. Base UI declares id as a prop with the tag @ignore, so types.md has no row for it. |
| Item | prop | id | The part replaces an absent id with a generated id and renders it on the item. Base UI declares id as a prop with the tag @ignore, so types.md has no row for it. |
| LinkItem | prop | id | The part replaces an absent id with a generated id and renders it on the link. Base UI declares id as a prop with the tag @ignore, so types.md has no row for it. |
| SubmenuTrigger | prop | id | The part replaces an absent id with a generated id and registers the trigger in the store of the submenu with it. Base UI declares id as a prop with the tag @ignore, so types.md has no row for it. |
| GroupLabel | prop | id | The part replaces an absent id with a generated id and gives it to the group for aria-labelledby. It is thus a declared prop. Base UI reads it from the native attributes of the label, and types.md has no row for a native attribute. |
| RadioItem | prop | id | The part replaces an absent id with a generated id and renders it on the item. Base UI declares id as a prop with the tag @ignore, so types.md has no row for it. |
| CheckboxItem | prop | id | The part replaces an absent id with a generated id and renders it on the item. Base UI declares id as a prop with the tag @ignore, so types.md has no row for it. |
Return types that differ from Base UI: 0.
csp-provider
Matches the Base UI commit 5aacdbb of 2026-11-10.
Queued changes: 0.
Vase UI rows with no upstream row: 4.
| Files | Total | Ported | Replaced | Not applicable | Waits on |
|---|---|---|---|---|---|
| Sources | 5 | 4 | 0 | 1 | 0 |
| Type tests | 0 | 0 | 0 | 0 | 0 |
| Tests | Total | Matched | Replaced | Not applicable | Waits on | Engine Chromium | Engine Firefox | Engine WebKit |
|---|---|---|---|---|---|---|---|---|
| Cases | 4 | 4 | 0 | 0 | 0 | 0 | 0 | 0 |
| Suite calls | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| API | Total | Matched | Excepted |
|---|---|---|---|
| Entries | 1 | 1 | 0 |
| Prop rows | 3 | 3 | 0 |
Exception rows: 1.
| Thing | Kind | Reason | Detail |
|---|---|---|---|
| packages/react/src/internals/csp-context/index.ts | not-applicable | No Base UI component imports the file. It is the module of the package entry ./internals/csp-context, and Vase UI has no entry point for an internal module. |
Additions to the Base UI API: 0.
Return types that differ from Base UI: 0.
delay-group
Matches the Base UI commit 5aacdbb of 2026-11-10.
Queued changes: 0.
Vase UI rows with no upstream row: 17.
| Files | Total | Ported | Replaced | Not applicable | Waits on |
|---|---|---|---|---|---|
| Sources | 1 | 1 | 0 | 0 | 0 |
| Type tests | 0 | 0 | 0 | 0 | 0 |
| Tests | Total | Matched | Replaced | Not applicable | Waits on | Engine Chromium | Engine Firefox | Engine WebKit |
|---|---|---|---|---|---|---|---|---|
| Cases | 6 | 6 | 0 | 0 | 0 | 0 | 0 | 0 |
| Suite calls | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
Exception rows: 0.
Additions to the Base UI API: 0.
Return types that differ from Base UI: 0.
dialog
Matches the Base UI commit 5aacdbb of 2026-11-10.
Queued changes: 0.
Vase UI rows with no upstream row: 73.
| Files | Total | Ported | Replaced | Not applicable | Waits on |
|---|---|---|---|---|---|
| Sources | 24 | 24 | 0 | 0 | 0 |
| Type tests | 1 | 1 | 0 | 0 | 0 |
| Tests | Total | Matched | Replaced | Not applicable | Waits on | Engine Chromium | Engine Firefox | Engine WebKit |
|---|---|---|---|---|---|---|---|---|
| Cases | 124 | 118 | 4 | 2 | 0 | 0 | 0 | 0 |
| Suite calls | 13 | 13 | 0 | 0 | 0 | 0 | 0 | 0 |
| API | Total | Matched | Excepted |
|---|---|---|---|
| Entries | 17 | 17 | 0 |
| Prop rows | 45 | 45 | 0 |
Exception rows: 6.
| Thing | Kind | Reason | Detail |
|---|---|---|---|
| packages/react/src/dialog/popup/DialogPopup.test.tsx, case "<Dialog.Popup /> > prop: finalFocus > respects finalFocus when initialFocus points outside the popup" | replaced | With modal set to true, the elements outside the popup are inert (BUG-4678), so initialFocus cannot focus an element outside the popup. The case BUG-4678: keeps the initial focus off an inert element, and returns the focus to $name tests this, and the return of the focus. | Test: packages/vue/src/dialog/popup/DialogPopup.test.tsx |
| packages/react/src/dialog/popup/DialogPopup.test.tsx, case "<Dialog.Popup /> > prop: finalFocus > moves final focus to trigger if initialFocus points outside the popup and finalFocus is not specified" | replaced | With modal set to true, the elements outside the popup are inert (BUG-4678), so initialFocus cannot focus an element outside the popup. The case BUG-4678: keeps the initial focus off an inert element, and returns the focus to $name tests this, and the return of the focus. | Test: packages/vue/src/dialog/popup/DialogPopup.test.tsx |
| packages/react/src/dialog/portal/DialogPortal.test.tsx, case "<Dialog.Portal /> > Suspense integration > should not throw "Maximum update depth exceeded" when Suspense boundary is outside Portal" | not-applicable | The case guards a render loop of React (issue 3695) while a Suspense boundary outside the portal waits for a lazy component in the popup. The portal of Vase UI renders its content after the mount, so a Vue <Suspense> around it does not wait for that content and shows no fallback. | |
| packages/react/src/dialog/root/DialogRoot.detached-triggers.test.tsx, case "<Dialog.Root /> > handle-backed root ownership > does not warn for a detached payload open in production" | replaced | The case needs a production build. Vitest sets process.env.NODE_ENV when it transforms a file for a browser, so only the Node project can change it. The case ignores openWithPayload() with no root and gives no warning in a production build tests the same. | Test: packages/vue/src/dialog/root/DialogRoot.node.test.tsx |
| packages/react/src/dialog/root/DialogRoot.detached-triggers.test.tsx, case "<Dialog.Root /> > handle-backed root ownership > multiple roots sharing one handle > keeps the root attached through Strict Mode effect replay" | not-applicable | The case tests React Strict Mode. Vue has no Strict Mode. | |
| packages/react/src/dialog/root/DialogRoot.test.tsx, case "<Dialog.Root /> > closes a popover opened during a nested dialog, not the parent, on Escape after the nested dialog closes" | replaced | The case expects the focus on <body> after the nested dialog closes. Vase UI moves it into the parent popup (BUG-6037), so Escape there closes the parent. The case BUG-6037: on Escape after a nested dialog closes, closes $name tests Escape with that focus, and with no focus in a popup, where the popover closes as in Base UI. | Test: packages/vue/src/dialog/root/DialogRoot.test.tsx |
Additions to the Base UI API: 4.
| Entry | Kind | Name | Reason |
|---|---|---|---|
| Trigger | prop | disabled | The part reads disabled for its state and for useButton, so it is a declared prop. Base UI reads it from the native attributes of the trigger, and types.md has no row for a native attribute. |
| Title | prop | id | The part reads id to register it with the root for aria-labelledby of the popup, so it is a declared prop. Base UI reads it from the native attributes of the title, and types.md has no row for a native attribute. |
| Description | prop | id | The part reads id to register it with the root for aria-describedby of the popup, so it is a declared prop. Base UI reads it from the native attributes of the description, and types.md has no row for a native attribute. |
| Close | prop | disabled | The part reads disabled for its state and for useButton, so it is a declared prop. Base UI reads it from the native attributes of the button, and types.md has no row for a native attribute. |
Return types that differ from Base UI: 1.
| Entry | Vase UI returns | Reason |
|---|---|---|
| createHandle | DialogHandle<Payload> | Dialog.Handle is the class DialogHandle. The declaration of the function names the class, and Base UI documents the same class by its name in the namespace. |
direction-provider
Matches the Base UI commit 5aacdbb of 2026-11-10.
Queued changes: 0.
Vase UI rows with no upstream row: 0.
| Files | Total | Ported | Replaced | Not applicable | Waits on |
|---|---|---|---|---|---|
| Sources | 5 | 4 | 0 | 1 | 0 |
| Type tests | 1 | 1 | 0 | 0 | 0 |
| Tests | Total | Matched | Replaced | Not applicable | Waits on | Engine Chromium | Engine Firefox | Engine WebKit |
|---|---|---|---|---|---|---|---|---|
| Cases | 2 | 2 | 0 | 0 | 0 | 0 | 0 | 0 |
| Suite calls | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| API | Total | Matched | Excepted |
|---|---|---|---|
| Entries | 2 | 2 | 0 |
| Prop rows | 2 | 2 | 0 |
Exception rows: 1.
| Thing | Kind | Reason | Detail |
|---|---|---|---|
| packages/react/src/internals/direction-context/index.ts | not-applicable | No Base UI component imports the file. It is the module of the package entry ./internals/direction-context, and Vase UI has no entry point for an internal module. |
Additions to the Base UI API: 0.
Return types that differ from Base UI: 1.
| Entry | Vase UI returns | Reason |
|---|---|---|
| useDirection | ComputedRef<TextDirection> | setup runs one time, so a plain value does not follow a change of the provider. The composable returns a ComputedRef of the direction. |
drawer
Matches the Base UI commit 5aacdbb of 2026-11-10.
Queued changes: 0.
Vase UI rows with no upstream row: 44.
| Files | Total | Ported | Replaced | Not applicable | Waits on |
|---|---|---|---|---|---|
| Sources | 31 | 31 | 0 | 0 | 0 |
| Type tests | 1 | 1 | 0 | 0 | 0 |
| Tests | Total | Matched | Replaced | Not applicable | Waits on | Engine Chromium | Engine Firefox | Engine WebKit |
|---|---|---|---|---|---|---|---|---|
| Cases | 279 | 275 | 1 | 3 | 0 | 0 | 0 | 0 |
| Suite calls | 4 | 4 | 0 | 0 | 0 | 0 | 0 | 0 |
| API | Total | Matched | Excepted |
|---|---|---|---|
| Entries | 26 | 26 | 0 |
| Prop rows | 67 | 67 | 0 |
Exception rows: 4.
| Thing | Kind | Reason | Detail |
|---|---|---|---|
| packages/react/src/drawer/popup/DrawerPopup.test.tsx, case "<Drawer.Popup /> > warns without relying on React owner-stack support" | not-applicable | The case mocks SafeReact.captureOwnerStack, which only React has. The warning of Drawer.Popup has no owner stack. | |
| packages/react/src/drawer/provider/DrawerProvider.test.tsx, case "<Drawer.Provider /> > releases closed drawers once they are removed" | replaced | The case forces a garbage collection through node:v8, so Base UI runs it only in jsdom. A browser has no node:v8. The case is in the test file that the Node project runs, with the same title. It reads the context of the provider from a server render. | Test: packages/vue/src/drawer/provider/DrawerProvider.node.test.tsx |
| packages/react/src/drawer/swipe-area/DrawerSwipeArea.test.tsx, case "<Drawer.SwipeArea /> > works before a generated swipe area id is available" | not-applicable | The case mocks useId to give undefined at first, as in React 17. useVaseUiId gives an id in setup, so the swipe area has its id in its first render. | |
| packages/react/src/drawer/swipe-area/DrawerSwipeArea.test.tsx, case "<Drawer.SwipeArea /> > registers the swipe area once a generated id becomes available" | not-applicable | The case mocks useId to give undefined at first, as in React 17. useVaseUiId gives an id in setup, so the swipe area has its id in its first render. |
Additions to the Base UI API: 5.
| Entry | Kind | Name | Reason |
|---|---|---|---|
| Trigger | prop | disabled | The part gives disabled to Dialog.Trigger, which reads it for its state and for useButton, so it is a declared prop. Base UI reads it from the native attributes of the trigger, and types.md has no row for a native attribute. |
| SwipeArea | prop | id | The part reads id to register the swipe area as a trigger in the store of the drawer, and follows a change of it, so it is a declared prop. Base UI reads it from the native attributes of the swipe area, and types.md has no row for a native attribute. |
| Title | prop | id | Drawer.Title is Dialog.Title. The part gives id to the root for aria-labelledby and follows a change of it, so it is a declared prop. Base UI reads it from the native attributes of the title, and types.md has no row for a native attribute. |
| Description | prop | id | Drawer.Description is Dialog.Description. The part gives id to the root for aria-describedby and follows a change of it, so it is a declared prop. Base UI reads it from the native attributes of the description, and types.md has no row for a native attribute. |
| Close | prop | disabled | Drawer.Close is Dialog.Close. The part reads disabled for its state and for useButton, so it is a declared prop. Base UI reads it from the native attributes of the button, and types.md has no row for a native attribute. |
Return types that differ from Base UI: 1.
| Entry | Vase UI returns | Reason |
|---|---|---|
| createHandle | DrawerHandle<Payload> | Drawer.Handle is the class DrawerHandle. The declaration of the function names the class, and Base UI documents the same class by its name in the namespace. |
field
Matches the Base UI commit 5aacdbb of 2026-11-10.
Queued changes: 0.
Vase UI rows with no upstream row: 49.
| Files | Total | Ported | Replaced | Not applicable | Waits on |
|---|---|---|---|---|---|
| Sources | 18 | 18 | 0 | 0 | 0 |
| Type tests | 1 | 1 | 0 | 0 | 0 |
| Tests | Total | Matched | Replaced | Not applicable | Waits on | Engine Chromium | Engine Firefox | Engine WebKit |
|---|---|---|---|---|---|---|---|---|
| Cases | 169 | 164 | 1 | 4 | 0 | 0 | 0 | 0 |
| Suite calls | 6 | 6 | 0 | 0 | 0 | 0 | 0 | 0 |
| API | Total | Matched | Excepted |
|---|---|---|---|
| Entries | 13 | 13 | 0 |
| Prop rows | 33 | 31 | 2 |
Exception rows: 4.
| Thing | Kind | Reason | Detail |
|---|---|---|---|
| packages/react/src/field/root/FieldRoot.react17.test.tsx | not-applicable | The file tests the paths for React 17. It mocks SafeReact, so that React has no useId and no captureOwnerStack. Vue useId gives the ids, and Vue has no owner stack. | |
| packages/react/src/field/root/FieldRoot.test.tsx, case "<Field.Root /> > does not set `aria-labelledby` during SSR when Field.Label is absent" | replaced | A server render with no hydrate() runs in Node with no DOM. The case with the same title renders a select in a field with no label, and asserts that the server HTML of the trigger has no aria-labelledby. | Test: packages/vue/src/field/root/FieldRoot.node.test.tsx |
| docs/src/app/(docs)/react/components/field/types.md, entry "Root", prop "actionsRef" | not-applicable | A React ref prop. The part exposes validate(), and the case "validates the field when the validate method is called" tests it. pnpm parity:check cannot read that member, because vue-component-meta leaves out an exposed member that has the name of a prop, and validate is also a prop. | |
| docs/src/app/(docs)/react/components/field/types.md, entry "Root", prop "validationMode" | not-applicable | The row gives the default 'onSubmit'. The part has no Vue default: a prop that is absent takes the validationMode of the form, and 'onSubmit' with no form, as in Base UI. With a Vue default, the part cannot find out that the prop is absent. |
Additions to the Base UI API: 8.
| Entry | Kind | Name | Reason |
|---|---|---|---|
| Control | prop | value | The part reads value to find out that it is controlled, so it is a declared prop. Base UI reads it from the native attributes of the input, and types.md has no row for a native attribute. |
| Control | emit | update:value | The part has the prop value, so v-model:value needs this emit. types.md has no row value, so the check expects no such emit. |
| Control | prop | id | The part gives id to the labelable provider and follows a change of it, so it is a declared prop. Base UI reads it from the native attributes of the input. |
| Control | prop | name | The part registers name with the field and follows a change of it, so it is a declared prop. Base UI reads it from the native attributes of the input. |
| Control | prop | disabled | The part reads disabled for the state and for the registration, so it is a declared prop. Base UI reads it from the native attributes of the input. |
| Error | prop | id | The part gives id to the labelable provider as the id of a message and follows a change of it, so it is a declared prop. Base UI reads it from the native attributes of the div. |
| Label | prop | id | The part gives id to the labelable provider as the id of a label and follows a change of it, so it is a declared prop. Base UI reads it from the native attributes of the label. |
| Description | prop | id | The part gives id to the labelable provider as the id of a message and follows a change of it, so it is a declared prop. Base UI reads it from the native attributes of the p. |
Return types that differ from Base UI: 0.
fieldset
Matches the Base UI commit 5aacdbb of 2026-11-10.
Queued changes: 0.
Vase UI rows with no upstream row: 11.
| Files | Total | Ported | Replaced | Not applicable | Waits on |
|---|---|---|---|---|---|
| Sources | 6 | 6 | 0 | 0 | 0 |
| Type tests | 0 | 0 | 0 | 0 | 0 |
| Tests | Total | Matched | Replaced | Not applicable | Waits on | Engine Chromium | Engine Firefox | Engine WebKit |
|---|---|---|---|---|---|---|---|---|
| Cases | 11 | 10 | 1 | 0 | 0 | 0 | 0 | 0 |
| Suite calls | 2 | 2 | 0 | 0 | 0 | 0 | 0 | 0 |
| API | Total | Matched | Excepted |
|---|---|---|---|
| Entries | 3 | 3 | 0 |
| Prop rows | 6 | 6 | 0 |
Exception rows: 1.
| Thing | Kind | Reason | Detail |
|---|---|---|---|
| packages/react/src/fieldset/legend/FieldsetLegend.test.tsx, case "<Fieldset.Legend /> > does not set `aria-labelledby` during SSR when legend is absent" | replaced | A server render with no hydrate() runs in Node with no DOM. The result differs from Base UI on purpose. A root that the consumer does not name assumes a legend until it is mounted, so the server HTML points at the generated id. An aria-labelledby with no valid IDREF gives no name (step 2B of https://www.w3.org/TR/accname-1.2/#computation-steps), so aria-label and the native name still apply. A root that has a non-blank aria-label or an aria-labelledby of the consumer has no generated id in the server HTML. The case "renders the generated id in the aria-labelledby of a root that has no legend" asserts the server HTML. The case "renders no aria-labelledby of its own on a root that the consumer names: $name" asserts that a named root has no generated id. The case "has no aria-labelledby when it has no legend" in FieldsetRoot.test.tsx asserts that the mounted root has none. | Test: packages/vue/src/fieldset/legend/FieldsetLegend.node.test.tsx |
Additions to the Base UI API: 2.
| Entry | Kind | Name | Reason |
|---|---|---|---|
| Root | prop | disabled | The part reads disabled for the state and the context, so it is a declared prop. Base UI reads it from the native attributes of the root, and types.md has no row for a native attribute. |
| Legend | prop | id | The part reads id to give it to the root for aria-labelledby, so it is a declared prop. Base UI reads it from the native attributes of the legend, and types.md has no row for a native attribute. |
Return types that differ from Base UI: 0.
filter-dropdown
Matches the Base UI commit 5aacdbb of 2026-11-10.
Queued changes: 0.
Vase UI rows with no upstream row: 4.
| Files | Total | Ported | Replaced | Not applicable | Waits on |
|---|---|---|---|---|---|
| Sources | 13 | 13 | 0 | 0 | 0 |
| Type tests | 0 | 0 | 0 | 0 | 0 |
| Tests | Total | Matched | Replaced | Not applicable | Waits on | Engine Chromium | Engine Firefox | Engine WebKit |
|---|---|---|---|---|---|---|---|---|
| Cases | 15 | 15 | 0 | 0 | 0 | 0 | 0 | 0 |
| Suite calls | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 |
Exception rows: 0.
Additions to the Base UI API: 0.
Return types that differ from Base UI: 0.
focus
Matches the Base UI commit 5aacdbb of 2026-11-10.
Queued changes: 0.
Vase UI rows with no upstream row: 91.
| Files | Total | Ported | Replaced | Not applicable | Waits on |
|---|---|---|---|---|---|
| Sources | 6 | 6 | 0 | 0 | 0 |
| Type tests | 0 | 0 | 0 | 0 | 0 |
| Tests | Total | Matched | Replaced | Not applicable | Waits on | Engine Chromium | Engine Firefox | Engine WebKit |
|---|---|---|---|---|---|---|---|---|
| Cases | 113 | 113 | 0 | 0 | 0 | 0 | 0 | 0 |
| Suite calls | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
Exception rows: 0.
Additions to the Base UI API: 0.
Return types that differ from Base UI: 0.
form
Matches the Base UI commit 5aacdbb of 2026-11-10.
Queued changes: 0.
Vase UI rows with no upstream row: 32.
| Files | Total | Ported | Replaced | Not applicable | Waits on |
|---|---|---|---|---|---|
| Sources | 4 | 4 | 0 | 0 | 0 |
| Type tests | 1 | 1 | 0 | 0 | 0 |
| Tests | Total | Matched | Replaced | Not applicable | Waits on | Engine Chromium | Engine Firefox | Engine WebKit |
|---|---|---|---|---|---|---|---|---|
| Cases | 36 | 36 | 0 | 0 | 0 | 0 | 0 | 0 |
| Suite calls | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 |
| API | Total | Matched | Excepted |
|---|---|---|---|
| Entries | 1 | 1 | 0 |
| Prop rows | 7 | 7 | 0 |
Exception rows: 0.
Additions to the Base UI API: 3.
| Entry | Kind | Name | Reason |
|---|---|---|---|
| Form | emit | submit | Base UI calls the native onSubmit of the consumer after its validation. In Vue, a native listener in the attributes runs before the part, so the part emits submit after its validation. types.md has no row for a native listener. |
| Form | prop | onFormSubmit | The part must know that a formSubmit listener exists before it prevents the native submit. Vue gives a listener to a declared prop with that name, and the emit formSubmit still calls it. The row onFormSubmit of types.md maps to the emit. |
| Form | prop | onFormSubmitOnce | A template gives this key to a listener that it binds with @form-submit.once. The part reads it for the same reason as onFormSubmit. The emit formSubmit still calls the listener one time. |
Return types that differ from Base UI: 0.
handbook-animation
Matches the Base UI commit 5aacdbb of 2026-11-10.
Queued changes: 0.
Vase UI rows with no upstream row: 0.
| Files | Total | Ported | Replaced | Not applicable | Waits on |
|---|---|---|---|---|---|
| Sources | 0 | 0 | 0 | 0 | 0 |
| Type tests | 0 | 0 | 0 | 0 | 0 |
| Tests | Total | Matched | Replaced | Not applicable | Waits on | Engine Chromium | Engine Firefox | Engine WebKit |
|---|---|---|---|---|---|---|---|---|
| Cases | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| Suite calls | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
Exception rows: 0.
Additions to the Base UI API: 0.
Return types that differ from Base UI: 0.
handbook-forms
Matches the Base UI commit 5aacdbb of 2026-11-10.
Queued changes: 0.
Vase UI rows with no upstream row: 0.
| Files | Total | Ported | Replaced | Not applicable | Waits on |
|---|---|---|---|---|---|
| Sources | 0 | 0 | 0 | 0 | 0 |
| Type tests | 0 | 0 | 0 | 0 | 0 |
| Tests | Total | Matched | Replaced | Not applicable | Waits on | Engine Chromium | Engine Firefox | Engine WebKit |
|---|---|---|---|---|---|---|---|---|
| Cases | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| Suite calls | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
Exception rows: 0.
Additions to the Base UI API: 0.
Return types that differ from Base UI: 0.
input
Matches the Base UI commit 5aacdbb of 2026-11-10.
Queued changes: 0.
Vase UI rows with no upstream row: 6.
| Files | Total | Ported | Replaced | Not applicable | Waits on |
|---|---|---|---|---|---|
| Sources | 3 | 3 | 0 | 0 | 0 |
| Type tests | 1 | 1 | 0 | 0 | 0 |
| Tests | Total | Matched | Replaced | Not applicable | Waits on | Engine Chromium | Engine Firefox | Engine WebKit |
|---|---|---|---|---|---|---|---|---|
| Cases | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| Suite calls | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 |
| API | Total | Matched | Excepted |
|---|---|---|---|
| Entries | 2 | 2 | 0 |
| Prop rows | 6 | 6 | 0 |
Exception rows: 0.
Additions to the Base UI API: 1.
| Entry | Kind | Name | Reason |
|---|---|---|---|
| Input | prop | disabled | The part gives its attributes to Field.Control, which declares disabled as a Boolean. The native attribute type Booleanish is not assignable to that prop, so the part declares disabled as Field.Control does. types.md has no row for a native attribute. |
Return types that differ from Base UI: 0.
interactions
Matches the Base UI commit 5aacdbb of 2026-11-10.
Queued changes: 0.
Vase UI rows with no upstream row: 187.
| Files | Total | Ported | Replaced | Not applicable | Waits on |
|---|---|---|---|---|---|
| Sources | 15 | 15 | 0 | 0 | 0 |
| Type tests | 0 | 0 | 0 | 0 | 0 |
| Tests | Total | Matched | Replaced | Not applicable | Waits on | Engine Chromium | Engine Firefox | Engine WebKit |
|---|---|---|---|---|---|---|---|---|
| Cases | 199 | 194 | 4 | 1 | 0 | 0 | 0 | 0 |
| Suite calls | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
Exception rows: 5.
| Thing | Kind | Reason | Detail |
|---|---|---|---|
| packages/react/src/utils/popups/interactions/useDismiss.test.tsx, case "useDismiss > default options > does not dismiss with the escape key that ends a composition spanning a reopen" | replaced | The case asserts that Escape directly after compositionend keeps the popup open. Only WebKit sends compositionend before the keydown that ends a composition, so only WebKit needs that window. In the other engines Vase UI closes the popup (BUG-6158). The case "ignores Escape for 5 ms after compositionend in WebKit" tests the window in WebKit. | Test: packages/vue/src/utils/popups/interactions/useDismiss.test.tsx |
| packages/react/src/utils/popups/interactions/useListNavigation.test.tsx, case "useListNavigation > focus management in nested lists" | replaced | Base UI runs the case only in jsdom. jsdom does not focus the first item at the open, and a browser does, so the keys of the case reach other items in a browser. In the block lists below the list in the component tree, the cases open a list below with its key, in a browser, and the first item of that list gets the focus. | Test: packages/vue/src/utils/popups/interactions/useListNavigation.test.tsx |
| packages/react/src/utils/popups/interactions/useListNavigation.test.tsx, case "useListNavigation > keyboard navigation in nested menus lists" | replaced | Base UI runs the case only in jsdom. jsdom does not focus the first item at the open, and a browser does, so the keys of the case reach other items in a browser. The case moves in a grid of a list below, and closes that list for Escape of the block lists below the list in the component tree makes the same moves in a browser. | Test: packages/vue/src/utils/popups/interactions/useListNavigation.test.tsx |
| packages/react/src/utils/popups/interactions/useListNavigation.test.tsx, case "useListNavigation > keyboard navigation in nested menus with different orientation" | replaced | Base UI runs the case only in jsdom. jsdom does not focus the first item at the open, and a browser does, so the keys of the case reach other items in a browser. The cases of VUE-46 in the block lists below the list in the component tree press the close key of a list below a horizontal list, in a browser. The list below closes, and the list above moves its highlight. | Test: packages/vue/src/utils/popups/interactions/useListNavigation.test.tsx |
| packages/react/test/useTestInteractions.test.tsx, case "useTestInteractions > prop getters are memoized" | not-applicable | setup runs one time, so the composable makes the getters one time and their identity cannot change. React makes new getters when a props object of the list changes. A new case covers the half that applies: a getter reads the current props of an entry that changes. |
Additions to the Base UI API: 0.
Return types that differ from Base UI: 0.
internals
Matches the Base UI commit 5aacdbb of 2026-11-10.
Queued changes: 0.
Vase UI rows with no upstream row: 86.
| Files | Total | Ported | Replaced | Not applicable | Waits on |
|---|---|---|---|---|---|
| Sources | 80 | 75 | 0 | 5 | 0 |
| Type tests | 2 | 1 | 0 | 1 | 0 |
| Tests | Total | Matched | Replaced | Not applicable | Waits on | Engine Chromium | Engine Firefox | Engine WebKit |
|---|---|---|---|---|---|---|---|---|
| Cases | 275 | 248 | 2 | 25 | 0 | 0 | 0 | 0 |
| Suite calls | 2 | 2 | 0 | 0 | 0 | 0 | 0 | 0 |
Exception rows: 23.
| Thing | Kind | Reason | Detail |
|---|---|---|---|
| packages/react/test/hiddenInputTypeTests.tsx, case "hidden input type > stays focusable through inputRef" | not-applicable | The case gives the React ref prop inputRef. A part of Vase UI exposes its input as inputElement, and a part cannot know if a consumer reads an exposed member. With no name, no autoComplete, no required and no field, the input thus stays type="hidden", which cannot take the focus. A consumer that focuses the input gives the control one of them. | |
| packages/react/src/internals/useRenderElement.test.tsx, case "useRenderElement > updates merged refs and event handlers when ref shape changes across rerenders" | not-applicable | A part gives its ref callbacks one time, in setup, so their shape cannot change. React makes a new merged ref when the ref argument changes. A new test covers the handler half: a listener of the part that changes between two renders. | |
| packages/react/src/internals/useRenderElement.test.tsx, case "useRenderElement > prop: render > warns when render is passed a function with an uppercase name" | not-applicable | The warning is for a React component that is given as the render function. In Vue the as prop takes a component, and #render is a slot. | |
| packages/react/src/internals/useRenderElement.test.tsx, case "useRenderElement > prop: render > warns when render is passed a function with an uppercase acronym prefix" | not-applicable | The warning is for a React component that is given as the render function. In Vue the as prop takes a component, and #render is a slot. | |
| packages/react/src/internals/useRenderElement.test.tsx, case "useRenderElement > prop: render > does not warn when render is passed a lowercase callback" | not-applicable | The warning is for a React component that is given as the render function. In Vue the as prop takes a component, and #render is a slot. | |
| packages/react/src/internals/useRenderElement.test.tsx, case "useRenderElement > prop: render > does not warn when render is passed a screaming snake case callback" | not-applicable | The warning is for a React component that is given as the render function. In Vue the as prop takes a component, and #render is a slot. | |
| packages/react/src/internals/useRenderElement.test.tsx, case "useRenderElement > prop: render > does not warn when render is passed a callback with an inferred useCallback name" | not-applicable | The warning is for a React component that is given as the render function. In Vue the as prop takes a component, and #render is a slot. | |
| packages/react/src/internals/useRenderElement.test.tsx, case "useRenderElement > prop: render > does not warn when render is passed as a React element" | not-applicable | The warning is for a React component that is given as the render function. In Vue the as prop takes a component, and #render is a slot. | |
| packages/react/src/internals/useRenderElement.test.tsx, block "useRenderElement > prop: render > lazy-wrapped render element (Flight shape)" | not-applicable | The cases test the lazy wrapper that the Flight client of React Server Components gives in place of a render element. | |
| packages/react/src/internals/useRenderElement.test.tsx, case "useRenderElement > prop: render > throws error for invalid render element in development" | not-applicable | The case tests React.isValidElement and React.cloneElement. No part clones a vnode. | |
| packages/react/src/internals/useRenderElement.spec.tsx | not-applicable | The file tests that the result type follows enabled. The Vue composable has no enabled parameter: the template decides with v-if. | |
| packages/react/src/internals/use-button/useButton.test.tsx, block "useButton > server-side rendering" | replaced | A server render runs in Node with no DOM. The cases are in the test file that the Node project runs, with the same titles. | Test: packages/vue/src/internals/use-button/useButton.node.test.tsx |
| packages/react/src/internals/composite/list/CompositeList.tsx | not-applicable | The file is a React component that gives the list context to its children. A Vue part calls useCompositeList of list/useCompositeList.ts in setup, which holds the registry of this file, and renders its own element. | |
| packages/react/src/internals/composite/list/CompositeList.test.tsx, case "<CompositeList /> > prop: elementsRef > syncs replacement refs without publishing an unchanged map" | not-applicable | The case replaces the ref objects of a mounted list. A Vue part gives elementsRef and labelsRef one time, in setup. The new case does not publish an unchanged map when a label changes covers the half that applies. | |
| packages/react/src/internals/composite/list/CompositeList.test.tsx, block "<CompositeList /> > Suspense integration" | not-applicable | React hides a mounted tree and detaches its refs when a component in it suspends again. A mounted Vue component cannot suspend again. | |
| packages/react/src/internals/composite/root/CompositeRoot.tsx | not-applicable | The file is a React component that renders the element of a part and gives it the composite context. A Vue part renders its own element and calls useCompositeRoot of root/useCompositeRoot.ts in setup, which holds the work of this file. | |
| packages/react/src/internals/composite/item/CompositeItem.tsx | not-applicable | The file is a React component that renders the element of an item part and gives it the tab stop. A Vue part renders its own element and calls useCompositeItem of item/useCompositeItem.ts in setup, which holds the work of this file. | |
| packages/react/src/internals/resolvePopupLabel.test.tsx, case "resolvePopupLabel > uses a render element's own label instead of the trigger" | not-applicable | The case gives a React element with an aria-label as render. In Vue #render is a slot, which is a function, so the part does not see an attribute of the element of the slot. resolvePopupLabel reads the attributes of the part. | |
| packages/react/src/internals/resolvePopupLabel.test.tsx, case "resolvePopupLabel > reads a server-created render element's label" | not-applicable | The case gives the lazy wrapper of the React Flight client as render. | |
| packages/react/src/internals/resolveRenderedAriaLabel.ts | not-applicable | The file reads the aria-label of a React element that is given as render. In Vue the as prop takes a tag or a component with no props of its own, and #render is a slot, which is a function. A control reads its aria-label prop. | |
| packages/react/src/internals/resolveRenderedAriaLabel.test.tsx | not-applicable | The cases test resolveRenderedAriaLabel, which reads a React element that is given as render. | |
| packages/react/src/internals/composite/index.ts | not-applicable | No Base UI component imports the file. It is the module of the package entry ./internals/composite, and Vase UI has no entry point for an internal module. | |
| packages/react/src/internals/useItemRegistry.test.tsx, case "useItemRegistry > publishes every registration under StrictMode double effects" | not-applicable | The case runs the effects of the items two times under StrictMode. Vue has no Strict Mode, and a registration runs one time in onMounted. |
Additions to the Base UI API: 0.
Return types that differ from Base UI: 0.
menu
Matches the Base UI commit 5aacdbb of 2026-11-10.
Queued changes: 0.
Vase UI rows with no upstream row: 207.
| Files | Total | Ported | Replaced | Not applicable | Waits on |
|---|---|---|---|---|---|
| Sources | 76 | 76 | 0 | 0 | 0 |
| Type tests | 3 | 3 | 0 | 0 | 0 |
| Tests | Total | Matched | Replaced | Not applicable | Waits on | Engine Chromium | Engine Firefox | Engine WebKit |
|---|---|---|---|---|---|---|---|---|
| Cases | 572 | 558 | 3 | 11 | 0 | 0 | 0 | 0 |
| Suite calls | 40 | 40 | 0 | 0 | 0 | 0 | 0 | 0 |
| API | Total | Matched | Excepted |
|---|---|---|---|
| Entries | 46 | 46 | 0 |
| Prop rows | 165 | 161 | 4 |
Exception rows: 17.
| Thing | Kind | Reason | Detail |
|---|---|---|---|
| packages/react/src/menu/filter-root/MenuFilterRoot.react17.test.tsx | not-applicable | The cases are for React 17, which has no useId. useVaseUiId gives an id in setup. | |
| packages/react/src/menu/filter-root/MenuFilterRoot.test.tsx, case "<Menu.FilterProvider><Menu.Root/></Menu.FilterProvider> > filtering > uses the id from a custom trigger render element for popup labelling" | not-applicable | The case gives a React element with an id as render. In Vue #render is a slot, which is a function, so the part does not see the id of the element of the slot. The id prop gives the same result. | |
| packages/react/src/menu/filter-root/MenuFilterRoot.test.tsx, case "<Menu.FilterProvider><Menu.Root/></Menu.FilterProvider> > filtering > uses an explicit label from the popup render element" | not-applicable | The case gives a React element with an aria-label as render. In Vue #render is a slot, which is a function, so the part does not see the aria-label of the element of the slot. The aria-label attribute of the part gives the same result. | |
| packages/react/src/menu/filter-root/MenuFilterRoot.test.tsx, case "<Menu.FilterProvider><Menu.Root/></Menu.FilterProvider> > accessible names > keeps a list label supplied through a render element" | not-applicable | The case gives a React element with an attribute of the list as render. In Vue #render is a slot, which is a function, so the part does not see an attribute of the element of the slot. The attribute of the part gives the same result. | |
| packages/react/src/menu/filter-root/MenuFilterRoot.test.tsx, case "<Menu.FilterProvider><Menu.Root/></Menu.FilterProvider> > accessible names > omits aria-controls when the list renders with an explicitly empty id" | not-applicable | The case gives a React element with an id as render. In Vue #render is a slot, which is a function, so the part does not see the id of the element of the slot. The id prop gives the same result. | |
| packages/react/src/menu/filter-root/MenuFilterRoot.test.tsx, case "<Menu.FilterProvider><Menu.Root/></Menu.FilterProvider> > accessible names > points aria-controls at the id of a server-created render element" | not-applicable | The case gives the lazy wrapper of the React Flight client as render. | |
| packages/react/src/menu/filter-root/MenuFilterRoot.webkit.test.tsx, case "<Menu.FilterProvider><Menu.Root/></Menu.FilterProvider> (WebKit) > keeps selection state for an auto-focused input in Strict Mode" | not-applicable | The case checks the selection state after the double effects of Strict Mode. Vue has no Strict Mode. | |
| packages/react/src/menu/root/MenuRoot.test.tsx, case "<Menu.Root /> > warns that nested menus ignore the modal prop" | replaced | The case gives modal to Menu.SubmenuRoot through a cast. That part has no such prop in Vase UI, and Vue warns about the attribute. The case 'warns that a menu in a menubar ignores modal={false}' covers the warning of Vase UI. | Test: packages/vue/src/menu/root/MenuRoot.test.tsx |
| packages/react/src/menu/root/MenuRoot.test.tsx, case "<Menu.Root /> > does not emit the nested modal warning in production" | replaced | The case runs only in jsdom, and it sets process.env.NODE_ENV to production. Vitest sets that value when it transforms a file for a browser, so only the Node project can change it. The case 'gives no warning for modal in a production build' of the Node file covers it. | Test: packages/vue/src/menu/root/MenuRoot.node.test.tsx |
| packages/react/src/menu/root/MenuRoot.test.tsx, case "<Menu.Root /> > submenu parent store subscription > releases the subscriptions a submenu adds to its parent store when it unmounts" | not-applicable | A submenu of Vase UI adds no subscription to the store of its parent. A selector of its store reads the reactive state of the parent. | |
| packages/react/src/menu/root/MenuRoot.test.tsx, case "<Menu.Root /> > submenu parent store subscription > releases them under StrictMode" | not-applicable | Vue has no Strict Mode. A submenu of Vase UI adds no subscription to the store of its parent. | |
| packages/react/src/menu/submenu-trigger/MenuSubmenuTrigger.test.tsx, case "<Menu.SubmenuTrigger /> > prop: disabled > warns without an owner stack when React cannot provide one" | not-applicable | captureOwnerStack is a React API. The warning of Vase UI has no owner stack. | |
| packages/react/src/menu/submenu-trigger/MenuSubmenuTrigger.test.tsx, case "<Menu.SubmenuTrigger /> > prop: disabled > does not inspect rendered disabled elements in production" | replaced | The case needs a production build. Vitest sets process.env.NODE_ENV when it transforms a file for a browser, so only the Node project can change it. A server render calls no onMounted hook, so the Node case asserts only that a server render in a production build gives no warning. | Test: packages/vue/src/menu/submenu-trigger/MenuSubmenuTrigger.node.test.tsx |
| docs/src/app/(docs)/react/components/menu/types.md, entry "Positioner", prop "side" | not-applicable | The row gives the default 'bottom'. The part has no Vue default: a prop that is absent takes 'inline-end' for a submenu and for a menubar with a vertical orientation, and 'bottom' for any other menu, as in Base UI. With a Vue default, the part cannot find out that the prop is absent. | |
| docs/src/app/(docs)/react/components/menu/types.md, entry "Positioner", prop "align" | not-applicable | The row gives the default 'center'. The part has no Vue default: a prop that is absent takes 'start' for a submenu, a menubar and a context menu, and 'center' for any other menu, as in Base UI. With a Vue default, the part cannot find out that the prop is absent. | |
| docs/src/app/(docs)/react/components/menu/types.md, entry "Positioner", prop "sideOffset" | not-applicable | The row gives the default 0. The part has no Vue default: a prop that is absent takes -5 for a context menu that has no side and an align other than 'center', and 0 for any other menu, as in Base UI. With a Vue default, the part cannot find out that the prop is absent. | |
| docs/src/app/(docs)/react/components/menu/types.md, entry "Positioner", prop "alignOffset" | not-applicable | The row gives the default 0. The part has no Vue default: a prop that is absent takes 2 for a context menu that has no side and an align other than 'center', and 0 for any other menu, as in Base UI. With a Vue default, the part cannot find out that the prop is absent. |
Additions to the Base UI API: 11.
| Entry | Kind | Name | Reason |
|---|---|---|---|
| Trigger | prop | id | The part reads id to register the trigger in the store of the menu, so it is a declared prop. Base UI reads it from the native attributes of the trigger, and types.md has no row for a native attribute. |
| Input | prop | id | The part replaces an absent id with a generated id and renders it on the input, so it is a declared prop. Base UI reads it from the native attributes of the input, and types.md has no row for a native attribute. |
| Input | prop | disabled | The part reads disabled for its state and for the attribute of the input, so it is a declared prop. Base UI reads it from the native attributes of the input, and types.md has no row for a native attribute. |
| List | prop | id | The part replaces an absent id with a generated id and gives it to the store, which the trigger names in aria-controls. It is thus a declared prop. Base UI reads it from the native attributes of the list, and types.md has no row for a native attribute. |
| Popup | prop | id | The part replaces an absent id with a generated id and gives the id to the root, which the trigger names in aria-controls. Base UI declares id as a prop with the tag @ignore, so types.md has no row for it. |
| Item | prop | id | The part replaces an absent id with a generated id and renders it on the item. Base UI declares id as a prop with the tag @ignore, so types.md has no row for it. |
| LinkItem | prop | id | The part replaces an absent id with a generated id and renders it on the link. Base UI declares id as a prop with the tag @ignore, so types.md has no row for it. |
| SubmenuTrigger | prop | id | The part replaces an absent id with a generated id and registers the trigger in the store of the submenu with it. Base UI declares id as a prop with the tag @ignore, so types.md has no row for it. |
| GroupLabel | prop | id | The part replaces an absent id with a generated id and gives it to the group for aria-labelledby. It is thus a declared prop. Base UI reads it from the native attributes of the label, and types.md has no row for a native attribute. |
| RadioItem | prop | id | The part replaces an absent id with a generated id and renders it on the item. Base UI declares id as a prop with the tag @ignore, so types.md has no row for it. |
| CheckboxItem | prop | id | The part replaces an absent id with a generated id and renders it on the item. Base UI declares id as a prop with the tag @ignore, so types.md has no row for it. |
Return types that differ from Base UI: 2.
| Entry | Vase UI returns | Reason |
|---|---|---|
| createHandle | MenuHandle<Payload> | Menu.Handle is the class MenuHandle. The declaration of the function names the class, and Base UI documents the same class by its name in the namespace. |
| useFilter | Filter | Base UI documents the return type as MenuFilter. The function returns Filter, and MenuFilter is that type under another name. |
menubar
Matches the Base UI commit 5aacdbb of 2026-11-10.
Queued changes: 0.
Vase UI rows with no upstream row: 18.
| Files | Total | Ported | Replaced | Not applicable | Waits on |
|---|---|---|---|---|---|
| Sources | 4 | 4 | 0 | 0 | 0 |
| Type tests | 0 | 0 | 0 | 0 | 0 |
| Tests | Total | Matched | Replaced | Not applicable | Waits on | Engine Chromium | Engine Firefox | Engine WebKit |
|---|---|---|---|---|---|---|---|---|
| Cases | 44 | 44 | 0 | 0 | 0 | 0 | 0 | 0 |
| Suite calls | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 |
| API | Total | Matched | Excepted |
|---|---|---|---|
| Entries | 2 | 2 | 0 |
| Prop rows | 7 | 7 | 0 |
Exception rows: 0.
Additions to the Base UI API: 1.
| Entry | Kind | Name | Reason |
|---|---|---|---|
| Menubar | prop | id | The part reads id to give it to the menus of the menubar as the id of their root, so it is a declared prop. Base UI reads it from the native attributes of the menubar, and types.md has no row for a native attribute. |
Return types that differ from Base UI: 0.
merge-props
Matches the Base UI commit 5aacdbb of 2026-11-10.
Queued changes: 0.
Vase UI rows with no upstream row: 9.
| Files | Total | Ported | Replaced | Not applicable | Waits on |
|---|---|---|---|---|---|
| Sources | 2 | 2 | 0 | 0 | 0 |
| Type tests | 0 | 0 | 0 | 0 | 0 |
| Tests | Total | Matched | Replaced | Not applicable | Waits on | Engine Chromium | Engine Firefox | Engine WebKit |
|---|---|---|---|---|---|---|---|---|
| Cases | 30 | 30 | 0 | 0 | 0 | 0 | 0 | 0 |
| Suite calls | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| API | Total | Matched | Excepted |
|---|---|---|---|
| Entries | 4 | 4 | 0 |
| Prop rows | 0 | 0 | 0 |
Exception rows: 0.
Additions to the Base UI API: 0.
Return types that differ from Base UI: 3.
| Entry | Vase UI returns | Reason |
|---|---|---|
| makeEventPreventable | VaseUIEvent<T> | Vue has no synthetic event. The event is the DOM event, and VaseUIEvent adds preventVaseUIHandler() to it. |
| mergeProps | MergedProps<T> | With a type argument, the type is NativeElements[T]: the attributes of the element T, as Vue declares them. With no type argument, it is NativeElements['div']: the attributes that each HTML element has. Base UI's type is then in effect any. Each listener gets the DOM event, so a template can bind the result. Each handler of a set gets a VaseUIEvent. |
| mergePropsN | MergedProps<T> | With a type argument, the type is NativeElements[T]: the attributes of the element T, as Vue declares them. With no type argument, it is NativeElements['div']: the attributes that each HTML element has. Base UI's type is then in effect any. Each listener gets the DOM event, so a template can bind the result. Each handler of a set gets a VaseUIEvent. |
meter
Matches the Base UI commit 5aacdbb of 2026-11-10.
Queued changes: 0.
Vase UI rows with no upstream row: 10.
| Files | Total | Ported | Replaced | Not applicable | Waits on |
|---|---|---|---|---|---|
| Sources | 8 | 8 | 0 | 0 | 0 |
| Type tests | 0 | 0 | 0 | 0 | 0 |
| Tests | Total | Matched | Replaced | Not applicable | Waits on | Engine Chromium | Engine Firefox | Engine WebKit |
|---|---|---|---|---|---|---|---|---|
| Cases | 25 | 25 | 0 | 0 | 0 | 0 | 0 | 0 |
| Suite calls | 5 | 5 | 0 | 0 | 0 | 0 | 0 | 0 |
| API | Total | Matched | Excepted |
|---|---|---|---|
| Entries | 5 | 5 | 0 |
| Prop rows | 23 | 22 | 1 |
Exception rows: 1.
| Thing | Kind | Reason | Detail |
|---|---|---|---|
| docs/src/app/(docs)/react/components/meter/types.md, entry "Root", prop "aria-valuetext" | not-applicable | The row documents a native attribute that the React part does not read. In Vase UI the attribute goes to the element through the attributes, with its type from HTMLAttributes. A declared prop would take it out of the attributes. |
Additions to the Base UI API: 1.
| Entry | Kind | Name | Reason |
|---|---|---|---|
| Label | prop | id | The part reads id to give it to the root for aria-labelledby, so it is a declared prop. Base UI reads it from the native attributes of the label, and types.md has no row for a native attribute. |
Return types that differ from Base UI: 0.
navigation-menu
Matches the Base UI commit 5aacdbb of 2026-11-10.
Queued changes: 0.
Vase UI rows with no upstream row: 108.
| Files | Total | Ported | Replaced | Not applicable | Waits on |
|---|---|---|---|---|---|
| Sources | 33 | 33 | 0 | 0 | 0 |
| Type tests | 2 | 2 | 0 | 0 | 0 |
| Tests | Total | Matched | Replaced | Not applicable | Waits on | Engine Chromium | Engine Firefox | Engine WebKit |
|---|---|---|---|---|---|---|---|---|
| Cases | 129 | 122 | 5 | 2 | 0 | 0 | 0 | 0 |
| Suite calls | 13 | 13 | 0 | 0 | 0 | 0 | 0 | 0 |
| API | Total | Matched | Excepted |
|---|---|---|---|
| Entries | 23 | 23 | 0 |
| Prop rows | 67 | 66 | 1 |
Exception rows: 6.
| Thing | Kind | Reason | Detail |
|---|---|---|---|
| packages/react/src/navigation-menu/content/NavigationMenuContent.test.tsx, block "<NavigationMenu.Content /> > server-side rendering" | replaced | Each case of the block renders on a server with no hydration, so each case is in the Node file of the folder. | Test: packages/vue/src/navigation-menu/content/NavigationMenuContent.node.test.tsx |
| packages/react/src/navigation-menu/trigger/NavigationMenuTrigger.test.tsx, case "<NavigationMenu.Trigger /> > handles focus and positioner height" | replaced | Base UI runs its browser tests only in Chromium, and the case asserts pixel sizes of the default font of Chromium. Firefox and WebKit have other default fonts. The Vase UI case compares the size variable of the positioner with the measured size of the content, so it runs in each engine. | Test: packages/vue/src/navigation-menu/trigger/NavigationMenuTrigger.test.tsx |
| packages/react/src/navigation-menu/trigger/NavigationMenuTrigger.test.tsx, case "<NavigationMenu.Trigger /> > handles positioner width correctly" | replaced | Base UI runs its browser tests only in Chromium, and the case asserts pixel sizes of the default font of Chromium. Firefox and WebKit have other default fonts. The Vase UI case compares the size variable of the positioner with the measured size of the content, so it runs in each engine. | Test: packages/vue/src/navigation-menu/trigger/NavigationMenuTrigger.test.tsx |
| packages/react/src/navigation-menu/root/NavigationMenuRoot.test.tsx, case "<NavigationMenu.Root /> > nested menus > inline nested viewport > does not animate popup sizing when kept nested default content first moves into the portal" | replaced | The case gives the popup a shell height for the first read of offsetHeight and the full height for the next reads, after the click. In Vue, the popup measures its open size in the flush of the click, before the case can mock the size, and the kept nested content is in the portal at that measure. The Vase UI case measures the real layout: each fixed height of the popup is its open height. | Test: packages/vue/src/navigation-menu/root/NavigationMenuRoot.test.tsx |
| packages/react/src/navigation-menu/root/NavigationMenuRootContext.test.ts | not-applicable | The cases assert the displayName of a React context. A Vue injection key has no such name. | |
| docs/src/app/(docs)/react/components/navigation-menu/types.md, entry "Root", prop "render" | not-applicable | The default tag is nav, or div when the root is nested. The part decides it in setup, so the prop as has no static default. |
Additions to the Base UI API: 2.
| Entry | Kind | Name | Reason |
|---|---|---|---|
| Popup | prop | id | The part reads id to replace an absent id with a generated id, which the active trigger names in aria-controls. It is thus a declared prop. Base UI reads it from the native attributes of the popup, and types.md has no row for a native attribute. |
| Viewport | prop | id | The part reads id to replace an absent id with a generated id, which the owner element of the active trigger names in aria-owns. It is thus a declared prop. Base UI reads it from the native attributes of the viewport, and types.md has no row for a native attribute. |
Return types that differ from Base UI: 0.
number-field
Matches the Base UI commit 5aacdbb of 2026-11-10.
Queued changes: 0.
Vase UI rows with no upstream row: 87.
| Files | Total | Ported | Replaced | Not applicable | Waits on |
|---|---|---|---|---|---|
| Sources | 25 | 25 | 0 | 0 | 0 |
| Type tests | 2 | 2 | 0 | 0 | 0 |
| Tests | Total | Matched | Replaced | Not applicable | Waits on | Engine Chromium | Engine Firefox | Engine WebKit |
|---|---|---|---|---|---|---|---|---|
| Cases | 475 | 473 | 1 | 1 | 0 | 0 | 0 | 0 |
| Suite calls | 8 | 8 | 0 | 0 | 0 | 0 | 0 | 0 |
| API | Total | Matched | Excepted |
|---|---|---|---|
| Entries | 14 | 14 | 0 |
| Prop rows | 48 | 47 | 1 |
Exception rows: 3.
| Thing | Kind | Reason | Detail |
|---|---|---|---|
| packages/react/src/number-field/input/NumberFieldInput.test.tsx, case "<NumberField.Input /> > warns without an owner stack when React cannot provide one" | not-applicable | The case tests the owner stack of React. Vue has no owner stack. | |
| packages/react/src/number-field/utils/parse.test.ts, case "NumberField parse > parseNumber > keeps only the last of many dots" | replaced | The case reads .0.0.0 … .5 as 0.5. Vase UI does not read dots that are not group separators (BUG-5424, https://github.com/mui/base-ui/issues/5424), so that text gives null. The case "BUG-5424: keeps only the last of many dots that are group separators" tests the same count of dots with groups of three digits, and it tests that the text of Base UI gives null. | Test: packages/vue/src/number-field/utils/parse.test.ts |
| docs/src/app/(docs)/react/components/number-field/types.md, entry "Input", prop "aria-roledescription" | not-applicable | The row documents a native attribute and its default. In Vase UI the attribute goes to the element through the attributes, with its type from InputHTMLAttributes, and it replaces the default 'Number field'. A declared prop would take it out of the attributes. |
Additions to the Base UI API: 2.
| Entry | Kind | Name | Reason |
|---|---|---|---|
| Increment | prop | disabled | The part reads disabled for its state and for useButton, so it is a declared prop. Base UI reads it from the native attributes of the button, and types.md has no row for a native attribute. |
| Decrement | prop | disabled | The part reads disabled for its state and for useButton, so it is a declared prop. Base UI reads it from the native attributes of the button, and types.md has no row for a native attribute. |
Return types that differ from Base UI: 0.
otp-field
Matches the Base UI commit 5aacdbb of 2026-11-10.
Queued changes: 0.
Vase UI rows with no upstream row: 54.
| Files | Total | Ported | Replaced | Not applicable | Waits on |
|---|---|---|---|---|---|
| Sources | 9 | 9 | 0 | 0 | 0 |
| Type tests | 2 | 2 | 0 | 0 | 0 |
| Tests | Total | Matched | Replaced | Not applicable | Waits on | Engine Chromium | Engine Firefox | Engine WebKit |
|---|---|---|---|---|---|---|---|---|
| Cases | 163 | 159 | 3 | 1 | 0 | 0 | 0 | 0 |
| Suite calls | 3 | 3 | 0 | 0 | 0 | 0 | 0 | 0 |
| API | Total | Matched | Excepted |
|---|---|---|---|
| Entries | 6 | 6 | 0 |
| Prop rows | 28 | 28 | 0 |
Exception rows: 3.
| Thing | Kind | Reason | Detail |
|---|---|---|---|
| packages/react/src/otp-field/root/OTPFieldRoot.react17.test.tsx | not-applicable | The file mocks SafeReact to test the id fallback of React 17. Vue has useId in each version that the package supports. | |
| packages/react/src/otp-field/root/OTPFieldRoot.test.tsx, case "<OTPField.Root /> > value handling > prop: validationType > hidden validation input > renders the hidden validation input with an alphanumeric pattern during SSR" | replaced | A server render with no hydrate() runs in Node with no DOM. The case is in the test file that the Node project runs, with the same titles. | Test: packages/vue/src/otp-field/root/OTPFieldRoot.node.test.tsx |
| packages/react/src/otp-field/root/OTPFieldRoot.test.tsx, block "<OTPField.Root /> > Form > server-side rendering" | replaced | A server render runs in Node with no DOM. The two cases are in the test file that the Node project runs, with the same titles. | Test: packages/vue/src/otp-field/root/OTPFieldRoot.node.test.tsx |
Additions to the Base UI API: 2.
| Entry | Kind | Name | Reason |
|---|---|---|---|
| Root | prop | ariaLabelledby | The part reads aria-labelledby to find out if a label names the group and the inputs, and $attrs is not reactive, so it is a declared prop. Base UI reads it from the native attributes of the element, and types.md has no row for a native attribute. |
| Root | changeEventReason | none | A native reset changes the value. Base UI does not follow a reset. |
Return types that differ from Base UI: 0.
popover
Matches the Base UI commit 5aacdbb of 2026-11-10.
Queued changes: 0.
Vase UI rows with no upstream row: 57.
| Files | Total | Ported | Replaced | Not applicable | Waits on |
|---|---|---|---|---|---|
| Sources | 29 | 29 | 0 | 0 | 0 |
| Type tests | 2 | 2 | 0 | 0 | 0 |
| Tests | Total | Matched | Replaced | Not applicable | Waits on | Engine Chromium | Engine Firefox | Engine WebKit |
|---|---|---|---|---|---|---|---|---|
| Cases | 163 | 160 | 0 | 3 | 0 | 0 | 0 | 0 |
| Suite calls | 15 | 15 | 0 | 0 | 0 | 0 | 0 | 0 |
| API | Total | Matched | Excepted |
|---|---|---|---|
| Entries | 22 | 22 | 0 |
| Prop rows | 65 | 65 | 0 |
Exception rows: 3.
| Thing | Kind | Reason | Detail |
|---|---|---|---|
| packages/react/src/popover/root/PopoverRoot.detached-triggers.test.tsx, case "<Popover.Root /> > hands off hover between detached triggers when the rendered component retains a stale ref" | not-applicable | The case renders a component that keeps the first ref callback that it got. A part gives its ref callbacks one time, in setup, and the callback of a trigger only writes the element to a ref. A watcher on the element, the id and the store then registers the trigger on the store that it has at that time. A callback that a component keeps thus cannot register the trigger on the store that the trigger left. | |
| packages/react/src/popover/root/PopoverRoot.detached-triggers.test.tsx, case "<Popover.Root /> > keeps registration on the attached store when a stale-ref component swaps its host node" | not-applicable | The case renders a component that keeps the first ref callback that it got, and swaps its host node. A part gives its ref callbacks one time, in setup, and the callback of a trigger only writes the element to a ref. The watcher of the registration then moves the registration to the new element on the store that the trigger has at that time. | |
| packages/react/src/popover/root/PopoverRoot.detached-triggers.test.tsx, case "<Popover.Root /> > does not detach the consumer ref when the handle attaches to a root" | not-applicable | The case counts the calls of the ref callback of a consumer. Base UI merges that callback with the ref callbacks of the part. A new merged ref for a change of the store would give the consumer a call with null and a new call, and the case guards against it. A part gives its own ref callbacks one time, in setup, and Vue does not merge the ref of a consumer with them. A change of the store thus changes no ref. |
Additions to the Base UI API: 4.
| Entry | Kind | Name | Reason |
|---|---|---|---|
| Trigger | prop | disabled | The part reads disabled for its state and for useButton, so it is a declared prop. Base UI reads it from the native attributes of the trigger, and types.md has no row for a native attribute. |
| Title | prop | id | The part reads id to register it with the root for aria-labelledby of the popup, so it is a declared prop. Base UI reads it from the native attributes of the title, and types.md has no row for a native attribute. |
| Description | prop | id | The part reads id to register it with the root for aria-describedby of the popup, so it is a declared prop. Base UI reads it from the native attributes of the description, and types.md has no row for a native attribute. |
| Close | prop | disabled | The part reads disabled for its state and for useButton, so it is a declared prop. Base UI reads it from the native attributes of the button, and types.md has no row for a native attribute. |
Return types that differ from Base UI: 1.
| Entry | Vase UI returns | Reason |
|---|---|---|
| createHandle | PopoverHandle<Payload> | Popover.Handle is the class PopoverHandle. The declaration of the function names the class, and Base UI documents the same class by its name in the namespace. |
popups
Matches the Base UI commit 5aacdbb of 2026-11-10.
Queued changes: 0.
Vase UI rows with no upstream row: 125.
| Files | Total | Ported | Replaced | Not applicable | Waits on |
|---|---|---|---|---|---|
| Sources | 32 | 32 | 0 | 0 | 0 |
| Type tests | 2 | 2 | 0 | 0 | 0 |
| Tests | Total | Matched | Replaced | Not applicable | Waits on | Engine Chromium | Engine Firefox | Engine WebKit |
|---|---|---|---|---|---|---|---|---|
| Cases | 265 | 264 | 1 | 0 | 0 | 0 | 0 | 0 |
| Suite calls | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
Exception rows: 1.
| Thing | Kind | Reason | Detail |
|---|---|---|---|
| packages/react/src/utils/popups/popupTriggerMap.test.ts, case "PopupTriggerMap > does not throw in production when the same element is registered under multiple ids" | replaced | The case needs a production build. Vitest sets process.env.NODE_ENV when it transforms a file for a browser, so only the Node project can change it. | Test: packages/vue/src/utils/popups/popupTriggerMap.node.test.ts |
Additions to the Base UI API: 0.
Return types that differ from Base UI: 0.
portal
Matches the Base UI commit 5aacdbb of 2026-11-10.
Queued changes: 0.
Vase UI rows with no upstream row: 2.
| Files | Total | Ported | Replaced | Not applicable | Waits on |
|---|---|---|---|---|---|
| Sources | 2 | 2 | 0 | 0 | 0 |
| Type tests | 0 | 0 | 0 | 0 | 0 |
| Tests | Total | Matched | Replaced | Not applicable | Waits on | Engine Chromium | Engine Firefox | Engine WebKit |
|---|---|---|---|---|---|---|---|---|
| Cases | 17 | 14 | 1 | 2 | 0 | 0 | 0 | 0 |
| Suite calls | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
Exception rows: 3.
| Thing | Kind | Reason | Detail |
|---|---|---|---|
| packages/react/src/utils/popups/portal/FloatingPortal.test.tsx, case "$name container resolution > $name without canceling a view transition" | not-applicable | The cases use React.ViewTransition to test that the retry of the container check does not cancel a view transition. Vue has no such retry. | |
| packages/react/src/utils/popups/portal/FloatingPortal.test.tsx, case "$name container resolution > does not cancel a view transition that reveals a hidden pending portal" | not-applicable | The case uses React.ViewTransition and React.Activity to test that the retry of the container check does not cancel a view transition. Vue has no such retry. | |
| packages/react/src/utils/popups/portal/FloatingPortal.test.tsx, case "$name container resolution > mounts children once in a connected container when a later update replaces its ref" | replaced | The Vue portal gets its container in the flush after the mount. When updates in that flush replace the container, the children mount in the first container. That container is out of the document when their onMounted hooks run. Vue then moves the children to the new container. The case "mounts the children once, and moves them when updates in the flush of the mount replace the container" tests this. | Test: packages/vue/src/utils/popups/portal/FloatingPortalLite.test.tsx |
Additions to the Base UI API: 0.
Return types that differ from Base UI: 0.
preview-card
Matches the Base UI commit 5aacdbb of 2026-11-10.
Queued changes: 0.
Vase UI rows with no upstream row: 30.
| Files | Total | Ported | Replaced | Not applicable | Waits on |
|---|---|---|---|---|---|
| Sources | 24 | 24 | 0 | 0 | 0 |
| Type tests | 3 | 3 | 0 | 0 | 0 |
| Tests | Total | Matched | Replaced | Not applicable | Waits on | Engine Chromium | Engine Firefox | Engine WebKit |
|---|---|---|---|---|---|---|---|---|
| Cases | 59 | 59 | 0 | 0 | 0 | 0 | 0 | 0 |
| Suite calls | 11 | 11 | 0 | 0 | 0 | 0 | 0 | 0 |
| API | Total | Matched | Excepted |
|---|---|---|---|
| Entries | 18 | 18 | 0 |
| Prop rows | 49 | 49 | 0 |
Exception rows: 0.
Additions to the Base UI API: 1.
| Entry | Kind | Name | Reason |
|---|---|---|---|
| Trigger | prop | id | The part reads id to register the trigger in the store of the preview card, so it is a declared prop. Base UI reads it from the native attributes of the trigger, and types.md has no row for a native attribute. |
Return types that differ from Base UI: 1.
| Entry | Vase UI returns | Reason |
|---|---|---|
| createHandle | PreviewCardHandle<Payload> | PreviewCard.Handle is the class PreviewCardHandle. The declaration of the function names the class, and Base UI documents the same class by its name in the namespace. |
progress
Matches the Base UI commit 5aacdbb of 2026-11-10.
Queued changes: 0.
Vase UI rows with no upstream row: 10.
| Files | Total | Ported | Replaced | Not applicable | Waits on |
|---|---|---|---|---|---|
| Sources | 14 | 14 | 0 | 0 | 0 |
| Type tests | 0 | 0 | 0 | 0 | 0 |
| Tests | Total | Matched | Replaced | Not applicable | Waits on | Engine Chromium | Engine Firefox | Engine WebKit |
|---|---|---|---|---|---|---|---|---|
| Cases | 25 | 25 | 0 | 0 | 0 | 0 | 0 | 0 |
| Suite calls | 5 | 5 | 0 | 0 | 0 | 0 | 0 | 0 |
| API | Total | Matched | Excepted |
|---|---|---|---|
| Entries | 10 | 10 | 0 |
| Prop rows | 23 | 22 | 1 |
Exception rows: 1.
| Thing | Kind | Reason | Detail |
|---|---|---|---|
| docs/src/app/(docs)/react/components/progress/types.md, entry "Root", prop "aria-valuetext" | not-applicable | The row documents a native attribute that the React part does not read. In Vase UI the attribute goes to the element through the attributes, with its type from HTMLAttributes. A declared prop would take it out of the attributes. |
Additions to the Base UI API: 1.
| Entry | Kind | Name | Reason |
|---|---|---|---|
| Label | prop | id | The part reads id to give it to the root for aria-labelledby, so it is a declared prop. Base UI reads it from the native attributes of the label, and types.md has no row for a native attribute. |
Return types that differ from Base UI: 0.
radio
Matches the Base UI commit 5aacdbb of 2026-11-10.
Queued changes: 0.
Vase UI rows with no upstream row: 15.
| Files | Total | Ported | Replaced | Not applicable | Waits on |
|---|---|---|---|---|---|
| Sources | 8 | 8 | 0 | 0 | 0 |
| Type tests | 1 | 1 | 0 | 0 | 0 |
| Tests | Total | Matched | Replaced | Not applicable | Waits on | Engine Chromium | Engine Firefox | Engine WebKit |
|---|---|---|---|---|---|---|---|---|
| Cases | 19 | 18 | 0 | 1 | 0 | 0 | 0 | 0 |
| Suite calls | 3 | 3 | 0 | 0 | 0 | 0 | 0 | 0 |
| API | Total | Matched | Excepted |
|---|---|---|---|
| Entries | 4 | 4 | 0 |
| Prop rows | 13 | 13 | 0 |
Exception rows: 1.
| Thing | Kind | Reason | Detail |
|---|---|---|---|
| packages/react/src/radio/root/RadioRoot.test.tsx, case "<Radio.Root /> > prefers a render element's `aria-label` over an associated label" | not-applicable | The case gives the aria-label as a prop of a React element in render. Base UI reads that prop with React.isValidElement. In Vue, an element with props of its own is the #render slot, and a part does not see an attribute that the slot adds. Base UI also does not see an attribute that a render function adds. |
Additions to the Base UI API: 3.
| Entry | Kind | Name | Reason |
|---|---|---|---|
| Root | prop | id | The part reads id to name its hidden input and its element, and $attrs is not reactive, so it is a declared prop. Base UI reads it from the native attributes of the element, and types.md has no row for a native attribute. |
| Root | prop | ariaLabel | The part reads aria-label to find out if a label names the radio, and $attrs is not reactive, so it is a declared prop. Base UI reads it from the native attributes of the element, and types.md has no row for a native attribute. |
| Root | prop | ariaLabelledby | The part reads aria-labelledby to find out if a label names the radio, and $attrs is not reactive, so it is a declared prop. Base UI reads it from the native attributes of the element, and types.md has no row for a native attribute. |
Return types that differ from Base UI: 0.
radio-group
Matches the Base UI commit 5aacdbb of 2026-11-10.
Queued changes: 0.
Vase UI rows with no upstream row: 35.
| Files | Total | Ported | Replaced | Not applicable | Waits on |
|---|---|---|---|---|---|
| Sources | 4 | 4 | 0 | 0 | 0 |
| Type tests | 1 | 1 | 0 | 0 | 0 |
| Tests | Total | Matched | Replaced | Not applicable | Waits on | Engine Chromium | Engine Firefox | Engine WebKit |
|---|---|---|---|---|---|---|---|---|
| Cases | 88 | 83 | 0 | 5 | 0 | 0 | 0 | 0 |
| Suite calls | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 |
| API | Total | Matched | Excepted |
|---|---|---|---|
| Entries | 6 | 2 | 4 |
| Prop rows | 25 | 12 | 13 |
Exception rows: 9.
| Thing | Kind | Reason | Detail |
|---|---|---|---|
| packages/react/src/radio-group/RadioGroup.test.tsx, case "<RadioGroup /> > supports inputRef as a function" | not-applicable | The case gives inputRef as a ref callback. Vue has no inputRef prop. The part exposes inputElement. | |
| packages/react/src/radio-group/RadioGroup.test.tsx, case "<RadioGroup /> > does not detach a stable inputRef callback on unrelated re-renders" | not-applicable | The case tests that a ref callback stays attached when the parent renders again. Vue has no inputRef prop. The part exposes inputElement. | |
| packages/react/src/radio-group/RadioGroup.test.tsx, case "<RadioGroup /> > transfers the current input when the object inputRef changes" | not-applicable | The case changes the inputRef prop from one ref object to another. Vue has no inputRef prop. The part exposes inputElement. | |
| packages/react/src/radio-group/RadioGroup.test.tsx, case "<RadioGroup /> > detaches the previous callback inputRef when it changes (cleanup: %s)" | not-applicable | The case changes the inputRef prop between ref callbacks, with and without a cleanup function. Vue has no inputRef prop. The part exposes inputElement. | |
| packages/react/src/radio-group/RadioGroup.test.tsx, case "<RadioGroup /> > cleans up inputRef bindings when selecting, clearing, and unmounting" | not-applicable | The case counts the cleanups of a ref callback. Vue has no inputRef prop. The cases "points inputRef to the checked radio input when present", "keeps inputRef pointing to the first radio when the value is cleared" and "detaches inputRef when its current radio unmounts" test the same changes of inputElement. | |
| docs/src/app/(docs)/react/components/radio-group/types.md, entry "Root" | not-applicable | The types.md of Radio Group repeats this entry of Radio. The family radio ports it. | |
| docs/src/app/(docs)/react/components/radio-group/types.md, entry "Indicator" | not-applicable | The types.md of Radio Group repeats this entry of Radio. The family radio ports it. | |
| docs/src/app/(docs)/react/components/radio-group/types.md, entry "RadioRootDataAttributes" | not-applicable | The types.md of Radio Group repeats this entry of Radio. The family radio ports it. | |
| docs/src/app/(docs)/react/components/radio-group/types.md, entry "RadioIndicatorDataAttributes" | not-applicable | The types.md of Radio Group repeats this entry of Radio. The family radio ports it. |
Additions to the Base UI API: 1.
| Entry | Kind | Name | Reason |
|---|---|---|---|
| RadioGroup | prop | id | The part gives id to the field as the id of its control and follows a change of it, so it is a declared prop. Base UI reads it from the native attributes of the element, and types.md has no row for a native attribute. |
Return types that differ from Base UI: 0.
scroll-area
Matches the Base UI commit 5aacdbb of 2026-11-10.
Queued changes: 0.
Vase UI rows with no upstream row: 27.
| Files | Total | Ported | Replaced | Not applicable | Waits on |
|---|---|---|---|---|---|
| Sources | 22 | 22 | 0 | 0 | 0 |
| Type tests | 0 | 0 | 0 | 0 | 0 |
| Tests | Total | Matched | Replaced | Not applicable | Waits on | Engine Chromium | Engine Firefox | Engine WebKit |
|---|---|---|---|---|---|---|---|---|
| Cases | 98 | 97 | 0 | 1 | 0 | 0 | 0 | 0 |
| Suite calls | 6 | 6 | 0 | 0 | 0 | 0 | 0 | 0 |
| API | Total | Matched | Excepted |
|---|---|---|---|
| Entries | 14 | 14 | 0 |
| Prop rows | 21 | 21 | 0 |
Exception rows: 1.
| Thing | Kind | Reason | Detail |
|---|---|---|---|
| packages/react/src/scroll-area/scrollbar/ScrollAreaScrollbar.test.tsx, case "<ScrollArea.Scrollbar /> > data-hovering attribute > adds [data-hovering] when the synthetic pointer target differs from the native path" | not-applicable | The case dispatches pointerover and tests the target of a React synthetic enter event. A Vue listener gets the native event, whose path starts at its target. An own case covers the native event. |
Additions to the Base UI API: 0.
Return types that differ from Base UI: 0.
select
Matches the Base UI commit 5aacdbb of 2026-11-10.
Queued changes: 0.
Vase UI rows with no upstream row: 81.
| Files | Total | Ported | Replaced | Not applicable | Waits on |
|---|---|---|---|---|---|
| Sources | 40 | 40 | 0 | 0 | 0 |
| Type tests | 2 | 2 | 0 | 0 | 0 |
| Tests | Total | Matched | Replaced | Not applicable | Waits on | Engine Chromium | Engine Firefox | Engine WebKit |
|---|---|---|---|---|---|---|---|---|
| Cases | 332 | 327 | 4 | 1 | 0 | 0 | 0 | 2 |
| Suite calls | 23 | 23 | 0 | 0 | 0 | 0 | 0 | 0 |
| API | Total | Matched | Excepted |
|---|---|---|---|
| Entries | 32 | 32 | 0 |
| Prop rows | 109 | 108 | 1 |
Exception rows: 8.
| Thing | Kind | Reason | Detail |
|---|---|---|---|
| packages/react/src/select/root/SelectRoot.test.tsx, case "<Select.Root /> > server-side rendering > does not link Select.Label before hydration" | replaced | The case renders on a server only. Vase UI links the label in the server HTML (VUE-51). The block "VUE-51: a label of the select" asserts aria-labelledby in the server HTML, with the label before and after the trigger. | Test: packages/vue/src/select/root/SelectRoot.node.test.tsx |
| packages/react/src/select/root/SelectRoot.test.tsx, case "<Select.Root /> > prop: highlightItemOnHover > highlights an item on mouse move by default" | engine | WebKit sends a mousemove with no movement when a list scrolls under a pointer that does not move. In WebKit, the list navigation thus ignores a mousemove with no movement, and the hover of user-event sends one. | Engine: webkit |
| packages/react/src/select/root/SelectRoot.test.tsx, case "<Select.Root /> > does not dismiss when pressing portalled content inside the popup but outside the list" | replaced | The case presses a plain element in a portal in the popup. Vue gives that event to no listener of the popup element, so that element is outside the popup. A press in a child popup that Teleport moved is inside (VUE-4). The case "VUE-4: a press in a child popup that Teleport moved does not close the parent popup" tests that press. | Test: packages/vue/src/utils/popups/interactions/useDismiss.test.tsx |
| packages/react/src/select/group-label/SelectGroupLabel.test.tsx, case "<Select.GroupLabel /> > replaces and unregisters its label in Strict Mode" | not-applicable | The case tests React Strict Mode. Vue has no Strict Mode. | |
| packages/react/src/select/item/SelectItem.test.tsx, case "<Select.Item /> > should select the item and close popup when clicked" | replaced | The case renders Select.Item in Select.Positioner with no Select.Popup. The popup root unmounts after the exit animations of the popup element, so with no popup element the positioner stays mounted and visible after the close. The case "selects the item with a click and hides the positioner after the popup closes" renders the popup. | Test: packages/vue/src/select/item/SelectItem.test.tsx |
| packages/react/src/select/item/SelectItem.test.tsx, case "<Select.Item /> > should highlight a hovered item and commit it with a mouse click" | engine | WebKit sends a mousemove with no movement when a list scrolls under a pointer that does not move. In WebKit, the list navigation thus ignores a mousemove with no movement, and the case dispatches one. | Engine: webkit |
| packages/react/src/select/store.test.tsx, case "select store synchronization > synchronizes an updated disabled prop after descendant ref callbacks" | replaced | Vue writes the props of the root to the store before the items render, so a click in the ref callback of that render does not select the item. The case "gives a new disabled prop of the root to the items in the render that changes it" asserts this. | Test: packages/vue/src/select/store.test.tsx |
| docs/src/app/(docs)/react/components/select/types.md, entry "Root", prop "multiple" | not-applicable | The row gives the default false. The prop has no declared default, because withDefaults refuses a default for a prop of the generic type Multiple & boolean (TS2322). Vue gives an absent Boolean prop the value false, and the JSDoc has @default false for the API reference. |
Additions to the Base UI API: 2.
| Entry | Kind | Name | Reason |
|---|---|---|---|
| Trigger | prop | id | The part reads id to give the trigger its own id, which the labels of the select and of the field name, and $attrs is not reactive, so it is a declared prop. Base UI reads it from the native attributes of the trigger, and types.md has no row for a native attribute. |
| GroupLabel | prop | id | The part gives id to the group for aria-labelledby and follows a change of it, so it is a declared prop. Base UI reads it from the native attributes of the group label, and types.md has no row for a native attribute. |
Return types that differ from Base UI: 0.
separator
Matches the Base UI commit 5aacdbb of 2026-11-10.
Queued changes: 0.
Vase UI rows with no upstream row: 2.
| Files | Total | Ported | Replaced | Not applicable | Waits on |
|---|---|---|---|---|---|
| Sources | 3 | 3 | 0 | 0 | 0 |
| Type tests | 0 | 0 | 0 | 0 | 0 |
| Tests | Total | Matched | Replaced | Not applicable | Waits on | Engine Chromium | Engine Firefox | Engine WebKit |
|---|---|---|---|---|---|---|---|---|
| Cases | 2 | 2 | 0 | 0 | 0 | 0 | 0 | 0 |
| Suite calls | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 |
| API | Total | Matched | Excepted |
|---|---|---|---|
| Entries | 2 | 2 | 0 |
| Prop rows | 4 | 4 | 0 |
Exception rows: 0.
Additions to the Base UI API: 0.
Return types that differ from Base UI: 0.
slider
Matches the Base UI commit 5aacdbb of 2026-11-10.
Queued changes: 0.
Vase UI rows with no upstream row: 34.
| Files | Total | Ported | Replaced | Not applicable | Waits on |
|---|---|---|---|---|---|
| Sources | 27 | 26 | 0 | 1 | 0 |
| Type tests | 1 | 1 | 0 | 0 | 0 |
| Tests | Total | Matched | Replaced | Not applicable | Waits on | Engine Chromium | Engine Firefox | Engine WebKit |
|---|---|---|---|---|---|---|---|---|
| Cases | 237 | 235 | 2 | 0 | 0 | 0 | 0 | 0 |
| Suite calls | 7 | 7 | 0 | 0 | 0 | 0 | 0 | 0 |
| API | Total | Matched | Excepted |
|---|---|---|---|
| Entries | 13 | 13 | 0 |
| Prop rows | 49 | 47 | 2 |
Exception rows: 5.
| Thing | Kind | Reason | Detail |
|---|---|---|---|
| packages/react/src/slider/thumb/prehydrationScript.template.js | not-applicable | Base UI turns this file into prehydrationScript.min.ts with a minifier. No component imports the template. Vase UI has no minifier. | |
| packages/react/src/slider/root/SliderRoot.test.tsx, case "<Slider.Root /> > server-side rendering > does not link Slider.Label before hydration" | replaced | A server render with no hydrate() runs in Node with no DOM. The case asserts that the server HTML has no aria-labelledby on the root and on the input of the thumb. Vase UI has both attributes on the server. The root assumes its label until it is mounted. The thumb defers its render. Its input then has the id of the label, with the label before or after the control. The case "VUE-29: links the label to the root and to the input of each thumb, with the label $name" asserts the server HTML. | Test: packages/vue/src/slider/root/SliderRoot.node.test.tsx |
| packages/react/src/slider/root/SliderRoot.test.tsx, case "<Slider.Root /> > prop: disabled > explicitly blurs the focused thumb when disabled" | replaced | Base UI runs the case only in jsdom, and it asserts a call of blur() with a spy. Firefox moves the focus off a disabled input itself, so the root has nothing to blur there. The case "does not keep the focus on a thumb that becomes disabled" asserts the result in each engine: the thumb does not keep the focus. | Test: packages/vue/src/slider/root/SliderRoot.test.tsx |
| docs/src/app/(docs)/react/components/slider/types.md, entry "Thumb", prop "aria-valuetext" | not-applicable | The part declares this prop as ariaValuetext, because Vue gives the attribute aria-valuetext to that prop. The prop has a row in surfaceAdditions. | |
| docs/src/app/(docs)/react/components/slider/types.md, entry "Thumb", prop "tabIndex" | not-applicable | The part declares this prop as tabindex, the name of the attribute in the types of Vue. The prop has a row in surfaceAdditions. |
Additions to the Base UI API: 7.
| Entry | Kind | Name | Reason |
|---|---|---|---|
| Root | prop | id | The part reads id to make the id of Slider.Label, and $attrs is not reactive, so it is a declared prop. Base UI reads it from the native attributes of the root, and types.md has no row for a native attribute. |
| Root | prop | ariaLabelledby | The part reads aria-labelledby to name itself and the thumbs, and $attrs is not reactive, so it is a declared prop. Base UI reads it from the native attributes of the root, and types.md has no row for a native attribute. |
| Thumb | prop | ariaLabel | The part gives aria-label to its input and not to its element, and $attrs is not reactive, so it is a declared prop. Base UI reads it from the native attributes of the thumb, and types.md has no row for a native attribute. |
| Thumb | prop | ariaLabelledby | The part gives aria-labelledby to its input and not to its element, and $attrs is not reactive, so it is a declared prop. Base UI reads it from the native attributes of the thumb, and types.md has no row for a native attribute. |
| Thumb | prop | ariaDescribedby | The part gives aria-describedby to its input and not to its element, and $attrs is not reactive, so it is a declared prop. Base UI reads it from the native attributes of the thumb, and types.md has no row for a native attribute. |
| Thumb | prop | ariaValuetext | The prop is aria-valuetext of Base UI. Vue gives the attribute aria-valuetext to the prop ariaValuetext. |
| Thumb | prop | tabindex | The prop is tabIndex of Base UI. A key in the types of Vue has the name of the attribute, tabindex. |
Return types that differ from Base UI: 0.
switch
Matches the Base UI commit 5aacdbb of 2026-11-10.
Queued changes: 0.
Vase UI rows with no upstream row: 26.
| Files | Total | Ported | Replaced | Not applicable | Waits on |
|---|---|---|---|---|---|
| Sources | 8 | 8 | 0 | 0 | 0 |
| Type tests | 0 | 0 | 0 | 0 | 0 |
| Tests | Total | Matched | Replaced | Not applicable | Waits on | Engine Chromium | Engine Firefox | Engine WebKit |
|---|---|---|---|---|---|---|---|---|
| Cases | 69 | 67 | 1 | 1 | 0 | 0 | 0 | 0 |
| Suite calls | 3 | 3 | 0 | 0 | 0 | 0 | 0 | 0 |
| API | Total | Matched | Excepted |
|---|---|---|---|
| Entries | 4 | 4 | 0 |
| Prop rows | 19 | 19 | 0 |
Exception rows: 2.
| Thing | Kind | Reason | Detail |
|---|---|---|---|
| packages/react/src/switch/root/SwitchRoot.test.tsx, case "<Switch.Root /> > extra props > prefers a render element's `aria-label` over an associated label" | not-applicable | The case gives the aria-label as a prop of a React element in render. Base UI reads that prop with React.isValidElement. In Vue, an element with props of its own is the #render slot, and a part does not see an attribute that the slot adds. Base UI also does not see an attribute that a render function adds. | |
| packages/react/src/switch/root/SwitchRoot.test.tsx, case "<Switch.Root /> > Form > preserves Field validation props through canceled changes, submit, and reset" | replaced | The last lines of the case say that a native reset does not change the switch. Vase UI resets an uncontrolled switch to defaultChecked (BUG-6206). The case "preserves Field validation props through canceled changes, submit, and a native reset" has the other assertions of the upstream case and the reset of Vase UI. The block "BUG-6206: a native reset" tests the reset of a switch in a form. | Test: packages/vue/src/switch/root/SwitchRoot.test.tsx |
Additions to the Base UI API: 2.
| Entry | Kind | Name | Reason |
|---|---|---|---|
| Root | prop | ariaLabel | The part reads aria-label to find out if a label names the switch, and $attrs is not reactive, so it is a declared prop. Base UI reads it from the native attributes of the element, and types.md has no row for a native attribute. |
| Root | prop | ariaLabelledby | The part reads aria-labelledby to find out if a label names the switch, and $attrs is not reactive, so it is a declared prop. Base UI reads it from the native attributes of the element, and types.md has no row for a native attribute. |
Return types that differ from Base UI: 0.
tabs
Matches the Base UI commit 5aacdbb of 2026-11-10.
Queued changes: 0.
Vase UI rows with no upstream row: 32.
| Files | Total | Ported | Replaced | Not applicable | Waits on |
|---|---|---|---|---|---|
| Sources | 18 | 17 | 0 | 1 | 0 |
| Type tests | 0 | 0 | 0 | 0 | 0 |
| Tests | Total | Matched | Replaced | Not applicable | Waits on | Engine Chromium | Engine Firefox | Engine WebKit |
|---|---|---|---|---|---|---|---|---|
| Cases | 142 | 138 | 3 | 1 | 0 | 0 | 0 | 0 |
| Suite calls | 5 | 5 | 0 | 0 | 0 | 0 | 0 | 0 |
| API | Total | Matched | Excepted |
|---|---|---|---|
| Entries | 11 | 11 | 0 |
| Prop rows | 27 | 26 | 1 |
Exception rows: 6.
| Thing | Kind | Reason | Detail |
|---|---|---|---|
| packages/react/src/tabs/panel/TabsPanel.test.tsx, case "<Tabs.Panel /> > Suspense integration > renders a panel that suspends when opened with the boundary outside the root" | not-applicable | React hides the mounted tree and shows the fallback when a panel suspends after the mount. A mounted Vue component cannot suspend again. | |
| docs/src/app/(docs)/react/components/tabs/types.md, entry "Root", prop "defaultValue" | not-applicable | The part must tell an absent defaultValue from defaultValue={0}. Only the absent prop makes the root emit the initial change and fall back from a disabled tab. A Vue prop with a default cannot tell the two apart, so the prop has no default. The part uses 0 for an absent prop, which is the default that Base UI documents. | |
| packages/react/src/tabs/indicator/prehydrationScript.template.js | not-applicable | Base UI turns this file into prehydrationScript.min.ts with a minifier. No component imports the template. Vase UI has no minifier and copies the minified text. | |
| packages/react/src/tabs/indicator/TabsIndicator.test.tsx, case "<Tabs.Indicator /> > pre-hydration rendering > renders the inline pre-hydration script during server-side rendering" | replaced | A server render with no hydrate() runs in Node with no DOM. The case is in the test file that the Node project runs, with the same title. | Test: packages/vue/src/tabs/indicator/TabsIndicator.node.test.tsx |
| packages/react/src/tabs/indicator/TabsIndicator.test.tsx, case "<Tabs.Indicator /> > pre-hydration rendering > inlines the script contents during server-side rendering" | replaced | A server render with no hydrate() runs in Node with no DOM. The case is in the test file that the Node project runs, with the same title. | Test: packages/vue/src/tabs/indicator/TabsIndicator.node.test.tsx |
| packages/react/src/tabs/indicator/TabsIndicator.test.tsx, case "<Tabs.Indicator /> > pre-hydration rendering > applies the CSP nonce to the pre-hydration script" | replaced | A server render with no hydrate() runs in Node with no DOM. The case is in the test file that the Node project runs, with the same title. | Test: packages/vue/src/tabs/indicator/TabsIndicator.node.test.tsx |
Additions to the Base UI API: 2.
| Entry | Kind | Name | Reason |
|---|---|---|---|
| Tab | prop | id | The part reads id to give it to the root for the aria-labelledby of the panel, so it is a declared prop. Base UI reads it from the native attributes of the tab, and types.md has no row for a native attribute. |
| Panel | prop | id | The part reads id to give it to the root for the aria-controls of the tab, so it is a declared prop. types.md has no row for a native attribute. Base UI gives the root a generated id, and an id of the consumer replaces only the id of the element. |
Return types that differ from Base UI: 0.
toast
Matches the Base UI commit 5aacdbb of 2026-11-10.
Queued changes: 0.
Vase UI rows with no upstream row: 44.
| Files | Total | Ported | Replaced | Not applicable | Waits on |
|---|---|---|---|---|---|
| Sources | 36 | 36 | 0 | 0 | 0 |
| Type tests | 2 | 2 | 0 | 0 | 0 |
| Tests | Total | Matched | Replaced | Not applicable | Waits on | Engine Chromium | Engine Firefox | Engine WebKit |
|---|---|---|---|---|---|---|---|---|
| Cases | 224 | 218 | 1 | 5 | 0 | 0 | 0 | 0 |
| Suite calls | 10 | 10 | 0 | 0 | 0 | 0 | 0 | 0 |
| API | Total | Matched | Excepted |
|---|---|---|---|
| Entries | 25 | 25 | 0 |
| Prop rows | 52 | 42 | 10 |
Exception rows: 16.
| Thing | Kind | Reason | Detail |
|---|---|---|---|
| packages/react/src/toast/action/ToastAction.test.tsx, case "<Toast.Action /> > renders content passed through the render prop" | not-applicable | The case gives its content in the React element of render, and the part reads the children of that element. A Vue part cannot read what its #render slot renders. It renders only while it has a default slot or the content of its toast. | |
| packages/react/src/toast/provider/ToastProvider.test.tsx, case "<Toast.Provider /> > does not sync provider props from an abandoned render" | not-applicable | The case suspends a React transition, and React then abandons a render. Vue has no render that it abandons. | |
| packages/react/src/toast/utils/isRenderableNode.test.ts, case "hasRenderableChildren > requires an element whose children are renderable" | replaced | The case builds React elements. A Vue part cannot read the children of a vnode, so Vase UI has no hasRenderableChildren. The cases of the label parts test the rule that replaces it: a part renders only while it has a default slot or a label with content. | Test: packages/vue/src/toast/utils/useToastLabelPart.test.tsx |
| packages/react/src/toast/utils/useToastLabelPart.test.tsx, case "useToastLabelPart > <$name /> > prop: render > renders content passed through the render prop" | not-applicable | The case gives its content in the React element of render, and the part reads the children of that element. A Vue part cannot read what its #render slot renders. It renders only while it has a default slot or the content of its toast. | |
| packages/react/src/toast/utils/useToastLabelPart.test.tsx, case "useToastLabelPart > <$name /> > prop: render > renders content passed through a render function" | not-applicable | The case gives its content in the React element of render, and the part reads the children of that element. A Vue part cannot read what its #render slot renders. It renders only while it has a default slot or the content of its toast. | |
| packages/react/src/toast/utils/useToastLabelPart.test.tsx, case "useToastLabelPart > <$name /> > prop: render > `wires ${attribute} to a part rendered through the render prop`" | not-applicable | The case gives its content in the React element of render, and the part reads the children of that element. A Vue part cannot read what its #render slot renders. It renders only while it has a default slot or the content of its toast. | |
| docs/src/app/(docs)/react/components/toast/types.md, entry "Positioner", prop "disableAnchorTracking" | not-applicable | The row gives the default false. The part has no Vue default: a prop that is absent takes disableAnchorTracking of positionerProps of the toast, and false when the toast has none, as in Base UI. With a Vue default, the part cannot find out that the prop is absent. | |
| docs/src/app/(docs)/react/components/toast/types.md, entry "Positioner", prop "align" | not-applicable | The row gives the default 'center'. The part has no Vue default: a prop that is absent takes align of positionerProps of the toast, and 'center' when the toast has none, as in Base UI. With a Vue default, the part cannot find out that the prop is absent. | |
| docs/src/app/(docs)/react/components/toast/types.md, entry "Positioner", prop "alignOffset" | not-applicable | The row gives the default 0. The part has no Vue default: a prop that is absent takes alignOffset of positionerProps of the toast, and 0 when the toast has none, as in Base UI. With a Vue default, the part cannot find out that the prop is absent. | |
| docs/src/app/(docs)/react/components/toast/types.md, entry "Positioner", prop "side" | not-applicable | The row gives the default 'top'. The part has no Vue default: a prop that is absent takes side of positionerProps of the toast, and 'top' when the toast has none, as in Base UI. With a Vue default, the part cannot find out that the prop is absent. | |
| docs/src/app/(docs)/react/components/toast/types.md, entry "Positioner", prop "sideOffset" | not-applicable | The row gives the default 0. The part has no Vue default: a prop that is absent takes sideOffset of positionerProps of the toast, and 0 when the toast has none, as in Base UI. With a Vue default, the part cannot find out that the prop is absent. | |
| docs/src/app/(docs)/react/components/toast/types.md, entry "Positioner", prop "arrowPadding" | not-applicable | The row gives the default 5. The part has no Vue default: a prop that is absent takes arrowPadding of positionerProps of the toast, and 5 when the toast has none, as in Base UI. With a Vue default, the part cannot find out that the prop is absent. | |
| docs/src/app/(docs)/react/components/toast/types.md, entry "Positioner", prop "collisionBoundary" | not-applicable | The row gives the default 'clipping-ancestors'. The part has no Vue default: a prop that is absent takes collisionBoundary of positionerProps of the toast, and 'clipping-ancestors' when the toast has none, as in Base UI. With a Vue default, the part cannot find out that the prop is absent. | |
| docs/src/app/(docs)/react/components/toast/types.md, entry "Positioner", prop "collisionPadding" | not-applicable | The row gives the default 5. The part has no Vue default: a prop that is absent takes collisionPadding of positionerProps of the toast, and 5 when the toast has none, as in Base UI. With a Vue default, the part cannot find out that the prop is absent. | |
| docs/src/app/(docs)/react/components/toast/types.md, entry "Positioner", prop "sticky" | not-applicable | The row gives the default false. The part has no Vue default: a prop that is absent takes sticky of positionerProps of the toast, and false when the toast has none, as in Base UI. With a Vue default, the part cannot find out that the prop is absent. | |
| docs/src/app/(docs)/react/components/toast/types.md, entry "Positioner", prop "positionMethod" | not-applicable | The row gives the default 'absolute'. The part has no Vue default: a prop that is absent takes positionMethod of positionerProps of the toast, and 'absolute' when the toast has none, as in Base UI. With a Vue default, the part cannot find out that the prop is absent. |
Additions to the Base UI API: 4.
| Entry | Kind | Name | Reason |
|---|---|---|---|
| Title | prop | id | The part gives id to the root for aria-labelledby and follows a change of it, so it is a declared prop. Base UI reads it from the native attributes of the title, and types.md has no row for a native attribute. |
| Description | prop | id | The part gives id to the root for aria-describedby and follows a change of it, so it is a declared prop. Base UI reads it from the native attributes of the description, and types.md has no row for a native attribute. |
| Close | prop | disabled | The part reads disabled for useButton, so it is a declared prop. Base UI reads it from the native attributes of the button, and types.md has no row for a native attribute. |
| Action | prop | disabled | The part reads disabled for useButton, so it is a declared prop. Base UI reads it from the native attributes of the button, and types.md has no row for a native attribute. |
Return types that differ from Base UI: 2.
| Entry | Vase UI returns | Reason |
|---|---|---|
| createToastManager | ToastManager<Data> | The source of Base UI returns the same type, ToastManager<Data>. Its types.md prints the type argument as {}. |
| useToastManager | UseToastManagerReturnValue<Data> | setup runs one time, so a plain array does not follow a change of the toasts. toasts of the result is thus a ComputedRef of the array. The source of Base UI returns UseToastManagerReturnValue<Data>, and its types.md prints the type argument as {}. |
toggle
Matches the Base UI commit 5aacdbb of 2026-11-10.
Queued changes: 0.
Vase UI rows with no upstream row: 8.
| Files | Total | Ported | Replaced | Not applicable | Waits on |
|---|---|---|---|---|---|
| Sources | 3 | 3 | 0 | 0 | 0 |
| Type tests | 0 | 0 | 0 | 0 | 0 |
| Tests | Total | Matched | Replaced | Not applicable | Waits on | Engine Chromium | Engine Firefox | Engine WebKit |
|---|---|---|---|---|---|---|---|---|
| Cases | 7 | 7 | 0 | 0 | 0 | 0 | 0 | 0 |
| Suite calls | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 |
| API | Total | Matched | Excepted |
|---|---|---|---|
| Entries | 2 | 2 | 0 |
| Prop rows | 9 | 9 | 0 |
Exception rows: 0.
Additions to the Base UI API: 0.
Return types that differ from Base UI: 0.
toggle-group
Matches the Base UI commit 5aacdbb of 2026-11-10.
Queued changes: 0.
Vase UI rows with no upstream row: 7.
| Files | Total | Ported | Replaced | Not applicable | Waits on |
|---|---|---|---|---|---|
| Sources | 4 | 4 | 0 | 0 | 0 |
| Type tests | 1 | 1 | 0 | 0 | 0 |
| Tests | Total | Matched | Replaced | Not applicable | Waits on | Engine Chromium | Engine Firefox | Engine WebKit |
|---|---|---|---|---|---|---|---|---|
| Cases | 23 | 23 | 0 | 0 | 0 | 0 | 0 | 0 |
| Suite calls | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 |
| API | Total | Matched | Excepted |
|---|---|---|---|
| Entries | 2 | 2 | 0 |
| Prop rows | 10 | 10 | 0 |
Exception rows: 0.
Additions to the Base UI API: 0.
Return types that differ from Base UI: 0.
toolbar
Matches the Base UI commit 5aacdbb of 2026-11-10.
Queued changes: 0.
Vase UI rows with no upstream row: 22.
| Files | Total | Ported | Replaced | Not applicable | Waits on |
|---|---|---|---|---|---|
| Sources | 16 | 16 | 0 | 0 | 0 |
| Type tests | 1 | 1 | 0 | 0 | 0 |
| Tests | Total | Matched | Replaced | Not applicable | Waits on | Engine Chromium | Engine Firefox | Engine WebKit |
|---|---|---|---|---|---|---|---|---|
| Cases | 57 | 57 | 0 | 0 | 0 | 0 | 0 | 0 |
| Suite calls | 6 | 6 | 0 | 0 | 0 | 0 | 0 | 0 |
| API | Total | Matched | Excepted |
|---|---|---|---|
| Entries | 12 | 12 | 0 |
| Prop rows | 29 | 29 | 0 |
Exception rows: 0.
Additions to the Base UI API: 0.
Return types that differ from Base UI: 0.
tooltip
Matches the Base UI commit 5aacdbb of 2026-11-10.
Queued changes: 0.
Vase UI rows with no upstream row: 36.
| Files | Total | Ported | Replaced | Not applicable | Waits on |
|---|---|---|---|---|---|
| Sources | 24 | 24 | 0 | 0 | 0 |
| Type tests | 2 | 2 | 0 | 0 | 0 |
| Tests | Total | Matched | Replaced | Not applicable | Waits on | Engine Chromium | Engine Firefox | Engine WebKit |
|---|---|---|---|---|---|---|---|---|
| Cases | 121 | 120 | 1 | 0 | 0 | 0 | 0 | 0 |
| Suite calls | 10 | 10 | 0 | 0 | 0 | 0 | 0 | 0 |
| API | Total | Matched | Excepted |
|---|---|---|---|
| Entries | 17 | 17 | 0 |
| Prop rows | 55 | 53 | 2 |
Exception rows: 3.
| Thing | Kind | Reason | Detail |
|---|---|---|---|
| packages/react/src/tooltip/root/TooltipRoot.test.tsx, case "<Tooltip.Root /> > hover changes > commits a hover open while the hover event is dispatched" | replaced | Vue has no flushSync, so the popup renders in the update after the hover event, and a listener of the same event does not find it. The case shows the popup after the awaited hover event asserts the popup after the awaited event. | Test: packages/vue/src/tooltip/root/TooltipRoot.test.tsx |
| docs/src/app/(docs)/react/components/tooltip/types.md, entry "Trigger", prop "delay" | not-applicable | The row gives the default 600. The prop has no Vue default, because Base UI tells an absent prop from a number: an absent prop takes the delay of Tooltip.Provider. With no provider, the part uses 600 for an absent prop, which is the default that Base UI documents. | |
| docs/src/app/(docs)/react/components/tooltip/types.md, entry "Trigger", prop "closeDelay" | not-applicable | The row gives the default 0. The prop has no Vue default, because Base UI tells an absent prop from a number: an absent prop takes the close delay of Tooltip.Provider. With no provider, the part uses 0 for an absent prop, which is the default that Base UI documents. |
Additions to the Base UI API: 1.
| Entry | Kind | Name | Reason |
|---|---|---|---|
| Trigger | prop | id | The part reads id to register the trigger in the store of the tooltip, so it is a declared prop. Base UI reads it from the native attributes of the trigger, and types.md has no row for a native attribute. |
Return types that differ from Base UI: 1.
| Entry | Vase UI returns | Reason |
|---|---|---|
| createHandle | TooltipHandle<Payload> | Tooltip.Handle is the class TooltipHandle. The declaration of the function names the class, and Base UI documents the same class by its name in the namespace. |
tree
Matches the Base UI commit 5aacdbb of 2026-11-10.
Queued changes: 0.
Vase UI rows with no upstream row: 0.
| Files | Total | Ported | Replaced | Not applicable | Waits on |
|---|---|---|---|---|---|
| Sources | 3 | 3 | 0 | 0 | 0 |
| Type tests | 0 | 0 | 0 | 0 | 0 |
| Tests | Total | Matched | Replaced | Not applicable | Waits on | Engine Chromium | Engine Firefox | Engine WebKit |
|---|---|---|---|---|---|---|---|---|
| Cases | 6 | 6 | 0 | 0 | 0 | 0 | 0 | 0 |
| Suite calls | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
Exception rows: 0.
Additions to the Base UI API: 0.
Return types that differ from Base UI: 0.
unstable-use-media-query
Matches the Base UI commit 5aacdbb of 2026-11-10.
Queued changes: 0.
Vase UI rows with no upstream row: 4.
| Files | Total | Ported | Replaced | Not applicable | Waits on |
|---|---|---|---|---|---|
| Sources | 1 | 1 | 0 | 0 | 0 |
| Type tests | 0 | 0 | 0 | 0 | 0 |
| Tests | Total | Matched | Replaced | Not applicable | Waits on | Engine Chromium | Engine Firefox | Engine WebKit |
|---|---|---|---|---|---|---|---|---|
| Cases | 4 | 2 | 2 | 0 | 0 | 0 | 0 | 0 |
| Suite calls | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
Exception rows: 2.
| Thing | Kind | Reason | Detail |
|---|---|---|---|
| packages/react/src/unstable-use-media-query/index.test.tsx, case "useMediaQuery > uses ssrMatchMedia for the server snapshot" | replaced | A server render runs in Node with no DOM. The case is in the test file that the Node project runs, with the same title. | Test: packages/vue/src/unstable-use-media-query/index.node.test.tsx |
| packages/react/src/unstable-use-media-query/index.test.tsx, case "useMediaQuery > uses the client matchMedia result as the server snapshot when noSsr is set" | replaced | A server render runs in Node with no DOM. The case is in the test file that the Node project runs, with the same title. | Test: packages/vue/src/unstable-use-media-query/index.node.test.tsx |
Additions to the Base UI API: 0.
Return types that differ from Base UI: 0.
use-render
Matches the Base UI commit 5aacdbb of 2026-11-10.
Queued changes: 0.
Vase UI rows with no upstream row: 4.
| Files | Total | Ported | Replaced | Not applicable | Waits on |
|---|---|---|---|---|---|
| Sources | 2 | 2 | 0 | 0 | 0 |
| Type tests | 1 | 1 | 0 | 0 | 0 |
| Tests | Total | Matched | Replaced | Not applicable | Waits on | Engine Chromium | Engine Firefox | Engine WebKit |
|---|---|---|---|---|---|---|---|---|
| Cases | 14 | 14 | 0 | 0 | 0 | 0 | 0 | 0 |
| Suite calls | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| API | Total | Matched | Excepted |
|---|---|---|---|
| Entries | 2 | 2 | 0 |
| Prop rows | 0 | 0 | 0 |
Exception rows: 0.
Additions to the Base UI API: 0.
Return types that differ from Base UI: 1.
| Entry | Vase UI returns | Reason |
|---|---|---|
| useRender | useRender.ReturnValue<RenderedElementType> | A composable cannot return the rendered element. It returns element, elementProps and tag. The template of the component renders the element. |
utils
Matches the Base UI commit 5aacdbb of 2026-11-10.
Queued changes: 0.
Vase UI rows with no upstream row: 36.
| Files | Total | Ported | Replaced | Not applicable | Waits on |
|---|---|---|---|---|---|
| Sources | 30 | 28 | 0 | 2 | 0 |
| Type tests | 0 | 0 | 0 | 0 | 0 |
| Tests | Total | Matched | Replaced | Not applicable | Waits on | Engine Chromium | Engine Firefox | Engine WebKit |
|---|---|---|---|---|---|---|---|---|
| Cases | 49 | 46 | 1 | 2 | 0 | 0 | 0 | 0 |
| Suite calls | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 |
Exception rows: 5.
| Thing | Kind | Reason | Detail |
|---|---|---|---|
| packages/react/src/utils/getPseudoElementBounds.test.ts, case "getPseudoElementBounds > does not read pseudo-element styles in jsdom" | not-applicable | The case tests the branch for jsdom, which has no getComputedStyle for a pseudo-element. Vase UI runs no test in jsdom, so the function has no such branch. | |
| packages/react/src/utils/resolveRef.ts | not-applicable | The file reads the current of a React ref object. Vue toValue reads a ref, a getter or a value. | |
| packages/react/src/utils/resolveStyle.ts | not-applicable | The file resolves the React style prop, which is an object or a function of the state. In Vase UI the style attribute and the stateStyle prop do this, and useRenderElement merges them. No Vase UI code needs the function. | |
| packages/react/src/utils/useFormatLocale.test.tsx, case "useFormatLocale > returns the runtime locale on the first client-only render" | not-applicable | React tells a hydration from a client-only mount with the server snapshot of useSyncExternalStore. Vue has no public API that tells them apart in setup, so the first render of a client-only mount also gets en-US. The case "returns en-US for the first render of a client-only mount, then the runtime locale" states what Vase UI does. | |
| packages/react/src/utils/useIsHydrating.test.tsx, case "useIsHydrating > returns true before hydration for server-rendered markup" | replaced | A server render runs in Node with no DOM. The case is in the test file that the Node project runs, with the same title. | Test: packages/vue/src/utils/useIsHydrating.node.test.tsx |
Additions to the Base UI API: 0.
Return types that differ from Base UI: 0.
viewport
Matches the Base UI commit 5aacdbb of 2026-11-10.
Queued changes: 0.
Vase UI rows with no upstream row: 0.
| Files | Total | Ported | Replaced | Not applicable | Waits on |
|---|---|---|---|---|---|
| Sources | 5 | 5 | 0 | 0 | 0 |
| Type tests | 0 | 0 | 0 | 0 | 0 |
| Tests | Total | Matched | Replaced | Not applicable | Waits on | Engine Chromium | Engine Firefox | Engine WebKit |
|---|---|---|---|---|---|---|---|---|
| Cases | 13 | 13 | 0 | 0 | 0 | 0 | 0 | 0 |
| Suite calls | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
Exception rows: 0.
Additions to the Base UI API: 0.
Return types that differ from Base UI: 0.
Base UI changes
Each change has a state for each family that it touches:
mirrored: the matched commit of the family is this commit or a later one.queued: the matched commit of the family is older than this commit.not applicable: the change does not apply to Vue. The table shows the reason.
A change that touches no family has one of two other states:
not started: the change is in a part of Base UI that Vase UI has not ported yet.out of scope: Vase UI does not port the changed files. The table shows the reason when a rule names the files.
| Date | Change | Commit | State |
|---|---|---|---|
| 2026-11-10 | [docs][popover] Add close button to demos (#6095) | 5aacdbb |
|
Edge cases
Where Vue or the browser lets us handle an edge case better than the original, we do, and we list each one with its test.
| ID | Behavior | Evidence | Due with | Tests |
|---|---|---|---|---|
| BUG-1425 | When validationMode is onBlur, a required Radio Group with no value is invalid after the focus leaves it. The group or its field needs a name. In that mode, a required Select with no value is invalid after the focus leaves its trigger, and after an outside press closes its popup. A required Combobox with a selection mode and no value, whose trigger is the control, is invalid after the focus leaves its trigger, and after an outside press or a focus that leaves closes its popup. A Combobox whose input is outside the popup keeps the rule of a text field. Base UI shows no error until the form is submitted. | packages/react/src/radio-group/RadioGroup.tsx |
| |
| BUG-4678 | A Dialog, an Alert Dialog or a Drawer with modal set to true sets inert on the nodes outside the popup. Tab cannot reach those nodes, and a press cannot reach an element that is drawn above the backdrop. Live regions, scripts, nested portal nodes and focus guards do not get inert, as in Base UI. modal="trap-focus" and all other popups keep the aria-hidden marks of Base UI. | packages/react/src/utils/popups/focus/markOthers.ts |
| |
| BUG-4854 | A long press with a stylus opens the Context Menu. | packages/react/src/context-menu/trigger/ContextMenuTrigger.tsx |
| |
| BUG-5237 | In WebKit on an Apple system, the focus guard is a button with a name, so axe finds no violation. | packages/react/src/utils/popups/focus/FocusGuard.tsx |
| |
| BUG-5410 | A press in a same-origin iframe closes a popup that is not modal. | packages/react/src/utils/popups/interactions/useDismiss.ts |
| |
| BUG-5424 | A Number Field submits the number that it shows. Text with two dots or more is a number only when each dot before the last one can be a group separator, as in 1.234.567.89. A run of dots counts as one dot, as in Base UI: 1..5 in en-US and 1,,5 in de-DE are 1.5. Other text, such as 1.2.3, does not change the value. The rule also applies to the decimal separator of the locale: 1,2,3 in de-DE does not change the value. Base UI reads 1.2.3 as 12.3, so the field shows 1.2.3 and submits 12.3. | packages/react/src/number-field/utils/parse.ts |
| |
| BUG-5519 | During an exit animation, no focusable content is under aria-hidden. Combobox.Popup stops its focus manager when the popup closes, so its focus guards, which have aria-hidden, go at the close, and the focus returns at the close. Base UI stops the focus manager when the popup unmounts. | packages/react/src/combobox/popup/ComboboxPopup.tsx |
| |
| BUG-5574 | A pick with the mouse during an IME composition leaves no stale text in the input. | packages/react/src/combobox/item/ComboboxItem.tsx |
| |
| BUG-5577 | A popup that opens in a container that the marks of an earlier popup cover lifts aria-hidden, inert and data-vase-ui-inert from its own elements and their ancestors while it is open. The other children of those ancestors keep the marks, and the release of the later popup puts the marks back. | packages/react/src/utils/popups/focus/markOthers.ts |
| |
| BUG-5776 | Typeahead on a closed trigger does not select a mounted item that is disabled. The trigger reads the disabled state from the element of the item, so a label that comes from items while the list is not mounted still matches. | packages/react/src/combobox/trigger/ComboboxTrigger.tsx |
| |
| BUG-6037 | When a popup closes, focus goes to the first of these that can take it. The order is finalFocus, the trigger, the element that had focus before the popup opened, the parent popup (its first tabbable element, or the popup itself when it has none), and the newest element that had focus before an earlier open. When no click on the trigger opened the popup, the element that had focus before the popup opened comes before the trigger. An element that is not in the document, or that has the hidden attribute or an ancestor with it, cannot take focus. After a press on the backdrop in a browser without preventScroll, an explicit finalFocus still gets focus. | packages/react/src/utils/popups/focus/FloatingFocusManager.tsx |
| |
| BUG-6119 | The scroll lock probes the scrollbar gutter one time for each document. A later lock makes no layout probe, so it cannot stop a transition that runs. | packages/utils/src/useScrollLock.ts |
| |
| BUG-6158 | Escape closes the popup when the key comes directly after compositionend. | packages/react/src/utils/popups/interactions/useDismiss.ts |
| |
| BUG-6174 | The timers of a toast start again when focus goes into an iframe on the page. | packages/react/src/toast/viewport/ToastViewport.tsx |
| |
| BUG-6206 | A native form reset puts each uncontrolled control back to its default value and removes data-filled. | packages/react/src/checkbox/root/CheckboxRoot.tsx |
| |
| VUE-1 | A controlled Boolean prop that the consumer does not give stays undefined, and the part is uncontrolled. The same is true for an undefined binding, for kebab case and camel case, and for a Boolean prop with a generic type. :open="false", v-model:open and a bare attribute make the part controlled. | packages/utils/src/useControlled.ts |
| |
| VUE-2 | The server HTML has the label ids, the aria-* attributes and the roving tabindex. The DOM after hydration is equal to the DOM of a render with no server HTML. | packages/react/src/radio-group/RadioGroup.tsx |
| |
| VUE-3 | With Vue 3.5.41 or later on the server, the server HTML of a closed panel with hiddenUntilFound has hidden="until-found". In Vue 3.6, the server writes it from 3.6.0-rc.3. A server with an earlier Vue writes a bare hidden, and the part writes hidden="until-found" to the element at mount. | packages/react/src/collapsible/panel/CollapsiblePanel.tsx |
| |
| VUE-4 | A press in a child popup that Teleport moved does not close the parent popup. This includes a press on a part in its portal, such as Dialog.Backdrop. Escape closes the top popup first. | packages/react/src/utils/popups/interactions/useDismiss.ts |
| |
| VUE-5 | When KeepAlive deactivates a popup, the popup keeps its open state. It releases the scroll lock, the modal marks, the document listeners and the timers. The portal element of the popup leaves the document. When KeepAlive activates the popup again, the popup takes them back, and the same portal element returns. | packages/react/src/utils/popups/portal/FloatingPortal.tsx |
| |
| VUE-6 | Two copies of the library on one page share one scroll-lock count, one set of modal marks and one Escape stack. | packages/utils/src/useScrollLock.ts |
| |
| VUE-7 | When hydration ends and the pointer is over a trigger that opens on hover, the popup opens. | packages/react/src/navigation-menu/trigger/NavigationMenuTrigger.tsx |
| |
| VUE-8 | Grid navigation gives an item that is wider than the grid the width of the grid, in each build. A development build also reports the item. It uses the whole part of a number of columns. It uses 1 for a number of columns that is below 1 or not a finite number, and for an item width or an item height that is not a finite number. For these values, no build stays in a loop that does not end. | packages/react/src/internals/composite/listIndex.ts |
| |
| VUE-9 | Between the server HTML and the mount of its last item, a composite does not have two tab stops. While the server HTML has the tab stop on an item that is not mounted, the mounted items do not take it. | packages/react/src/internals/composite/item/useCompositeItem.ts |
| |
| VUE-10 | The check of an event target reads the disabled state of the innermost trigger that contains the target, in each order of registration. Base UI reads the first trigger of the map that contains the target. An outer trigger that registered first then decides for a target in an inner trigger that registered later. | packages/react/src/utils/popups/element.ts |
| |
| VUE-11 | When the active control of a field goes away, the field uses the control that was active before it and is still there. The same occurs when the active control is disabled and when KeepAlive deactivates it. The field has no control only when none is left. | packages/react/src/internals/field-register-control/useFieldControlRegistration.ts |
| |
| VUE-12 | When two panels of one collapsible are mounted, the trigger names the panel that registered last in aria-controls. When that panel unmounts, the trigger names the panel that stays. Base UI gives the trigger no aria-controls then, because the panel that stays does not register again. | packages/react/src/collapsible/panel/CollapsiblePanel.tsx |
| |
| VUE-13 | Form follows a change that the consumer makes in place to a reactive errors object, such as a new key. Base UI sees only a new object. Form watches the prop deeply and keeps a new plain copy for each change. | packages/react/src/form/Form.tsx |
| |
| VUE-14 | A tab names the id of its panel in aria-controls, also when the panel has an id of its own. Base UI registers the generated id of the panel, so aria-controls names no element when the consumer gives the panel an id. | packages/react/src/tabs/panel/TabsPanel.tsx |
| |
| VUE-15 | When two triggers of one item are mounted, the panel names the trigger that registered last in aria-labelledby. When that trigger unmounts, the panel names the trigger that stays. Base UI gives the panel no aria-labelledby then, because the trigger that stays does not register again. | packages/react/src/accordion/panel/AccordionPanel.tsx |
| |
| VUE-16 | When the id of a control changes, the control gets the aria-labelledby of the native <label> that names the new id, in each browser. Base UI reads labels of the hidden input, and WebKit keeps that list as it was before the change. | packages/react/src/internals/labelable-provider/useAriaLabelledBy.ts |
| |
| VUE-17 | The server HTML of a control in a field has the id of the label in aria-labelledby and the ids of the messages in aria-describedby, in each order of the parts. A control with no label has no aria-labelledby. Base UI adds these attributes after the page hydrates. |
| packages/react/src/internals/labelable-provider/LabelableProvider.tsx |
|
| VUE-18 | A panel with an empty id has the id that its item generates. The open trigger names that id in aria-controls. Base UI gives the panel id="" then, so aria-controls names no element. | packages/react/src/accordion/panel/AccordionPanel.tsx |
| |
| VUE-19 | tabbable() leaves out a details, audio[controls], video[controls] or contenteditable element that has tabindex="-1", because the Tab key skips it. Base UI lists it. A tabindex value that is not an integer counts as no attribute. | packages/react/src/utils/popups/focus/tabbable.ts |
| |
| VUE-20 | A portal follows a container that is a ref or a getter. When it gives another element, the portal moves its portal element there and does not mount the popup again. When it gives null, the portal uses its fallback container. Base UI reads a ref object one time. | packages/react/src/utils/popups/portal/FloatingPortalLite.tsx |
| |
| VUE-21 | A native form reset removes the error of the field. Field.Control, Switch.Root, Checkbox.Root, CheckboxGroup, NumberField.Root, RadioGroup, OTPField.Root, Select.Root, Slider.Root, Combobox.Root and Autocomplete.Root do this after each reset that they follow, also when their value does not change. Each part cancels the pending validation and removes the message that the field put on the input. Each part also clears the validity of the field and the error of the form for the field. The change of a reset starts no validation. Base UI keeps the error that the field showed before the reset. Its controls do not listen for a reset, and a reset sends no input event. |
| packages/react/src/field/control/FieldControl.tsx |
|
| VUE-22 | Triggers can join an open popup that has no active trigger, and none of them claims it. A mount of triggers that claim nothing does this, and so does an activation by KeepAlive. The popup then takes the first trigger of its map that is in the document. Base UI takes a trigger there only when it is the only one. Base UI has no counterpart of KeepAlive. | packages/react/src/utils/popups/popupRoot.tsx |
| |
| VUE-23 | Each Field.Label has the id of its id prop, or an id of its own, and the id follows the prop. The control names the last label in aria-labelledby. Base UI gives each label the id of the last label that registered. Two labels of one field then have the same id, and a change of the id prop has no effect. |
| packages/react/src/field/label/FieldLabel.tsx |
|
| VUE-24 | A trigger that gets disabled stops its open delay, its rest delay and its safe polygon. Base UI keeps the timer, so the popup opens when the delay ends. The owner of a Tooltip then hears openChange(true) and openChange(false) with the reason disabled, for a popup that never shows. |
| packages/react/src/utils/popups/interactions/useHoverTriggerInteraction.ts |
|
| VUE-25 | The hover intent opens the popup for the innermost trigger that contains the event target, in each order of registration. Base UI takes the first trigger of the map that contains the target. An outer trigger that registered first then gets the popup for a target in an inner trigger that registered later. | packages/react/src/utils/popups/interactions/hoverIntent.ts |
| |
| VUE-26 | When KeepAlive deactivates a trigger, the trigger stops its timers and its safe polygon. A popup that hover opened and that the trigger anchors then closes with the reason trigger-hover. This is the exception to the open state that a popup keeps under KeepAlive. KeepAlive sends no leave event, so the popup would stay open after the activation with no pointer on its trigger. Base UI has no counterpart of KeepAlive. |
| packages/react/src/utils/popups/interactions/useHoverTriggerInteraction.ts |
|
| VUE-27 | A pointerdown whose pointer the browser does not know, such as an event that a script dispatched, still starts and ends a drag. Slider.Control calls setPointerCapture in a try block, because Firefox throws NotFoundError for such a pointer. Base UI has no guard there. The throw then stops the handler before it adds the listeners that end the drag. | packages/react/src/slider/thumb/SliderThumb.tsx |
| |
| VUE-28 | Slider.Control reads the computed style of its element again after the element changes, for example when as changes. Base UI reads the style one time and keeps it. The kept style belongs to an element that left the document, and a browser gives such a style no values. Base UI then reads the border and the padding of the control as 0, and a press on a control that has them gives a wrong value. |
| packages/react/src/slider/thumb/SliderThumb.tsx |
|
| VUE-29 | The server HTML of a range slider has the index, the value and the text of the value of each thumb, also when the thumbs have no index prop. A thumb takes its index from the order in which the thumbs render. Base UI needs the index prop on each thumb for a server render, because its list of thumbs fills only after the mount. The server HTML of a slider that has Slider.Label links the label. The root has the id of the label in aria-labelledby. The input of each thumb that has no name of its own has it too. The label can come before or after the control. Base UI links the label after the page hydrates. |
| packages/react/src/slider/thumb/SliderThumb.tsx |
|
| VUE-30 | close() of a toast store reads no document while the store has no viewport element. A call in a server render, or before the viewport is mounted, thus closes the toast. Base UI reads the global document before it reads the viewport, so the same call throws in Node. | packages/react/src/toast/store.ts |
| |
| VUE-31 | A trigger that gets disabled, or that moves to another store, stops its open delay. Base UI keeps the timer until the trigger unmounts, so the popup opens when the delay ends, also for a trigger that is disabled or that has another store. |
| packages/react/src/utils/popups/interactions/useFocus.ts |
|
| VUE-32 | When KeepAlive deactivates a trigger, the trigger stops its open delay. A popup that focus opened and that the trigger anchors then closes with the reason trigger-focus. This is the exception to the open state that a popup keeps under KeepAlive. The focused trigger leaves the document, and Firefox and WebKit send no focusout for it, so the popup would stay open after the activation with no focus on its trigger. Base UI has no counterpart of KeepAlive. |
| packages/react/src/utils/popups/interactions/useFocus.ts |
|
| VUE-33 | A previousFocusableElement or nextFocusableElement that is a ref or a getter with no element is the same as null. When the Tab key leaves the popup, the focus then goes to the outside guard of the portal, which moves it on. Base UI falls back only when the prop itself is empty. A ref object with no current moves no focus there, and the focus stays on a hidden guard of the popup. | packages/react/src/utils/popups/focus/FloatingFocusManager.tsx |
| |
| VUE-34 | isOpen of a popup handle is reactive. A computed, a watch or a template that reads it follows the open state of the popup, and the root that controls the handle. In Base UI, a component that reads isOpen in its render does not render again when the popup opens or closes. | packages/react/src/tooltip/store/TooltipHandle.ts |
| |
| VUE-35 | CheckboxGroup follows a change that the consumer makes in place to a reactive value array, such as push or splice. The checkboxes, the state of the field and the validation get the new value. Base UI sees only a new array. The part reads a copy of the array, and the copy reads each item. | packages/react/src/checkbox-group/CheckboxGroup.tsx |
| |
| VUE-36 | The label of an item can be a function that returns the content of the label, so one label can show in the value part and in its item at the same time. The text of such an item is the result of itemToStringLabel, or the text of its value. Base UI takes a React element as a label, and the text of that item is [object Object]. | packages/react/src/internals/resolveValueLabel.tsx |
| |
| VUE-37 | The scroll lock reads the scrollbar width from the window of the document that it locks. Base UI gives the document to ownerWindow, which returns the window of the page. For a frame, Base UI then compares the frame with the page. It takes the way for scrollbars that take space also when the frame has overlay scrollbars. | packages/utils/src/useScrollLock.ts |
| |
| VUE-38 | The picture of the old content is the DOM at the time of the trigger change. It has what a component in the content changed by itself. Base UI clones the content after each render of the viewport, so a change that no render of the viewport followed is not in its picture. | packages/react/src/utils/popups/viewport/usePopupViewport.tsx |
| |
| VUE-39 | The content of the new trigger mounts one time for a change of the active trigger, when the trigger gives its payload with the change. Base UI mounts it two times: the key of the content has the id of the trigger, and a layout effect of the same change raises the count in the key. | packages/react/src/utils/popups/viewport/usePopupViewport.tsx |
| |
| VUE-40 | The picture of the old content has no id on an HTML element and no name on a control. Two elements with one id then never exist during a morph, so aria-labelledby of the popup names the live title. A checked radio of the picture does not uncheck the live radio. Base UI keeps both in its picture. | packages/react/src/utils/popups/viewport/usePopupViewport.tsx |
| |
| VUE-41 | Each request of the frame scheduler runs its callback when requestAnimationFrame calls back at once. The scheduler sets its flag before it calls requestAnimationFrame. Base UI sets the flag after the call. tick clears the flag inside the call, and Base UI then sets it again. Each later request finds the flag set and never runs its callback. | packages/utils/src/useAnimationFrame.ts |
| |
| VUE-42 | A Checkbox.Root that has no value and no name, in a CheckboxGroup that has allValues, shows its own state. Base UI shows the checked prop of such a checkbox. After a click, its aria-checked stays false while its input is on. | packages/react/src/checkbox-group/useCheckboxGroupParent.ts |
| |
| VUE-43 | The node of a popup in the popup tree has the positioning context of the popup only while the popup is at that node. The node loses the context when the popup moves to another node or to another tree, when the positioner of the popup unmounts, and while KeepAlive holds the positioner. Base UI leaves the context of the last render on the node. A popup above that node then takes the popup for an open child, also after the popup closed or moved. | packages/react/src/utils/popups/positioning/useFloating.ts |
| |
| VUE-44 | With no locale, the filters of Combobox.useFilter, Autocomplete.useFilter, Combobox.Root and Autocomplete.Root use en-US until their component is mounted, and then the runtime locale. A locale that the developer gives applies from the first render, on the server and in the browser alike. A list that a server filtered then hydrates with no mismatch. With no locale, Base UI uses the runtime locale in each render, so a server and a browser can filter a list in different ways. |
| packages/react/src/combobox/root/utils/useFilter.ts |
|
| VUE-45 | When two titles of one dialog or of one popover are mounted, the popup names the title that registered last in aria-labelledby. When that title unmounts, the popup names the title that stays. Base UI gives the popup no aria-labelledby then, because the title that stays does not write its id again. The descriptions follow the same rule for aria-describedby. |
| packages/react/src/dialog/title/DialogTitle.tsx |
|
| VUE-46 | A key that the list of a child popup does not stop goes to the list of the parent popup one time, also when Teleport moved the child popup. A close key of the child list that is also a navigation key of the parent list then closes the child list and moves the highlight of the parent list. | packages/react/src/utils/popups/interactions/useListNavigation.ts |
| |
| VUE-47 | With restoreFocus, the focus manager moves the focus back into the popup when the focused element in the popup is removed, in each engine. Firefox and WebKit send no focusout for the removal, so a MutationObserver on the popup element also starts the restore. | packages/react/src/utils/popups/focus/FloatingFocusManager.tsx |
| |
| VUE-48 | A popup that unmounts leaves the timer of its delay group as it is. In Base UI, a closed popup that another popup replaced in the group clears the timer of that other popup when it unmounts. The group then stays active until a popup opens and closes again. Until then, each popup of the group opens with no delay and with no animation. | packages/react/src/utils/popups/delay-group/FloatingDelayGroup.tsx |
| |
| VUE-49 | The details of the openChange event of ContextMenu.Root have the type of preventUnmountOnClose(). Base UI types the details of onOpenChange without that function, while the object has it and the text of the unmount action tells the developer to call it. | packages/react/src/context-menu/root/ContextMenuRoot.tsx |
| |
| VUE-50 | A toast swipe reports no error when the browser refuses to capture the pointer. Firefox throws NotFoundError from setPointerCapture when the pointer id is not an active pointer, such as the pointer id of a dispatched event. Vase UI catches the error, and the swipe goes on. In Base UI the error leaves the pointerdown handler. | packages/react/src/toast/root/ToastRoot.tsx |
| |
| VUE-51 | The server HTML of Select.Trigger and Combobox.Trigger has the id of the label part of the family in aria-labelledby, in each order of the two parts. Base UI adds the attribute after the page hydrates. |
| packages/react/src/select/trigger/SelectTrigger.tsx |
|
| VUE-52 | Select.Value follows a change that the consumer makes in place to a reactive items prop of Select.Root, such as a new key. Base UI sees only a new object. The root watches the prop deeply and keeps a new plain copy for each change. | packages/react/src/select/root/SelectRoot.tsx |
| |
| VUE-53 | When KeepAlive deactivates a scrub area during a scrub, the scrub ends with no commit. The root loses data-scrubbing, the cursor goes out of the document and the pointer lock ends. After the activation, a pointer release commits nothing. Under <Activity>, Base UI ends the pointer lock and clears the scrubbing state of the root. The area keeps its own scrubbing state, so it shows its cursor again after the activation, and the next pointer release commits the value. | packages/react/src/number-field/scrub-area/NumberFieldScrubArea.tsx |
| |
| VUE-54 | When the active item of a composite changes while the focus is outside the composite, the tab stop moves to that item. A native reset, a controlled value that the parent changes, and an active item that mounts late are such changes. When such a change leaves no active item, or an active item that is disabled or hidden, the tab stop moves to the first enabled item. When the focus leaves the composite, the tab stop goes back to the active item. While the composite has the focus, the tab stop stays on the item that the user chose. The Tab key thus enters a Radio Group on the checked radio, and a Tabs list with manual activation on the active tab. In Base UI, only Tabs.Tab moves the tab stop, and only to a tab that becomes active. A Radio Group keeps the tab stop on the radio that had the focus last, and a Tabs list keeps it on the tab that had the focus last or that was active last. | packages/react/src/internals/composite/root/useCompositeRoot.ts |
| |
| VUE-55 | A root provides the tree that its popup uses. A detached trigger can give the root another tree. A part in the popup of the root then registers in the new tree, so a menu that a trigger in the popup opens closes with the root. Base UI provides the first tree of the root and never changes it. A part in the popup then registers in a tree that the popup does not read, and the menu that its trigger opens stays open. |
| packages/react/src/utils/popups/tree/FloatingTree.tsx |
|
| VUE-56 | When two titles of one toast are mounted, the toast names the title that registered last in aria-labelledby. When that title unmounts, the toast names the title that stays. Base UI gives the toast no aria-labelledby then, because the title that stays does not write its id again. The descriptions follow the same rule for aria-describedby. | packages/react/src/toast/utils/useToastLabelPart.ts |
| |
| VUE-57 | When Drawer.SwipeArea unmounts during a gesture that opens the drawer, it removes the swipe movement, data-swiping and transition: none from the popup. It also removes the swipe progress from the backdrop and from Drawer.Provider. The drawer is then fully open. In Base UI, the swipe area only lets an outside press close the drawer again. The popup then stays at the position of the gesture, and the indent stays at the progress of the gesture. | packages/react/src/drawer/swipe-area/DrawerSwipeArea.tsx |
| |
| VUE-58 | A listener of the consumer on Drawer.Viewport runs before the swipe listener of the viewport that has the same key, and event.preventVaseUIHandler() in it stops that swipe listener. A consumer can thus stop the start of a swipe, as it can stop the handler of each other part. In Base UI, the listener of the viewport runs first, and a listener of the consumer cannot stop it. | packages/react/src/drawer/viewport/DrawerViewport.tsx |
| |
| VUE-59 | A valueChange listener that calls details.cancel() stops the change of an uncontrolled Autocomplete.Root. The input and Autocomplete.Value keep the old text, and the root emits no update:value. Base UI writes its state before it calls onValueChange, so a canceled change still changes an uncontrolled autocomplete. | packages/react/src/autocomplete/root/AutocompleteRoot.tsx |
| |
| VUE-60 | A trigger that has focus guards beside its element has one root node. NavigationMenu.Viewport in a positioner has one root node too. A rule of <style scoped>, v-show, a custom directive and <Transition> of the consumer thus apply to the element of the part, also while the guards are in the document. |
| packages/react/src/navigation-menu/trigger/NavigationMenuTrigger.tsx |
|
| VUE-61 | Menu.Positioner, Popover.Positioner, Select.Positioner and Combobox.Positioner have one root node, also while their popup is modal: Menu.Portal, Popover.Portal, Select.Portal and Combobox.Portal render the internal backdrop of a modal popup. A rule of <style scoped>, v-show and a custom directive of the consumer thus apply to the element of the positioner. Autocomplete.Portal and Autocomplete.Positioner are the parts of Combobox under other names. In Base UI, the positioner renders the backdrop beside its element. |
| packages/react/src/menu/positioner/MenuPositioner.tsx |
|
| VUE-62 | While Drawer.VirtualKeyboardProvider holds the space for the keyboard on a scroll container, it watches the style attribute of that element. When a render or a script replaces a declaration of the provider, the provider keeps the new value for the restore and writes its declaration again before the next frame. In Base UI, a new padding-bottom of the consumer replaces the space until the next alignment, and the restore then writes the value from before the keyboard over it. | packages/react/src/drawer/virtual-keyboard-provider/DrawerVirtualKeyboardProvider.tsx |
| |
| VUE-63 | A popup that is not modal can stay open when a guard of its trigger asks for the close, for example when its open prop does not change. Tab from the trigger then moves the focus to the elements of the popup, and Tab from the last of them moves it to the element after the trigger. Shift+Tab from the trigger moves the focus to the element before the trigger. Tab from the trigger skips a popup with no tabbable element. The trigger keeps the focus only when no other element is tabbable. The search for the next element skips the guards of the trigger and the content of the popup. In Base UI, the search can give a guard of the portal, and that guard sends the focus back into the popup. When the popup has no tabbable element, the guards call focus() on each other in the same call stack, and no handler ends the sequence. A popup that has guards beside its trigger has no guards at the place of its own portal (VUE-66), so the search cannot give one. |
| packages/react/src/utils/popups/useTriggerFocusGuards.ts |
|
| VUE-64 | A popup root in a pending branch of <Suspense> does not attach its store to its handle. The handle, and each detached trigger, stay with the root that is mounted. The root attaches when Vue resolves the branch, before the onMounted hook of each part below it. A branch that Vue drops never attaches. Base UI has this rule for a render that React does not commit. | packages/react/src/utils/popups/popupRoot.tsx |
| |
| VUE-65 | Combobox.createItems gives getValue and getLabel the leaf item of grouped data, and the result has the type of the leaf item. This holds with TypeScript 5.9 and 6.0. With these versions, the order of the members of the data type of Base UI gives the group as the item, so such a call does not type-check there. TypeScript 7.0 accepts both orders. | packages/react/src/combobox/items/createItems.ts |
| |
| VUE-66 | A popup that is not modal and stays open has one place in the tab order, right after its trigger, while its trigger has focus guards. The Tab key goes from the element before the trigger to the trigger, to the elements of the popup, and to the element after the trigger. Shift+Tab goes the other way. A menu goes from any element of its popup to the trigger for Shift+Tab, as in Base UI. This holds when the portal is beside the trigger, before it or detached. A popup with no tabbable element is skipped in both directions. The guard before the trigger sends a focus from any other element to the trigger when the popup stays open. The guard after the trigger sends a focus from the trigger, or from no element, to the start of the popup, and a focus from another element outside the popup to the end of the popup. The portal then renders no guards of its own. A focus that comes in through the last inside guard does not stop the return of the focus, so Escape from inside the popup returns the focus to the trigger. In Base UI, Tab from the element before the trigger stays on that element. A detached portal keeps the focus at the element after the trigger, or between that element and the popup. Base UI also sets the flag that stops the return of the focus before it tests where the focus comes from, so a focus that comes in through the last inside guard stops the return of the focus at the next close. |
| packages/react/src/utils/popups/useTriggerFocusGuards.ts |
|
| VUE-67 | In Safari on a Mac, a focus with no related target opens the popup of a trigger only when the last input of the window was a key. Each trigger records that input while it is mounted, also while it is not enabled. Base UI records it only while the trigger is enabled, and it assumes a key before the first record. A trigger of a menubar is enabled only while a menu of the menubar is open, so it misses the press that opened the menu. A hover on the next trigger then opens the next menu, the first menu gives the focus back to its trigger, and that focus opens the first menu again. Both menus then close. |
| packages/react/src/utils/popups/interactions/useFocus.ts |
|
Components of Vase UI's own
A component of Vase UI's own is a component that Base UI does not have. Its behavior follows a standard, and its API follows the convention of the families. parity/own holds one record for each component.
None yet.
Not ported
parity/scope.json lists what Vase UI does not port, each with its reason.
| What | Reason |
|---|---|
filter-dropdown as a public entry | Base UI has no package export, no index file and no docs page for it. Vase UI ports it only as the internal dependency of Menu. |
the 35 ./internals/* entry points | Base UI does not document them. Vase UI ports the code that a component needs as internal modules and exports none of them. |
| the temporal adapters | No Base UI component imports them at the tracked commit. |
createSelector and createSelectorMemoized | No Base UI component imports them. Vue computed does this work. |
StoreInspector | A React development panel. No component imports it. |
| the React 17 and 18 shims | They exist only to support old React versions. |
Accordion loopFocus and orientation props | Deprecated upstream; they have no effect. |
Context Menu positionMethod and closeParentOnEsc props | Deprecated upstream; "This prop has no effect on Context Menu." |