Repository navigation
Releases: ourostack/ouro-ui
Release list
v1.2.14
Install an item at this release; its Ouro dependencies install from the same tag. In an app with Tailwind CSS v4 and an "@/*" path alias, run shadcn's init once first, with the Base UI base:
npx shadcn@latest init --base base --preset nova --yes --no-monorepo
npx shadcn@latest add ourostack/ouro-ui/ouro-base#v1.2.14 ourostack/ouro-ui/dialog#v1.2.14The tests, stories and specs are optional, one item per component: npx shadcn@latest add ourostack/ouro-ui/dialog#v1.2.14 ourostack/ouro-ui/dialog-tests#v1.2.14, or ourostack/ouro-ui/ouro-tests#v1.2.14 for all of them. List every item with npx shadcn@latest search 'ourostack/ouro-ui#v1.2.14'.
Changes in this release
- New: Lightbox, a grid of image thumbnails that opens a full-screen viewer on the one pressed, steps through the images with the arrow keys, buttons, a trackpad or a swipe, and announces the position once per change; with
open,index,onOpenChangeandonIndexChangeit is the viewer alone, opened from thumbnails the app lays out. - New:
Tablecolumns sort from their headers withsortable(orsortValue), uncontrolled fromdefaultSortor controlled withsortandonSortChange, andTableHeadtakessortedandonSortfor parts; the sorted header carriesaria-sortand an arrow, and each sort is announced (docs, test).
Upgrading from v0.1.0
v0.1.0 installed each component with its tests, and v0.2.0 moves some shared helpers to new files, so update every Ouro component in one go.[^upgrade-v0.1.0][^upgrade-v0.2.0] Run the commands in your app's root, the folder with components.json. They find your folders the way shadcn does, from the aliases in components.json and the paths in your tsconfig (npx shadcn@latest info shows them), so they work in a src/ or an app/ layout as written.
What changed, by version
What an app on v0.1.0 has to know beyond these steps.[^upgrade-test] The release notes list every change.[^upgrade-releases]
-
v0.2.0: each component's tests became an optional
<name>-testsitem, some helpers moved to new files (step 4), and v0.1.0's self-referential theme lines can go (step 2).[^upgrade-v0.2.0] -
v0.3.0: sheets and dialogs stack at
--ouro-layer, a z-index of 50 by default, instead of level 0, so an app element at z-index 1 to 49 that used to show above an open sheet or dialog now sits under it.[^upgrade-v0.3.0] Raise the token in a:rootrule of your own if your app has fixed bars above z-50 (see "Theme" in the README), and drop any portal z-index workaround. A controlledDialogopened by a touch or pen tap focuses its popup instead of its first input, andConfirmDialog'sfinalFocusapplies only after a confirmed action; drop workarounds for either. Every coverage hint is/* istanbul ignore … @preserve */, so a build plugin that kept the hints can go.[^upgrade-hints] A-testsitem sets Vitest to^5. -
v0.3.1: every component starts with
"use client", so Next.js App Router pages can render them, and the@ouronamespace serves the newest release instead ofmain.[^upgrade-v0.3.1] -
After v0.7.0: a required field's
*is drawn with CSS on a new element around the label and is no longer a<span data-ouro-required>inside it, so tests that expected the label text "Email*" now find "Email".[^upgrade-v0.8.0] The clear, show-password and choose-date buttons in TextField and DatePicker are named by hidden text, notaria-label, so replacebutton[aria-label="…"]selectors with[data-ouro-clear],[data-ouro-show-password]or[data-ouro-choose-date]. The same goes for a file's remove button in FileField ([data-ouro-file-remove]), Combobox's clear and chip remove buttons ([data-ouro-combobox-clear],[data-ouro-combobox-chip-remove]) and NumberField's steppers ([data-ouro-increment],[data-ouro-decrement]). A remove button's hidden text holds the file or item name, so a loosegetByText("pasta.png")now finds it too; pass{ exact: true }.FieldLabel'sclassNamenow goes on the new element around the label, which also carries[data-ouro-required], and a group's labelidis on the inner<span>;style,id,data-*and handlers still go on the<label>. A numericsizeon TextField is now a type error and a development warning; set a width withclassName="w-40".FormErrorsvalues are nowErrorMessages | null | undefined, so code that readserrors.emailasstring | string[]no longer type checks until it handlesnulland React nodes.[^upgrade-v0.8.0] For action answers, importformErrorsfrom@/lib/ouro/form-answerand passerrors={formErrors(actionData, actionData?.errors)}from the initial render; this replaces the unreleased separateresultprop. It identifies equal new answers while keeping cleared fields masked on pending/client renders and rendering current uncleared messages. Cached/reused answers need{ key: acceptedAnswerKey }, changed on actual acceptance, not submit start. Raw records remain compatible but content-only. Reconstruct the value beside Form after JSON/spread/cloning or server-component transport: only messages serialize, not private identity; a safely transported acceptance key can identify the locally reconstructed answer. On a phone, a Link that is not in running text is now a box at least 44px tall and wide that takes its room in the layout, so a column of links grows; passinlineto a link in a sentence inside your own<div>or<span>.[^upgrade-link-box] A Form's top message element exists only while there is a message. Component roots no longer setm-0, so a parent'sspace-y-*now spaces them; remove any margin you added to work around that.[^upgrade-v0.8.0] -
v0.8.0: no component installs
ouro-base; add it once (step 1 does), and later adds leave your stylesheet alone.[^upgrade-v0.8.0] A component installs only the helpers it imports, one item per file inlib/ouro, and a-testsitem brings the tests of everything its component installs.[^upgrade-v0.8.0] The-testsitems no longer set a version for Vitest, Storybook or Playwright.[^upgrade-test-versions] The specs importtestandexpectfrome2e/ouro-fixtures.ts, a file of yours (step 1).[^upgrade-v0.8.0]lib/ouro/use-dev-warnings.tsis nowlib/ouro/use-dev-messages.ts(step 4).[^upgrade-v0.8.0] Earlier releases installed every helper inlib/ourowith any component, and an update deletes nothing: audit references before deleting unused historical helpers and their own tests (step 5).[^upgrade-v0.8.0]OuroProvidernow comes only with the components that render links and withtooltip; your copy stays, and a new app addslib-ouro-providerby name. -
After v0.8.2:
ouroTestProjects({ page })also sizes the touch project's Playwright host, preventing Vitest 4 from scaling landscape touch frames.[^upgrade-touch-host] The logical touch viewport still starts at 390x844 with mobile/touch input, but an explicitly enlarged host changes outer-window/screen dimensions and screen orientation; do not treat those as hardware-phone measurements.[^upgrade-touch-host] Withoutpage, the touch host stays 390x844.[^upgrade-touch-host] On Vitest 4, retain a host large enough for every requested test viewport, such as the Testing guide's 1600x1200 example.[^upgrade-touch-host] -
v1.0.0: the first stable release keeps the component APIs, theme tokens, defaults and test-tool minimums from v0.8.4.[^upgrade-v1.0.0] An app already on v0.8.4 needs no component API migration: review the update with
--dry-runand--diff, then overwrite only the items it already uses and reapply its local edits.[^upgrade-v1.0.0] Runtime components and their optional-testsitems remain separate; keep your app-owned fixtures and test configuration. Apps on older releases still need the applicable migration steps above and below. -
After v1.0.2: sheets, and a Menu shown as a bottom sheet, read their shadow from
--ouro-shadow-sheet-<side>, and Checkbox's tick draws over--ouro-duration-draw, both of which only a freshouro-baseadds (step 1 adds it again); without it a sheet draws no shadow and the tick appears at once.[^upgrade-v1.1.0] Every other shadow reads shadcn's--shadow-*tokens, and headings and overlay titles--font-heading, so a theme that sets them now restyles Ouro too.[^upgrade-v1.1.0][^upgrade-theme-tokens] -
After v1.0.1:
formErrorsnow declares its already-frozen message arrays readonly.[^upgrade-v1.0.2] Keep mutableFormErrorsandErrorMessagesfor raw builders; annotate returned snapshots asFormErrorValueorReadonlyFormErrors, or let their type infer. Form, EditSheet, fielderrorprops and the normalization helper accept readonly projections. This corrects a false mutation permission, so a snapshot no longer fits a mutable raw-map annotation.[^upgrade-v1.0.2] TypeScript'sArray.isArraynarrows toany[]and can bypass readonly; unchanged runtime freezing still rejects container mutation, without deeply freezing React nodes. -
After v1.2.2:
ButtonacceptsreloadDocumentfor a document request through an internalhref, and its downloads use a plain anchor likeLink's.[^upgrade-document-download] A false or nulldownloadno longer forces document navigation; passreloadDocumentwhen a resource route needs it without a download.[^upgrade-document-download] -
After v1.2.4: Select, Menu, Combobox, Popover, Tooltip, HoverCard and NavigationMenu open their popups at the end of
<body>unlessOuroProvideris given aportalContainer, and in development each warns once when it...
v1.2.13
Install an item at this release; its Ouro dependencies install from the same tag. In an app with Tailwind CSS v4 and an "@/*" path alias, run shadcn's init once first, with the Base UI base:
npx shadcn@latest init --base base --preset nova --yes --no-monorepo
npx shadcn@latest add ourostack/ouro-ui/ouro-base#v1.2.13 ourostack/ouro-ui/dialog#v1.2.13The tests, stories and specs are optional, one item per component: npx shadcn@latest add ourostack/ouro-ui/dialog#v1.2.13 ourostack/ouro-ui/dialog-tests#v1.2.13, or ourostack/ouro-ui/ouro-tests#v1.2.13 for all of them. List every item with npx shadcn@latest search 'ourostack/ouro-ui#v1.2.13'.
Changes in this release
- The site, docs, Storybook and
@ouronamespace moved to https://ui.ouro.bot/, so acomponents.jsoncan namehttps://ui.ouro.bot/r/{name}.json. - New: the tokens as plain stylesheets for pages without Tailwind,
site/public/css/ouro-base.css,ouro-theme.cssandthemes/<preset>.css, which follow the system's dark mode unless the page sets.lightor.dark(guide, test).
Upgrading from v0.1.0
v0.1.0 installed each component with its tests, and v0.2.0 moves some shared helpers to new files, so update every Ouro component in one go.[^upgrade-v0.1.0][^upgrade-v0.2.0] Run the commands in your app's root, the folder with components.json. They find your folders the way shadcn does, from the aliases in components.json and the paths in your tsconfig (npx shadcn@latest info shows them), so they work in a src/ or an app/ layout as written.
What changed, by version
What an app on v0.1.0 has to know beyond these steps.[^upgrade-test] The release notes list every change.[^upgrade-releases]
-
v0.2.0: each component's tests became an optional
<name>-testsitem, some helpers moved to new files (step 4), and v0.1.0's self-referential theme lines can go (step 2).[^upgrade-v0.2.0] -
v0.3.0: sheets and dialogs stack at
--ouro-layer, a z-index of 50 by default, instead of level 0, so an app element at z-index 1 to 49 that used to show above an open sheet or dialog now sits under it.[^upgrade-v0.3.0] Raise the token in a:rootrule of your own if your app has fixed bars above z-50 (see "Theme" in the README), and drop any portal z-index workaround. A controlledDialogopened by a touch or pen tap focuses its popup instead of its first input, andConfirmDialog'sfinalFocusapplies only after a confirmed action; drop workarounds for either. Every coverage hint is/* istanbul ignore … @preserve */, so a build plugin that kept the hints can go.[^upgrade-hints] A-testsitem sets Vitest to^5. -
v0.3.1: every component starts with
"use client", so Next.js App Router pages can render them, and the@ouronamespace serves the newest release instead ofmain.[^upgrade-v0.3.1] -
After v0.7.0: a required field's
*is drawn with CSS on a new element around the label and is no longer a<span data-ouro-required>inside it, so tests that expected the label text "Email*" now find "Email".[^upgrade-v0.8.0] The clear, show-password and choose-date buttons in TextField and DatePicker are named by hidden text, notaria-label, so replacebutton[aria-label="…"]selectors with[data-ouro-clear],[data-ouro-show-password]or[data-ouro-choose-date]. The same goes for a file's remove button in FileField ([data-ouro-file-remove]), Combobox's clear and chip remove buttons ([data-ouro-combobox-clear],[data-ouro-combobox-chip-remove]) and NumberField's steppers ([data-ouro-increment],[data-ouro-decrement]). A remove button's hidden text holds the file or item name, so a loosegetByText("pasta.png")now finds it too; pass{ exact: true }.FieldLabel'sclassNamenow goes on the new element around the label, which also carries[data-ouro-required], and a group's labelidis on the inner<span>;style,id,data-*and handlers still go on the<label>. A numericsizeon TextField is now a type error and a development warning; set a width withclassName="w-40".FormErrorsvalues are nowErrorMessages | null | undefined, so code that readserrors.emailasstring | string[]no longer type checks until it handlesnulland React nodes.[^upgrade-v0.8.0] For action answers, importformErrorsfrom@/lib/ouro/form-answerand passerrors={formErrors(actionData, actionData?.errors)}from the initial render; this replaces the unreleased separateresultprop. It identifies equal new answers while keeping cleared fields masked on pending/client renders and rendering current uncleared messages. Cached/reused answers need{ key: acceptedAnswerKey }, changed on actual acceptance, not submit start. Raw records remain compatible but content-only. Reconstruct the value beside Form after JSON/spread/cloning or server-component transport: only messages serialize, not private identity; a safely transported acceptance key can identify the locally reconstructed answer. On a phone, a Link that is not in running text is now a box at least 44px tall and wide that takes its room in the layout, so a column of links grows; passinlineto a link in a sentence inside your own<div>or<span>.[^upgrade-link-box] A Form's top message element exists only while there is a message. Component roots no longer setm-0, so a parent'sspace-y-*now spaces them; remove any margin you added to work around that.[^upgrade-v0.8.0] -
v0.8.0: no component installs
ouro-base; add it once (step 1 does), and later adds leave your stylesheet alone.[^upgrade-v0.8.0] A component installs only the helpers it imports, one item per file inlib/ouro, and a-testsitem brings the tests of everything its component installs.[^upgrade-v0.8.0] The-testsitems no longer set a version for Vitest, Storybook or Playwright.[^upgrade-test-versions] The specs importtestandexpectfrome2e/ouro-fixtures.ts, a file of yours (step 1).[^upgrade-v0.8.0]lib/ouro/use-dev-warnings.tsis nowlib/ouro/use-dev-messages.ts(step 4).[^upgrade-v0.8.0] Earlier releases installed every helper inlib/ourowith any component, and an update deletes nothing: audit references before deleting unused historical helpers and their own tests (step 5).[^upgrade-v0.8.0]OuroProvidernow comes only with the components that render links and withtooltip; your copy stays, and a new app addslib-ouro-providerby name. -
After v0.8.2:
ouroTestProjects({ page })also sizes the touch project's Playwright host, preventing Vitest 4 from scaling landscape touch frames.[^upgrade-touch-host] The logical touch viewport still starts at 390x844 with mobile/touch input, but an explicitly enlarged host changes outer-window/screen dimensions and screen orientation; do not treat those as hardware-phone measurements.[^upgrade-touch-host] Withoutpage, the touch host stays 390x844.[^upgrade-touch-host] On Vitest 4, retain a host large enough for every requested test viewport, such as the Testing guide's 1600x1200 example.[^upgrade-touch-host] -
v1.0.0: the first stable release keeps the component APIs, theme tokens, defaults and test-tool minimums from v0.8.4.[^upgrade-v1.0.0] An app already on v0.8.4 needs no component API migration: review the update with
--dry-runand--diff, then overwrite only the items it already uses and reapply its local edits.[^upgrade-v1.0.0] Runtime components and their optional-testsitems remain separate; keep your app-owned fixtures and test configuration. Apps on older releases still need the applicable migration steps above and below. -
After v1.0.2: sheets, and a Menu shown as a bottom sheet, read their shadow from
--ouro-shadow-sheet-<side>, and Checkbox's tick draws over--ouro-duration-draw, both of which only a freshouro-baseadds (step 1 adds it again); without it a sheet draws no shadow and the tick appears at once.[^upgrade-v1.1.0] Every other shadow reads shadcn's--shadow-*tokens, and headings and overlay titles--font-heading, so a theme that sets them now restyles Ouro too.[^upgrade-v1.1.0][^upgrade-theme-tokens] -
After v1.0.1:
formErrorsnow declares its already-frozen message arrays readonly.[^upgrade-v1.0.2] Keep mutableFormErrorsandErrorMessagesfor raw builders; annotate returned snapshots asFormErrorValueorReadonlyFormErrors, or let their type infer. Form, EditSheet, fielderrorprops and the normalization helper accept readonly projections. This corrects a false mutation permission, so a snapshot no longer fits a mutable raw-map annotation.[^upgrade-v1.0.2] TypeScript'sArray.isArraynarrows toany[]and can bypass readonly; unchanged runtime freezing still rejects container mutation, without deeply freezing React nodes. -
After v1.2.2:
ButtonacceptsreloadDocumentfor a document request through an internalhref, and its downloads use a plain anchor likeLink's.[^upgrade-document-download] A false or nulldownloadno longer forces document navigation; passreloadDocumentwhen a resource route needs it without a download.[^upgrade-document-download] -
After v1.2.4: Select, Menu, Combobox, Popover, Tooltip, HoverCard and NavigationMenu open their popups at the end of
<body>unlessOuroProvideris given aportalContainer, and in development each warns once when it opens outside every landmark of a page with a<main>.[^upgrade-portal-container] To keep popups in a landmark, makeconst popups = useRef<HTMLDivElement>(null), wrap the app in<OuroProvider portalContainer={popups}>and render<div ref={popups} />last inside<main>, outsid...
v1.2.12
Install an item at this release; its Ouro dependencies install from the same tag. In an app with Tailwind CSS v4 and an "@/*" path alias, run shadcn's init once first, with the Base UI base:
npx shadcn@latest init --base base --preset nova --yes --no-monorepo
npx shadcn@latest add ourostack/ouro-ui/ouro-base#v1.2.12 ourostack/ouro-ui/dialog#v1.2.12The tests, stories and specs are optional, one item per component: npx shadcn@latest add ourostack/ouro-ui/dialog#v1.2.12 ourostack/ouro-ui/dialog-tests#v1.2.12, or ourostack/ouro-ui/ouro-tests#v1.2.12 for all of them. List every item with npx shadcn@latest search 'ourostack/ouro-ui#v1.2.12'.
Changes in this release
- Fix:
audit()andmeasureTextContrast()leave no color easing outside shadow roots after they measure text; before, hiding and then showing the glyphs started each element's color transition, such as a link's underline, so the next measurement waited out that transition and took its screenshot while another was running (test). - Fix:
audit()now measures placeholder text, which axe leaves out, and fails a placeholder too faint against its field, such as one drawn at half the text color as Tailwind's base styles draw it (test). - Fix:
audit()andmeasureTextContrast()try a screenshot once more when it ran out of time on a box that shows and holds still; a stall in Vitest's Node process, which runs Playwright, could use up the limit while the page held still, and the text then failed as one that could not be screenshotted (test). - Add: the test kit's
popups.tslists the popups Ouro UI's story check opens and how it opens them, tooltips, navigation panels and hover cards among them. It imports nothing, so an app's Playwright story check can open the same popups from the same list.
Upgrading from v0.1.0
v0.1.0 installed each component with its tests, and v0.2.0 moves some shared helpers to new files, so update every Ouro component in one go.[^upgrade-v0.1.0][^upgrade-v0.2.0] Run the commands in your app's root, the folder with components.json. They find your folders the way shadcn does, from the aliases in components.json and the paths in your tsconfig (npx shadcn@latest info shows them), so they work in a src/ or an app/ layout as written.
What changed, by version
What an app on v0.1.0 has to know beyond these steps.[^upgrade-test] The release notes list every change.[^upgrade-releases]
-
v0.2.0: each component's tests became an optional
<name>-testsitem, some helpers moved to new files (step 4), and v0.1.0's self-referential theme lines can go (step 2).[^upgrade-v0.2.0] -
v0.3.0: sheets and dialogs stack at
--ouro-layer, a z-index of 50 by default, instead of level 0, so an app element at z-index 1 to 49 that used to show above an open sheet or dialog now sits under it.[^upgrade-v0.3.0] Raise the token in a:rootrule of your own if your app has fixed bars above z-50 (see "Theme" in the README), and drop any portal z-index workaround. A controlledDialogopened by a touch or pen tap focuses its popup instead of its first input, andConfirmDialog'sfinalFocusapplies only after a confirmed action; drop workarounds for either. Every coverage hint is/* istanbul ignore … @preserve */, so a build plugin that kept the hints can go.[^upgrade-hints] A-testsitem sets Vitest to^5. -
v0.3.1: every component starts with
"use client", so Next.js App Router pages can render them, and the@ouronamespace serves the newest release instead ofmain.[^upgrade-v0.3.1] -
After v0.7.0: a required field's
*is drawn with CSS on a new element around the label and is no longer a<span data-ouro-required>inside it, so tests that expected the label text "Email*" now find "Email".[^upgrade-v0.8.0] The clear, show-password and choose-date buttons in TextField and DatePicker are named by hidden text, notaria-label, so replacebutton[aria-label="…"]selectors with[data-ouro-clear],[data-ouro-show-password]or[data-ouro-choose-date]. The same goes for a file's remove button in FileField ([data-ouro-file-remove]), Combobox's clear and chip remove buttons ([data-ouro-combobox-clear],[data-ouro-combobox-chip-remove]) and NumberField's steppers ([data-ouro-increment],[data-ouro-decrement]). A remove button's hidden text holds the file or item name, so a loosegetByText("pasta.png")now finds it too; pass{ exact: true }.FieldLabel'sclassNamenow goes on the new element around the label, which also carries[data-ouro-required], and a group's labelidis on the inner<span>;style,id,data-*and handlers still go on the<label>. A numericsizeon TextField is now a type error and a development warning; set a width withclassName="w-40".FormErrorsvalues are nowErrorMessages | null | undefined, so code that readserrors.emailasstring | string[]no longer type checks until it handlesnulland React nodes.[^upgrade-v0.8.0] For action answers, importformErrorsfrom@/lib/ouro/form-answerand passerrors={formErrors(actionData, actionData?.errors)}from the initial render; this replaces the unreleased separateresultprop. It identifies equal new answers while keeping cleared fields masked on pending/client renders and rendering current uncleared messages. Cached/reused answers need{ key: acceptedAnswerKey }, changed on actual acceptance, not submit start. Raw records remain compatible but content-only. Reconstruct the value beside Form after JSON/spread/cloning or server-component transport: only messages serialize, not private identity; a safely transported acceptance key can identify the locally reconstructed answer. On a phone, a Link that is not in running text is now a box at least 44px tall and wide that takes its room in the layout, so a column of links grows; passinlineto a link in a sentence inside your own<div>or<span>.[^upgrade-link-box] A Form's top message element exists only while there is a message. Component roots no longer setm-0, so a parent'sspace-y-*now spaces them; remove any margin you added to work around that.[^upgrade-v0.8.0] -
v0.8.0: no component installs
ouro-base; add it once (step 1 does), and later adds leave your stylesheet alone.[^upgrade-v0.8.0] A component installs only the helpers it imports, one item per file inlib/ouro, and a-testsitem brings the tests of everything its component installs.[^upgrade-v0.8.0] The-testsitems no longer set a version for Vitest, Storybook or Playwright.[^upgrade-test-versions] The specs importtestandexpectfrome2e/ouro-fixtures.ts, a file of yours (step 1).[^upgrade-v0.8.0]lib/ouro/use-dev-warnings.tsis nowlib/ouro/use-dev-messages.ts(step 4).[^upgrade-v0.8.0] Earlier releases installed every helper inlib/ourowith any component, and an update deletes nothing: audit references before deleting unused historical helpers and their own tests (step 5).[^upgrade-v0.8.0]OuroProvidernow comes only with the components that render links and withtooltip; your copy stays, and a new app addslib-ouro-providerby name. -
After v0.8.2:
ouroTestProjects({ page })also sizes the touch project's Playwright host, preventing Vitest 4 from scaling landscape touch frames.[^upgrade-touch-host] The logical touch viewport still starts at 390x844 with mobile/touch input, but an explicitly enlarged host changes outer-window/screen dimensions and screen orientation; do not treat those as hardware-phone measurements.[^upgrade-touch-host] Withoutpage, the touch host stays 390x844.[^upgrade-touch-host] On Vitest 4, retain a host large enough for every requested test viewport, such as the Testing guide's 1600x1200 example.[^upgrade-touch-host] -
v1.0.0: the first stable release keeps the component APIs, theme tokens, defaults and test-tool minimums from v0.8.4.[^upgrade-v1.0.0] An app already on v0.8.4 needs no component API migration: review the update with
--dry-runand--diff, then overwrite only the items it already uses and reapply its local edits.[^upgrade-v1.0.0] Runtime components and their optional-testsitems remain separate; keep your app-owned fixtures and test configuration. Apps on older releases still need the applicable migration steps above and below. -
After v1.0.2: sheets, and a Menu shown as a bottom sheet, read their shadow from
--ouro-shadow-sheet-<side>, and Checkbox's tick draws over--ouro-duration-draw, both of which only a freshouro-baseadds (step 1 adds it again); without it a sheet draws no shadow and the tick appears at once.[^upgrade-v1.1.0] Every other shadow reads shadcn's--shadow-*tokens, and headings and overlay titles--font-heading, so a theme that sets them now restyles Ouro too.[^upgrade-v1.1.0][^upgrade-theme-tokens] -
After v1.0.1:
formErrorsnow declares its already-frozen message arrays readonly.[^upgrade-v1.0.2] Keep mutableFormErrors...
v1.2.11
Install an item at this release; its Ouro dependencies install from the same tag. In an app with Tailwind CSS v4 and an "@/*" path alias, run shadcn's init once first, with the Base UI base:
npx shadcn@latest init --base base --preset nova --yes --no-monorepo
npx shadcn@latest add ourostack/ouro-ui/ouro-base#v1.2.11 ourostack/ouro-ui/dialog#v1.2.11The tests, stories and specs are optional, one item per component: npx shadcn@latest add ourostack/ouro-ui/dialog#v1.2.11 ourostack/ouro-ui/dialog-tests#v1.2.11, or ourostack/ouro-ui/ouro-tests#v1.2.11 for all of them. List every item with npx shadcn@latest search 'ourostack/ouro-ui#v1.2.11'.
Changes in this release
- Fix:
audit()andmeasureTextContrast()put back the scroll position of a scrolled box inside an open shadow root too; before, they left it where they had scrolled it to measure text (test). - Fix: the Sheet "With Form" story draws its placeholder in the muted text color, as Ouro's own fields do; before, it was half the text color, too faint against the light theme.
Upgrading from v0.1.0
v0.1.0 installed each component with its tests, and v0.2.0 moves some shared helpers to new files, so update every Ouro component in one go.[^upgrade-v0.1.0][^upgrade-v0.2.0] Run the commands in your app's root, the folder with components.json. They find your folders the way shadcn does, from the aliases in components.json and the paths in your tsconfig (npx shadcn@latest info shows them), so they work in a src/ or an app/ layout as written.
What changed, by version
What an app on v0.1.0 has to know beyond these steps.[^upgrade-test] The release notes list every change.[^upgrade-releases]
-
v0.2.0: each component's tests became an optional
<name>-testsitem, some helpers moved to new files (step 4), and v0.1.0's self-referential theme lines can go (step 2).[^upgrade-v0.2.0] -
v0.3.0: sheets and dialogs stack at
--ouro-layer, a z-index of 50 by default, instead of level 0, so an app element at z-index 1 to 49 that used to show above an open sheet or dialog now sits under it.[^upgrade-v0.3.0] Raise the token in a:rootrule of your own if your app has fixed bars above z-50 (see "Theme" in the README), and drop any portal z-index workaround. A controlledDialogopened by a touch or pen tap focuses its popup instead of its first input, andConfirmDialog'sfinalFocusapplies only after a confirmed action; drop workarounds for either. Every coverage hint is/* istanbul ignore … @preserve */, so a build plugin that kept the hints can go.[^upgrade-hints] A-testsitem sets Vitest to^5. -
v0.3.1: every component starts with
"use client", so Next.js App Router pages can render them, and the@ouronamespace serves the newest release instead ofmain.[^upgrade-v0.3.1] -
After v0.7.0: a required field's
*is drawn with CSS on a new element around the label and is no longer a<span data-ouro-required>inside it, so tests that expected the label text "Email*" now find "Email".[^upgrade-v0.8.0] The clear, show-password and choose-date buttons in TextField and DatePicker are named by hidden text, notaria-label, so replacebutton[aria-label="…"]selectors with[data-ouro-clear],[data-ouro-show-password]or[data-ouro-choose-date]. The same goes for a file's remove button in FileField ([data-ouro-file-remove]), Combobox's clear and chip remove buttons ([data-ouro-combobox-clear],[data-ouro-combobox-chip-remove]) and NumberField's steppers ([data-ouro-increment],[data-ouro-decrement]). A remove button's hidden text holds the file or item name, so a loosegetByText("pasta.png")now finds it too; pass{ exact: true }.FieldLabel'sclassNamenow goes on the new element around the label, which also carries[data-ouro-required], and a group's labelidis on the inner<span>;style,id,data-*and handlers still go on the<label>. A numericsizeon TextField is now a type error and a development warning; set a width withclassName="w-40".FormErrorsvalues are nowErrorMessages | null | undefined, so code that readserrors.emailasstring | string[]no longer type checks until it handlesnulland React nodes.[^upgrade-v0.8.0] For action answers, importformErrorsfrom@/lib/ouro/form-answerand passerrors={formErrors(actionData, actionData?.errors)}from the initial render; this replaces the unreleased separateresultprop. It identifies equal new answers while keeping cleared fields masked on pending/client renders and rendering current uncleared messages. Cached/reused answers need{ key: acceptedAnswerKey }, changed on actual acceptance, not submit start. Raw records remain compatible but content-only. Reconstruct the value beside Form after JSON/spread/cloning or server-component transport: only messages serialize, not private identity; a safely transported acceptance key can identify the locally reconstructed answer. On a phone, a Link that is not in running text is now a box at least 44px tall and wide that takes its room in the layout, so a column of links grows; passinlineto a link in a sentence inside your own<div>or<span>.[^upgrade-link-box] A Form's top message element exists only while there is a message. Component roots no longer setm-0, so a parent'sspace-y-*now spaces them; remove any margin you added to work around that.[^upgrade-v0.8.0] -
v0.8.0: no component installs
ouro-base; add it once (step 1 does), and later adds leave your stylesheet alone.[^upgrade-v0.8.0] A component installs only the helpers it imports, one item per file inlib/ouro, and a-testsitem brings the tests of everything its component installs.[^upgrade-v0.8.0] The-testsitems no longer set a version for Vitest, Storybook or Playwright.[^upgrade-test-versions] The specs importtestandexpectfrome2e/ouro-fixtures.ts, a file of yours (step 1).[^upgrade-v0.8.0]lib/ouro/use-dev-warnings.tsis nowlib/ouro/use-dev-messages.ts(step 4).[^upgrade-v0.8.0] Earlier releases installed every helper inlib/ourowith any component, and an update deletes nothing: audit references before deleting unused historical helpers and their own tests (step 5).[^upgrade-v0.8.0]OuroProvidernow comes only with the components that render links and withtooltip; your copy stays, and a new app addslib-ouro-providerby name. -
After v0.8.2:
ouroTestProjects({ page })also sizes the touch project's Playwright host, preventing Vitest 4 from scaling landscape touch frames.[^upgrade-touch-host] The logical touch viewport still starts at 390x844 with mobile/touch input, but an explicitly enlarged host changes outer-window/screen dimensions and screen orientation; do not treat those as hardware-phone measurements.[^upgrade-touch-host] Withoutpage, the touch host stays 390x844.[^upgrade-touch-host] On Vitest 4, retain a host large enough for every requested test viewport, such as the Testing guide's 1600x1200 example.[^upgrade-touch-host] -
v1.0.0: the first stable release keeps the component APIs, theme tokens, defaults and test-tool minimums from v0.8.4.[^upgrade-v1.0.0] An app already on v0.8.4 needs no component API migration: review the update with
--dry-runand--diff, then overwrite only the items it already uses and reapply its local edits.[^upgrade-v1.0.0] Runtime components and their optional-testsitems remain separate; keep your app-owned fixtures and test configuration. Apps on older releases still need the applicable migration steps above and below. -
After v1.0.2: sheets, and a Menu shown as a bottom sheet, read their shadow from
--ouro-shadow-sheet-<side>, and Checkbox's tick draws over--ouro-duration-draw, both of which only a freshouro-baseadds (step 1 adds it again); without it a sheet draws no shadow and the tick appears at once.[^upgrade-v1.1.0] Every other shadow reads shadcn's--shadow-*tokens, and headings and overlay titles--font-heading, so a theme that sets them now restyles Ouro too.[^upgrade-v1.1.0][^upgrade-theme-tokens] -
After v1.0.1:
formErrorsnow declares its already-frozen message arrays readonly.[^upgrade-v1.0.2] Keep mutableFormErrorsandErrorMessagesfor raw builders; annotate returned snapshots asFormErrorValueorReadonlyFormErrors, or let their type infer. Form, EditSheet, fielderrorprops and the normalization helper accept readonly projections. This corrects a false mutation permission, so a snapshot no longer fits a mutable raw-map annotation.[^upgrade-v1.0.2] TypeScript'sArray.isArraynarrows toany[]and can bypass readonly; unchanged runtime freezing still rejects container mutation, without deeply freezing React nodes. -
After v1.2.2:
ButtonacceptsreloadDocumentfor a document request through an internalhref, and its downloads use a plain anchor likeLink's.[^upgrade-document-download] A false or nulldownloadno longer forces document navigation; passreloadDocumentwhen a resource route needs it without a download.[^upgrade-document-download] -
After v1.2.4: Select, Menu, Combobox, Popover, Tooltip, HoverCard and NavigationMenu open their popups at the end of
<body>unlessOuroProvideris given aportalContainer, and in development each warns once when it opens outside every landmark of a page with a<main>.[^upgrade-portal-container] To keep popups in a landmark, makeconst popups = useRef<HTMLDivElement>(null), wrap the app in `<OuroProvider portalContainer={p...
v1.2.10
Install an item at this release; its Ouro dependencies install from the same tag. In an app with Tailwind CSS v4 and an "@/*" path alias, run shadcn's init once first, with the Base UI base:
npx shadcn@latest init --base base --preset nova --yes --no-monorepo
npx shadcn@latest add ourostack/ouro-ui/ouro-base#v1.2.10 ourostack/ouro-ui/dialog#v1.2.10The tests, stories and specs are optional, one item per component: npx shadcn@latest add ourostack/ouro-ui/dialog#v1.2.10 ourostack/ouro-ui/dialog-tests#v1.2.10, or ourostack/ouro-ui/ouro-tests#v1.2.10 for all of them. List every item with npx shadcn@latest search 'ourostack/ouro-ui#v1.2.10'.
Changes in this release
- Fix: the
testing/axekit compiles with TypeScript 5.9 again; v1.2.9'sauditnamed afocus()option only TypeScript 6's DOM types have (check).
Upgrading from v0.1.0
v0.1.0 installed each component with its tests, and v0.2.0 moves some shared helpers to new files, so update every Ouro component in one go.[^upgrade-v0.1.0][^upgrade-v0.2.0] Run the commands in your app's root, the folder with components.json. They find your folders the way shadcn does, from the aliases in components.json and the paths in your tsconfig (npx shadcn@latest info shows them), so they work in a src/ or an app/ layout as written.
What changed, by version
What an app on v0.1.0 has to know beyond these steps.[^upgrade-test] The release notes list every change.[^upgrade-releases]
-
v0.2.0: each component's tests became an optional
<name>-testsitem, some helpers moved to new files (step 4), and v0.1.0's self-referential theme lines can go (step 2).[^upgrade-v0.2.0] -
v0.3.0: sheets and dialogs stack at
--ouro-layer, a z-index of 50 by default, instead of level 0, so an app element at z-index 1 to 49 that used to show above an open sheet or dialog now sits under it.[^upgrade-v0.3.0] Raise the token in a:rootrule of your own if your app has fixed bars above z-50 (see "Theme" in the README), and drop any portal z-index workaround. A controlledDialogopened by a touch or pen tap focuses its popup instead of its first input, andConfirmDialog'sfinalFocusapplies only after a confirmed action; drop workarounds for either. Every coverage hint is/* istanbul ignore … @preserve */, so a build plugin that kept the hints can go.[^upgrade-hints] A-testsitem sets Vitest to^5. -
v0.3.1: every component starts with
"use client", so Next.js App Router pages can render them, and the@ouronamespace serves the newest release instead ofmain.[^upgrade-v0.3.1] -
After v0.7.0: a required field's
*is drawn with CSS on a new element around the label and is no longer a<span data-ouro-required>inside it, so tests that expected the label text "Email*" now find "Email".[^upgrade-v0.8.0] The clear, show-password and choose-date buttons in TextField and DatePicker are named by hidden text, notaria-label, so replacebutton[aria-label="…"]selectors with[data-ouro-clear],[data-ouro-show-password]or[data-ouro-choose-date]. The same goes for a file's remove button in FileField ([data-ouro-file-remove]), Combobox's clear and chip remove buttons ([data-ouro-combobox-clear],[data-ouro-combobox-chip-remove]) and NumberField's steppers ([data-ouro-increment],[data-ouro-decrement]). A remove button's hidden text holds the file or item name, so a loosegetByText("pasta.png")now finds it too; pass{ exact: true }.FieldLabel'sclassNamenow goes on the new element around the label, which also carries[data-ouro-required], and a group's labelidis on the inner<span>;style,id,data-*and handlers still go on the<label>. A numericsizeon TextField is now a type error and a development warning; set a width withclassName="w-40".FormErrorsvalues are nowErrorMessages | null | undefined, so code that readserrors.emailasstring | string[]no longer type checks until it handlesnulland React nodes.[^upgrade-v0.8.0] For action answers, importformErrorsfrom@/lib/ouro/form-answerand passerrors={formErrors(actionData, actionData?.errors)}from the initial render; this replaces the unreleased separateresultprop. It identifies equal new answers while keeping cleared fields masked on pending/client renders and rendering current uncleared messages. Cached/reused answers need{ key: acceptedAnswerKey }, changed on actual acceptance, not submit start. Raw records remain compatible but content-only. Reconstruct the value beside Form after JSON/spread/cloning or server-component transport: only messages serialize, not private identity; a safely transported acceptance key can identify the locally reconstructed answer. On a phone, a Link that is not in running text is now a box at least 44px tall and wide that takes its room in the layout, so a column of links grows; passinlineto a link in a sentence inside your own<div>or<span>.[^upgrade-link-box] A Form's top message element exists only while there is a message. Component roots no longer setm-0, so a parent'sspace-y-*now spaces them; remove any margin you added to work around that.[^upgrade-v0.8.0] -
v0.8.0: no component installs
ouro-base; add it once (step 1 does), and later adds leave your stylesheet alone.[^upgrade-v0.8.0] A component installs only the helpers it imports, one item per file inlib/ouro, and a-testsitem brings the tests of everything its component installs.[^upgrade-v0.8.0] The-testsitems no longer set a version for Vitest, Storybook or Playwright.[^upgrade-test-versions] The specs importtestandexpectfrome2e/ouro-fixtures.ts, a file of yours (step 1).[^upgrade-v0.8.0]lib/ouro/use-dev-warnings.tsis nowlib/ouro/use-dev-messages.ts(step 4).[^upgrade-v0.8.0] Earlier releases installed every helper inlib/ourowith any component, and an update deletes nothing: audit references before deleting unused historical helpers and their own tests (step 5).[^upgrade-v0.8.0]OuroProvidernow comes only with the components that render links and withtooltip; your copy stays, and a new app addslib-ouro-providerby name. -
After v0.8.2:
ouroTestProjects({ page })also sizes the touch project's Playwright host, preventing Vitest 4 from scaling landscape touch frames.[^upgrade-touch-host] The logical touch viewport still starts at 390x844 with mobile/touch input, but an explicitly enlarged host changes outer-window/screen dimensions and screen orientation; do not treat those as hardware-phone measurements.[^upgrade-touch-host] Withoutpage, the touch host stays 390x844.[^upgrade-touch-host] On Vitest 4, retain a host large enough for every requested test viewport, such as the Testing guide's 1600x1200 example.[^upgrade-touch-host] -
v1.0.0: the first stable release keeps the component APIs, theme tokens, defaults and test-tool minimums from v0.8.4.[^upgrade-v1.0.0] An app already on v0.8.4 needs no component API migration: review the update with
--dry-runand--diff, then overwrite only the items it already uses and reapply its local edits.[^upgrade-v1.0.0] Runtime components and their optional-testsitems remain separate; keep your app-owned fixtures and test configuration. Apps on older releases still need the applicable migration steps above and below. -
After v1.0.2: sheets, and a Menu shown as a bottom sheet, read their shadow from
--ouro-shadow-sheet-<side>, and Checkbox's tick draws over--ouro-duration-draw, both of which only a freshouro-baseadds (step 1 adds it again); without it a sheet draws no shadow and the tick appears at once.[^upgrade-v1.1.0] Every other shadow reads shadcn's--shadow-*tokens, and headings and overlay titles--font-heading, so a theme that sets them now restyles Ouro too.[^upgrade-v1.1.0][^upgrade-theme-tokens] -
After v1.0.1:
formErrorsnow declares its already-frozen message arrays readonly.[^upgrade-v1.0.2] Keep mutableFormErrorsandErrorMessagesfor raw builders; annotate returned snapshots asFormErrorValueorReadonlyFormErrors, or let their type infer. Form, EditSheet, fielderrorprops and the normalization helper accept readonly projections. This corrects a false mutation permission, so a snapshot no longer fits a mutable raw-map annotation.[^upgrade-v1.0.2] TypeScript'sArray.isArraynarrows toany[]and can bypass readonly; unchanged runtime freezing still rejects container mutation, without deeply freezing React nodes. -
After v1.2.2:
ButtonacceptsreloadDocumentfor a document request through an internalhref, and its downloads use a plain anchor likeLink's.[^upgrade-document-download] A false or nulldownloadno longer forces document navigation; passreloadDocumentwhen a resource route needs it without a download.[^upgrade-document-download] -
After v1.2.4: Select, Menu, Combobox, Popover, Tooltip, HoverCard and NavigationMenu open their popups at the end of
<body>unlessOuroProvideris given aportalContainer, and in development each warns once when it opens outside every landmark of a page with a<main>.[^upgrade-portal-container] To keep popups in a landmark, makeconst popups = useRef<HTMLDivElement>(null), wrap the app in<OuroProvider portalContainer={popups}>and render<div ref={popups} />last inside<main>, outside any element with atransform,filterorcontain, which would clip or move the popups. In a Next.js App Router layout, which is a server component, put the provider and the ref in a small client component. Inside a dialog your app draws itself, wrap its content in<OuroProvider portalContainer={null}>so its popups open where Base UI puts them.[^upgrade-portal-null] Dialogs, sheets and toasts still open ...
v1.2.9
Install an item at this release; its Ouro dependencies install from the same tag. In an app with Tailwind CSS v4 and an "@/*" path alias, run shadcn's init once first, with the Base UI base:
npx shadcn@latest init --base base --preset nova --yes --no-monorepo
npx shadcn@latest add ourostack/ouro-ui/ouro-base#v1.2.9 ourostack/ouro-ui/dialog#v1.2.9The tests, stories and specs are optional, one item per component: npx shadcn@latest add ourostack/ouro-ui/dialog#v1.2.9 ourostack/ouro-ui/dialog-tests#v1.2.9, or ourostack/ouro-ui/ouro-tests#v1.2.9 for all of them. List every item with npx shadcn@latest search 'ourostack/ouro-ui#v1.2.9'.
Changes in this release
- Fix:
audit()leaves the page as it found it: its Tab walk puts focus back where it was, with a focus ring only if it had one, and nothing focused if nothing was, with the selection or a field's caret as it was (test), and every scroll position it moved to measure text goes back (test, test). Before, with nothing focused, the walk left focus on the last control it reached, which could fan out and pause a toast stack. - Fix:
audit()andmeasureTextContrast()no longer leave an emptystyleattribute on the focused field when they screenshot the page (test). - Change:
audit()throws, naming the change, when its Tab walk changes what is open on the page and it has not changed back a second later, such as closing a popup that closes when focus leaves it (test). Before, it returned its findings and left the page changed. - Fix: the census puts back the page's own
window.axe, or none, when it is done, and its own axe-core stops listening for frame messages (test). - Fix: a collapsed stack of toasts no longer hides anything from screen readers, so a touch screen reader such as VoiceOver on iPhone reaches every toast, a destructive one included, with its action and dismiss buttons (test, spec). Before, Base UI hid the dismiss buttons until the stack was hovered or focused from the keyboard, and hid a destructive toast whole until it had focus.
- Change: the Notifications region is no longer a live region; two live regions last inside it say the words of each toast's message and description, politely or, for a destructive toast, assertively from a
role="alert", and never its buttons; the words leave them seven seconds later (test). For those seconds, its words are in the page twice, so find a toast in a test by its role and name,getByRole("dialog", { name: "Saved" })("alertdialog"for a destructive one), not by its text. - Fix: focus in a toast fans the stack out and pauses it however it came, as a screen reader moves it, where Base UI did so only for focus from the keyboard (test).
Upgrading from v0.1.0
v0.1.0 installed each component with its tests, and v0.2.0 moves some shared helpers to new files, so update every Ouro component in one go.[^upgrade-v0.1.0][^upgrade-v0.2.0] Run the commands in your app's root, the folder with components.json. They find your folders the way shadcn does, from the aliases in components.json and the paths in your tsconfig (npx shadcn@latest info shows them), so they work in a src/ or an app/ layout as written.
What changed, by version
What an app on v0.1.0 has to know beyond these steps.[^upgrade-test] The release notes list every change.[^upgrade-releases]
- v0.2.0: each component's tests became an optional
<name>-testsitem, some helpers moved to new files (step 4), and v0.1.0's self-referential theme lines can go (step 2).[^upgrade-v0.2.0] - v0.3.0: sheets and dialogs stack at
--ouro-layer, a z-index of 50 by default, instead of level 0, so an app element at z-index 1 to 49 that used to show above an open sheet or dialog now sits under it.[^upgrade-v0.3.0] Raise the token in a:rootrule of your own if your app has fixed bars above z-50 (see "Theme" in the README), and drop any portal z-index workaround. A controlledDialogopened by a touch or pen tap focuses its popup instead of its first input, andConfirmDialog'sfinalFocusapplies only after a confirmed action; drop workarounds for either. Every coverage hint is/* istanbul ignore … @preserve */, so a build plugin that kept the hints can go.[^upgrade-hints] A-testsitem sets Vitest to^5. - v0.3.1: every component starts with
"use client", so Next.js App Router pages can render them, and the@ouronamespace serves the newest release instead ofmain.[^upgrade-v0.3.1] - After v0.7.0: a required field's
*is drawn with CSS on a new element around the label and is no longer a<span data-ouro-required>inside it, so tests that expected the label text "Email*" now find "Email".[^upgrade-v0.8.0] The clear, show-password and choose-date buttons in TextField and DatePicker are named by hidden text, notaria-label, so replacebutton[aria-label="…"]selectors with[data-ouro-clear],[data-ouro-show-password]or[data-ouro-choose-date]. The same goes for a file's remove button in FileField ([data-ouro-file-remove]), Combobox's clear and chip remove buttons ([data-ouro-combobox-clear],[data-ouro-combobox-chip-remove]) and NumberField's steppers ([data-ouro-increment],[data-ouro-decrement]). A remove button's hidden text holds the file or item name, so a loosegetByText("pasta.png")now finds it too; pass{ exact: true }.FieldLabel'sclassNamenow goes on the new element around the label, which also carries[data-ouro-required], and a group's labelidis on the inner<span>;style,id,data-*and handlers still go on the<label>. A numericsizeon TextField is now a type error and a development warning; set a width withclassName="w-40".FormErrorsvalues are nowErrorMessages | null | undefined, so code that readserrors.emailasstring | string[]no longer type checks until it handlesnulland React nodes.[^upgrade-v0.8.0] For action answers, importformErrorsfrom@/lib/ouro/form-answerand passerrors={formErrors(actionData, actionData?.errors)}from the initial render; this replaces the unreleased separateresultprop. It identifies equal new answers while keeping cleared fields masked on pending/client renders and rendering current uncleared messages. Cached/reused answers need{ key: acceptedAnswerKey }, changed on actual acceptance, not submit start. Raw records remain compatible but content-only. Reconstruct the value beside Form after JSON/spread/cloning or server-component transport: only messages serialize, not private identity; a safely transported acceptance key can identify the locally reconstructed answer. On a phone, a Link that is not in running text is now a box at least 44px tall and wide that takes its room in the layout, so a column of links grows; passinlineto a link in a sentence inside your own<div>or<span>.[^upgrade-link-box] A Form's top message element exists only while there is a message. Component roots no longer setm-0, so a parent'sspace-y-*now spaces them; remove any margin you added to work around that.[^upgrade-v0.8.0] - v0.8.0: no component installs
ouro-base; add it once (step 1 does), and later adds leave ...
v1.2.8
Install an item at this release; its Ouro dependencies install from the same tag. In an app with Tailwind CSS v4 and an "@/*" path alias, run shadcn's init once first, with the Base UI base:
npx shadcn@latest init --base base --preset nova --yes --no-monorepo
npx shadcn@latest add ourostack/ouro-ui/ouro-base#v1.2.8 ourostack/ouro-ui/dialog#v1.2.8The tests, stories and specs are optional, one item per component: npx shadcn@latest add ourostack/ouro-ui/dialog#v1.2.8 ourostack/ouro-ui/dialog-tests#v1.2.8, or ourostack/ouro-ui/ouro-tests#v1.2.8 for all of them. List every item with npx shadcn@latest search 'ourostack/ouro-ui#v1.2.8'.
Changes in this release
- Fix: the census kit's
collectCensusthrows on an axe-core older than the kits' minimum, naming the version it found, before it loads axe into the page, asauditdoes (test). Both read the minimum and the comparison from the test kit'saxe-version.ts; the census ships a generated copy,e2e/ouro-census/census-axe-version.mjs(test).
Upgrading from v0.1.0
v0.1.0 installed each component with its tests, and v0.2.0 moves some shared helpers to new files, so update every Ouro component in one go.[^upgrade-v0.1.0][^upgrade-v0.2.0] Run the commands in your app's root, the folder with components.json. They find your folders the way shadcn does, from the aliases in components.json and the paths in your tsconfig (npx shadcn@latest info shows them), so they work in a src/ or an app/ layout as written.
What changed, by version
What an app on v0.1.0 has to know beyond these steps.[^upgrade-test] The release notes list every change.[^upgrade-releases]
-
v0.2.0: each component's tests became an optional
<name>-testsitem, some helpers moved to new files (step 4), and v0.1.0's self-referential theme lines can go (step 2).[^upgrade-v0.2.0] -
v0.3.0: sheets and dialogs stack at
--ouro-layer, a z-index of 50 by default, instead of level 0, so an app element at z-index 1 to 49 that used to show above an open sheet or dialog now sits under it.[^upgrade-v0.3.0] Raise the token in a:rootrule of your own if your app has fixed bars above z-50 (see "Theme" in the README), and drop any portal z-index workaround. A controlledDialogopened by a touch or pen tap focuses its popup instead of its first input, andConfirmDialog'sfinalFocusapplies only after a confirmed action; drop workarounds for either. Every coverage hint is/* istanbul ignore … @preserve */, so a build plugin that kept the hints can go.[^upgrade-hints] A-testsitem sets Vitest to^5. -
v0.3.1: every component starts with
"use client", so Next.js App Router pages can render them, and the@ouronamespace serves the newest release instead ofmain.[^upgrade-v0.3.1] -
After v0.7.0: a required field's
*is drawn with CSS on a new element around the label and is no longer a<span data-ouro-required>inside it, so tests that expected the label text "Email*" now find "Email".[^upgrade-v0.8.0] The clear, show-password and choose-date buttons in TextField and DatePicker are named by hidden text, notaria-label, so replacebutton[aria-label="…"]selectors with[data-ouro-clear],[data-ouro-show-password]or[data-ouro-choose-date]. The same goes for a file's remove button in FileField ([data-ouro-file-remove]), Combobox's clear and chip remove buttons ([data-ouro-combobox-clear],[data-ouro-combobox-chip-remove]) and NumberField's steppers ([data-ouro-increment],[data-ouro-decrement]). A remove button's hidden text holds the file or item name, so a loosegetByText("pasta.png")now finds it too; pass{ exact: true }.FieldLabel'sclassNamenow goes on the new element around the label, which also carries[data-ouro-required], and a group's labelidis on the inner<span>;style,id,data-*and handlers still go on the<label>. A numericsizeon TextField is now a type error and a development warning; set a width withclassName="w-40".FormErrorsvalues are nowErrorMessages | null | undefined, so code that readserrors.emailasstring | string[]no longer type checks until it handlesnulland React nodes.[^upgrade-v0.8.0] For action answers, importformErrorsfrom@/lib/ouro/form-answerand passerrors={formErrors(actionData, actionData?.errors)}from the initial render; this replaces the unreleased separateresultprop. It identifies equal new answers while keeping cleared fields masked on pending/client renders and rendering current uncleared messages. Cached/reused answers need{ key: acceptedAnswerKey }, changed on actual acceptance, not submit start. Raw records remain compatible but content-only. Reconstruct the value beside Form after JSON/spread/cloning or server-component transport: only messages serialize, not private identity; a safely transported acceptance key can identify the locally reconstructed answer. On a phone, a Link that is not in running text is now a box at least 44px tall and wide that takes its room in the layout, so a column of links grows; passinlineto a link in a sentence inside your own<div>or<span>.[^upgrade-link-box] A Form's top message element exists only while there is a message. Component roots no longer setm-0, so a parent'sspace-y-*now spaces them; remove any margin you added to work around that.[^upgrade-v0.8.0] -
v0.8.0: no component installs
ouro-base; add it once (step 1 does), and later adds leave your stylesheet alone.[^upgrade-v0.8.0] A component installs only the helpers it imports, one item per file inlib/ouro, and a-testsitem brings the tests of everything its component installs.[^upgrade-v0.8.0] The-testsitems no longer set a version for Vitest, Storybook or Playwright.[^upgrade-test-versions] The specs importtestandexpectfrome2e/ouro-fixtures.ts, a file of yours (step 1).[^upgrade-v0.8.0]lib/ouro/use-dev-warnings.tsis nowlib/ouro/use-dev-messages.ts(step 4).[^upgrade-v0.8.0] Earlier releases installed every helper inlib/ourowith any component, and an update deletes nothing: audit references before deleting unused historical helpers and their own tests (step 5).[^upgrade-v0.8.0]OuroProvidernow comes only with the components that render links and withtooltip; your copy stays, and a new app addslib-ouro-providerby name. -
After v0.8.2:
ouroTestProjects({ page })also sizes the touch project's Playwright host, preventing Vitest 4 from scaling landscape touch frames.[^upgrade-touch-host] The logical touch viewport still starts at 390x844 with mobile/touch input, but an explicitly enlarged host changes outer-window/screen dimensions and screen orientation; do not treat those as hardware-phone measurements.[^upgrade-touch-host] Withoutpage, the touch host stays 390x844.[^upgrade-touch-host] On Vitest 4, retain a host large enough for every requested test viewport, such as the Testing guide's 1600x1200 example.[^upgrade-touch-host] -
v1.0.0: the first stable release keeps the component APIs, theme tokens, defaults and test-tool minimums from v0.8.4.[^upgrade-v1.0.0] An app already on v0.8.4 needs no component API migration: review the update with
--dry-runand--diff, then overwrite only the items it already uses and reapply its local edits.[^upgrade-v1.0.0] Runtime components and their optional-testsitems remain separate; keep your app-owned fixtures and test configuration. Apps on older releases still need the applicable migration steps above and below. -
After v1.0.2: sheets, and a Menu shown as a bottom sheet, read their shadow from
--ouro-shadow-sheet-<side>, and Checkbox's tick draws over--ouro-duration-draw, both of which only a freshouro-baseadds (step 1 adds it again); without it a sheet draws no shadow and the tick appears at once.[^upgrade-v1.1.0] Every other shadow reads shadcn's--shadow-*tokens, and headings and overlay titles--font-heading, so a theme that sets them now restyles Ouro too.[^upgrade-v1.1.0][^upgrade-theme-tokens] -
After v1.0.1:
formErrorsnow declares its already-frozen message arrays readonly.[^upgrade-v1.0.2] Keep mutableFormErrorsandErrorMessagesfor raw builders; annotate returned snapshots asFormErrorValueorReadonlyFormErrors, or let their type infer. Form, EditSheet, fielderrorprops and the normalization helper accept readonly projections. This corrects a false mutation permission, so a snapshot no longer fits a mutable raw-map annotation.[^upgrade-v1.0.2] TypeScript'sArray.isArraynarrows toany[]and can bypass readonly; unchanged runtime freezing still rejects container mutation, without deeply freezing React nodes. -
After v1.2.2:
ButtonacceptsreloadDocumentfor a document request through an internalhref, and its downloads use a plain anchor likeLink's.[^upgrade-document-download] A false or nulldownloadno longer forces document navigation; passreloadDocumentwhen a resource route needs it without a download.[^upgrade-document-download] -
After v1.2.4: Select, Menu, Combobox, Popover, Tooltip, HoverCard and NavigationMenu open their popups at the end of
<body>unlessOuroProvideris given aportalContainer, and in development each warns once when it opens outside every landmark of a page with a<main>.[^upgrade-portal-container] To keep popups in a landmark, make `const popups = useRef<HTMLDivEle...
v1.2.7
Install an item at this release; its Ouro dependencies install from the same tag. In an app with Tailwind CSS v4 and an "@/*" path alias, run shadcn's init once first, with the Base UI base:
npx shadcn@latest init --base base --preset nova --yes --no-monorepo
npx shadcn@latest add ourostack/ouro-ui/ouro-base#v1.2.7 ourostack/ouro-ui/dialog#v1.2.7The tests, stories and specs are optional, one item per component: npx shadcn@latest add ourostack/ouro-ui/dialog#v1.2.7 ourostack/ouro-ui/dialog-tests#v1.2.7, or ourostack/ouro-ui/ouro-tests#v1.2.7 for all of them. List every item with npx shadcn@latest search 'ourostack/ouro-ui#v1.2.7'.
Changes in this release
- Fix:
auditintesting/axethrows on an axe-core older than 4.13.0, naming the version it found, and theouro-testinginstall message names axe-core 4.13.0 or later among what the tests need (test, message). Before,auditran on whatever axe-core the app had: the registry names axe-core without a version, so the CLI keeps the one an app already has.
Upgrading from v0.1.0
v0.1.0 installed each component with its tests, and v0.2.0 moves some shared helpers to new files, so update every Ouro component in one go.[^upgrade-v0.1.0][^upgrade-v0.2.0] Run the commands in your app's root, the folder with components.json. They find your folders the way shadcn does, from the aliases in components.json and the paths in your tsconfig (npx shadcn@latest info shows them), so they work in a src/ or an app/ layout as written.
What changed, by version
What an app on v0.1.0 has to know beyond these steps.[^upgrade-test] The release notes list every change.[^upgrade-releases]
-
v0.2.0: each component's tests became an optional
<name>-testsitem, some helpers moved to new files (step 4), and v0.1.0's self-referential theme lines can go (step 2).[^upgrade-v0.2.0] -
v0.3.0: sheets and dialogs stack at
--ouro-layer, a z-index of 50 by default, instead of level 0, so an app element at z-index 1 to 49 that used to show above an open sheet or dialog now sits under it.[^upgrade-v0.3.0] Raise the token in a:rootrule of your own if your app has fixed bars above z-50 (see "Theme" in the README), and drop any portal z-index workaround. A controlledDialogopened by a touch or pen tap focuses its popup instead of its first input, andConfirmDialog'sfinalFocusapplies only after a confirmed action; drop workarounds for either. Every coverage hint is/* istanbul ignore … @preserve */, so a build plugin that kept the hints can go.[^upgrade-hints] A-testsitem sets Vitest to^5. -
v0.3.1: every component starts with
"use client", so Next.js App Router pages can render them, and the@ouronamespace serves the newest release instead ofmain.[^upgrade-v0.3.1] -
After v0.7.0: a required field's
*is drawn with CSS on a new element around the label and is no longer a<span data-ouro-required>inside it, so tests that expected the label text "Email*" now find "Email".[^upgrade-v0.8.0] The clear, show-password and choose-date buttons in TextField and DatePicker are named by hidden text, notaria-label, so replacebutton[aria-label="…"]selectors with[data-ouro-clear],[data-ouro-show-password]or[data-ouro-choose-date]. The same goes for a file's remove button in FileField ([data-ouro-file-remove]), Combobox's clear and chip remove buttons ([data-ouro-combobox-clear],[data-ouro-combobox-chip-remove]) and NumberField's steppers ([data-ouro-increment],[data-ouro-decrement]). A remove button's hidden text holds the file or item name, so a loosegetByText("pasta.png")now finds it too; pass{ exact: true }.FieldLabel'sclassNamenow goes on the new element around the label, which also carries[data-ouro-required], and a group's labelidis on the inner<span>;style,id,data-*and handlers still go on the<label>. A numericsizeon TextField is now a type error and a development warning; set a width withclassName="w-40".FormErrorsvalues are nowErrorMessages | null | undefined, so code that readserrors.emailasstring | string[]no longer type checks until it handlesnulland React nodes.[^upgrade-v0.8.0] For action answers, importformErrorsfrom@/lib/ouro/form-answerand passerrors={formErrors(actionData, actionData?.errors)}from the initial render; this replaces the unreleased separateresultprop. It identifies equal new answers while keeping cleared fields masked on pending/client renders and rendering current uncleared messages. Cached/reused answers need{ key: acceptedAnswerKey }, changed on actual acceptance, not submit start. Raw records remain compatible but content-only. Reconstruct the value beside Form after JSON/spread/cloning or server-component transport: only messages serialize, not private identity; a safely transported acceptance key can identify the locally reconstructed answer. On a phone, a Link that is not in running text is now a box at least 44px tall and wide that takes its room in the layout, so a column of links grows; passinlineto a link in a sentence inside your own<div>or<span>.[^upgrade-link-box] A Form's top message element exists only while there is a message. Component roots no longer setm-0, so a parent'sspace-y-*now spaces them; remove any margin you added to work around that.[^upgrade-v0.8.0] -
v0.8.0: no component installs
ouro-base; add it once (step 1 does), and later adds leave your stylesheet alone.[^upgrade-v0.8.0] A component installs only the helpers it imports, one item per file inlib/ouro, and a-testsitem brings the tests of everything its component installs.[^upgrade-v0.8.0] The-testsitems no longer set a version for Vitest, Storybook or Playwright.[^upgrade-test-versions] The specs importtestandexpectfrome2e/ouro-fixtures.ts, a file of yours (step 1).[^upgrade-v0.8.0]lib/ouro/use-dev-warnings.tsis nowlib/ouro/use-dev-messages.ts(step 4).[^upgrade-v0.8.0] Earlier releases installed every helper inlib/ourowith any component, and an update deletes nothing: audit references before deleting unused historical helpers and their own tests (step 5).[^upgrade-v0.8.0]OuroProvidernow comes only with the components that render links and withtooltip; your copy stays, and a new app addslib-ouro-providerby name. -
After v0.8.2:
ouroTestProjects({ page })also sizes the touch project's Playwright host, preventing Vitest 4 from scaling landscape touch frames.[^upgrade-touch-host] The logical touch viewport still starts at 390x844 with mobile/touch input, but an explicitly enlarged host changes outer-window/screen dimensions and screen orientation; do not treat those as hardware-phone measurements.[^upgrade-touch-host] Withoutpage, the touch host stays 390x844.[^upgrade-touch-host] On Vitest 4, retain a host large enough for every requested test viewport, such as the Testing guide's 1600x1200 example.[^upgrade-touch-host] -
v1.0.0: the first stable release keeps the component APIs, theme tokens, defaults and test-tool minimums from v0.8.4.[^upgrade-v1.0.0] An app already on v0.8.4 needs no component API migration: review the update with
--dry-runand--diff, then overwrite only the items it already uses and reapply its local edits.[^upgrade-v1.0.0] Runtime components and their optional-testsitems remain separate; keep your app-owned fixtures and test configuration. Apps on older releases still need the applicable migration steps above and below. -
After v1.0.2: sheets, and a Menu shown as a bottom sheet, read their shadow from
--ouro-shadow-sheet-<side>, and Checkbox's tick draws over--ouro-duration-draw, both of which only a freshouro-baseadds (step 1 adds it again); without it a sheet draws no shadow and the tick appears at once.[^upgrade-v1.1.0] Every other shadow reads shadcn's--shadow-*tokens, and headings and overlay titles--font-heading, so a theme that sets them now restyles Ouro too.[^upgrade-v1.1.0][^upgrade-theme-tokens] -
After v1.0.1:
formErrorsnow declares its already-frozen message arrays readonly.[^upgrade-v1.0.2] Keep mutableFormErrorsandErrorMessagesfor raw builders; annotate returned snapshots asFormErrorValueorReadonlyFormErrors, or let their type infer. Form, EditSheet, fielderrorprops and the normalization helper accept readonly projections. This corrects a false mutation permission, so a snapshot no longer fits a mutable raw-map annotation.[^upgrade-v1.0.2] TypeScript'sArray.isArraynarrows toany[]and can bypass readonly; unchanged runtime freezing still rejects container mutation, without deeply freezing React nodes. -
After v1.2.2:
ButtonacceptsreloadDocumentfor a document request through an internalhref, and its downloads use a plain anchor likeLink's.[^upgrade-document-download] A false or nulldownloadno longer forces document navigation; passreloadDocumentwhen a resource route needs it without a download.[^upgrade-document-download] -
After v1.2.4: Select, Menu, Combobox, Popover, Tooltip, HoverCard and NavigationMenu open their popups at the end of
<body>unlessOuroProvideris given aportalContainer, and in development each warns once when it opens outside every landmark of a page with a<main>.[^upgrade-portal-container] To keep popups in a landmark, makeconst popups = useRef<HTMLDivElement>(null), wrap the app in `...
v1.2.6
Install an item at this release; its Ouro dependencies install from the same tag. In an app with Tailwind CSS v4 and an "@/*" path alias, run shadcn's init once first, with the Base UI base:
npx shadcn@latest init --base base --preset nova --yes --no-monorepo
npx shadcn@latest add ourostack/ouro-ui/ouro-base#v1.2.6 ourostack/ouro-ui/dialog#v1.2.6The tests, stories and specs are optional, one item per component: npx shadcn@latest add ourostack/ouro-ui/dialog#v1.2.6 ourostack/ouro-ui/dialog-tests#v1.2.6, or ourostack/ouro-ui/ouro-tests#v1.2.6 for all of them. List every item with npx shadcn@latest search 'ourostack/ouro-ui#v1.2.6'.
Changes in this release
- Fix: Link's installed test for keyboard focus in running text rests the pointer on plain text first, so it no longer depends on where an earlier test left the pointer, which could hover the first link and fail it (test).
- Fix:
auditintesting/axemeasures a link in a block of text whose colors axe could not read, as when an open list in<main>lies over the block where axe reads its background, and settles it by axe's own rule on a screenshot: its text color or background must stand out 3:1 from its block's, or neither may differ (test). Before, it failed such a link with axe's "could not be determined because of element overlap".
Upgrading from v0.1.0
v0.1.0 installed each component with its tests, and v0.2.0 moves some shared helpers to new files, so update every Ouro component in one go.[^upgrade-v0.1.0][^upgrade-v0.2.0] Run the commands in your app's root, the folder with components.json. They find your folders the way shadcn does, from the aliases in components.json and the paths in your tsconfig (npx shadcn@latest info shows them), so they work in a src/ or an app/ layout as written.
What changed, by version
What an app on v0.1.0 has to know beyond these steps.[^upgrade-test] The release notes list every change.[^upgrade-releases]
-
v0.2.0: each component's tests became an optional
<name>-testsitem, some helpers moved to new files (step 4), and v0.1.0's self-referential theme lines can go (step 2).[^upgrade-v0.2.0] -
v0.3.0: sheets and dialogs stack at
--ouro-layer, a z-index of 50 by default, instead of level 0, so an app element at z-index 1 to 49 that used to show above an open sheet or dialog now sits under it.[^upgrade-v0.3.0] Raise the token in a:rootrule of your own if your app has fixed bars above z-50 (see "Theme" in the README), and drop any portal z-index workaround. A controlledDialogopened by a touch or pen tap focuses its popup instead of its first input, andConfirmDialog'sfinalFocusapplies only after a confirmed action; drop workarounds for either. Every coverage hint is/* istanbul ignore … @preserve */, so a build plugin that kept the hints can go.[^upgrade-hints] A-testsitem sets Vitest to^5. -
v0.3.1: every component starts with
"use client", so Next.js App Router pages can render them, and the@ouronamespace serves the newest release instead ofmain.[^upgrade-v0.3.1] -
After v0.7.0: a required field's
*is drawn with CSS on a new element around the label and is no longer a<span data-ouro-required>inside it, so tests that expected the label text "Email*" now find "Email".[^upgrade-v0.8.0] The clear, show-password and choose-date buttons in TextField and DatePicker are named by hidden text, notaria-label, so replacebutton[aria-label="…"]selectors with[data-ouro-clear],[data-ouro-show-password]or[data-ouro-choose-date]. The same goes for a file's remove button in FileField ([data-ouro-file-remove]), Combobox's clear and chip remove buttons ([data-ouro-combobox-clear],[data-ouro-combobox-chip-remove]) and NumberField's steppers ([data-ouro-increment],[data-ouro-decrement]). A remove button's hidden text holds the file or item name, so a loosegetByText("pasta.png")now finds it too; pass{ exact: true }.FieldLabel'sclassNamenow goes on the new element around the label, which also carries[data-ouro-required], and a group's labelidis on the inner<span>;style,id,data-*and handlers still go on the<label>. A numericsizeon TextField is now a type error and a development warning; set a width withclassName="w-40".FormErrorsvalues are nowErrorMessages | null | undefined, so code that readserrors.emailasstring | string[]no longer type checks until it handlesnulland React nodes.[^upgrade-v0.8.0] For action answers, importformErrorsfrom@/lib/ouro/form-answerand passerrors={formErrors(actionData, actionData?.errors)}from the initial render; this replaces the unreleased separateresultprop. It identifies equal new answers while keeping cleared fields masked on pending/client renders and rendering current uncleared messages. Cached/reused answers need{ key: acceptedAnswerKey }, changed on actual acceptance, not submit start. Raw records remain compatible but content-only. Reconstruct the value beside Form after JSON/spread/cloning or server-component transport: only messages serialize, not private identity; a safely transported acceptance key can identify the locally reconstructed answer. On a phone, a Link that is not in running text is now a box at least 44px tall and wide that takes its room in the layout, so a column of links grows; passinlineto a link in a sentence inside your own<div>or<span>.[^upgrade-link-box] A Form's top message element exists only while there is a message. Component roots no longer setm-0, so a parent'sspace-y-*now spaces them; remove any margin you added to work around that.[^upgrade-v0.8.0] -
v0.8.0: no component installs
ouro-base; add it once (step 1 does), and later adds leave your stylesheet alone.[^upgrade-v0.8.0] A component installs only the helpers it imports, one item per file inlib/ouro, and a-testsitem brings the tests of everything its component installs.[^upgrade-v0.8.0] The-testsitems no longer set a version for Vitest, Storybook or Playwright.[^upgrade-test-versions] The specs importtestandexpectfrome2e/ouro-fixtures.ts, a file of yours (step 1).[^upgrade-v0.8.0]lib/ouro/use-dev-warnings.tsis nowlib/ouro/use-dev-messages.ts(step 4).[^upgrade-v0.8.0] Earlier releases installed every helper inlib/ourowith any component, and an update deletes nothing: audit references before deleting unused historical helpers and their own tests (step 5).[^upgrade-v0.8.0]OuroProvidernow comes only with the components that render links and withtooltip; your copy stays, and a new app addslib-ouro-providerby name. -
After v0.8.2:
ouroTestProjects({ page })also sizes the touch project's Playwright host, preventing Vitest 4 from scaling landscape touch frames.[^upgrade-touch-host] The logical touch viewport still starts at 390x844 with mobile/touch input, but an explicitly enlarged host changes outer-window/screen dimensions and screen orientation; do not treat those as hardware-phone measurements.[^upgrade-touch-host] Withoutpage, the touch host stays 390x844.[^upgrade-touch-host] On Vitest 4, retain a host large enough for every requested test viewport, such as the Testing guide's 1600x1200 example.[^upgrade-touch-host] -
v1.0.0: the first stable release keeps the component APIs, theme tokens, defaults and test-tool minimums from v0.8.4.[^upgrade-v1.0.0] An app already on v0.8.4 needs no component API migration: review the update with
--dry-runand--diff, then overwrite only the items it already uses and reapply its local edits.[^upgrade-v1.0.0] Runtime components and their optional-testsitems remain separate; keep your app-owned fixtures and test configuration. Apps on older releases still need the applicable migration steps above and below. -
After v1.0.2: sheets, and a Menu shown as a bottom sheet, read their shadow from
--ouro-shadow-sheet-<side>, and Checkbox's tick draws over--ouro-duration-draw, both of which only a freshouro-baseadds (step 1 adds it again); without it a sheet draws no shadow and the tick appears at once.[^upgrade-v1.1.0] Every other shadow reads shadcn's--shadow-*tokens, and headings and overlay titles--font-heading, so a theme that sets them now restyles Ouro too.[^upgrade-v1.1.0][^upgrade-theme-tokens] -
After v1.0.1:
formErrorsnow declares its already-frozen message arrays readonly.[^upgrade-v1.0.2] Keep mutableFormErrorsandErrorMessagesfor raw builders; annotate returned snapshots asFormErrorValueorReadonlyFormErrors, or let their type infer. Form, EditSheet, fielderrorprops and the normalization helper accept readonly projections. This corrects a false mutation permission, so a snapshot no longer fits a mutable raw-map annotation.[^upgrade-v1.0.2] TypeScript'sArray.isArraynarrows toany[]and can bypass readonly; unchanged runtime freezing still rejects container mutation, without deeply freezing React nodes. -
After v1.2.2:
ButtonacceptsreloadDocumentfor a document request through an internalhref, and its downloads use a plain anchor likeLink's.[^upgrade-document-download] A false or nulldownloadno longer forces document navigation; passreloadDocumentwhen a resource route needs it without a download.[^u...
v1.2.5
Install an item at this release; its Ouro dependencies install from the same tag. In an app with Tailwind CSS v4 and an "@/*" path alias, run shadcn's init once first, with the Base UI base:
npx shadcn@latest init --base base --preset nova --yes --no-monorepo
npx shadcn@latest add ourostack/ouro-ui/ouro-base#v1.2.5 ourostack/ouro-ui/dialog#v1.2.5The tests, stories and specs are optional, one item per component: npx shadcn@latest add ourostack/ouro-ui/dialog#v1.2.5 ourostack/ouro-ui/dialog-tests#v1.2.5, or ourostack/ouro-ui/ouro-tests#v1.2.5 for all of them. List every item with npx shadcn@latest search 'ourostack/ouro-ui#v1.2.5'.
Changes in this release
- New:
OuroProvider'sportalContainersets where Select, Menu, Combobox, Popover, Tooltip, HoverCard and NavigationMenu open their popups, so a<div ref={popups} />inside<main>keeps them in a landmark (element or ref, nested popups). - New:
portalContainer={null}on an innerOuroProvidergives popups Base UI's own place again, for a dialog the app draws itself (test). - New: in development, a popup that opens outside every landmark of a page with a
<main>warns once with that fix (warning). - Fix: Inspector's X-ray outlines step around text and its name tags sit clear of text and of each other, and its bar is a region named by its label (readable text, landmark).
- Fix: an open Combobox list no longer hides the rest of the page from screen readers (test).
- Fix: Command hides its empty list while nothing matches, leaving only the empty state (test).
- Fix: SidebarLayout's
headerrenders in a<header>, the page's banner landmark (test). - Fix: Chart's axis labels use the theme's muted text color again, not recharts' gray, which failed contrast in dark mode (test).
- New:
auditintesting/axefails every violation at any impact and every result axe could not decide unless it measures it, in both color schemes; it measures text contrast from screenshots, including SVG text, letters set on a curve, decorative text hidden from assistive technology and text under open popups (any impact, measured contrast, curve, hidden text). - Fix:
claims-check.mjs lint --prand--stdinfetch a link that only the pull request's text cites, instead of asking for a cache entry the full check then rejects (test). - Fix: ColorSchemeToggle's tests wait for the menu to open and for the page to switch before checking it (test).
- Fix: a Link in running text marks keyboard focus with a thick underline in the ring's color, instead of an outline that covered the next character, such as a comma (test).
- Fix: the docs' prop and parameter tables show only the columns they fill, and a page has no empty table (test).
Upgrading from v0.1.0
v0.1.0 installed each component with its tests, and v0.2.0 moves some shared helpers to new files, so update every Ouro component in one go.[^upgrade-v0.1.0][^upgrade-v0.2.0] Run the commands in your app's root, the folder with components.json. They find your folders the way shadcn does, from the aliases in components.json and the paths in your tsconfig (npx shadcn@latest info shows them), so they work in a src/ or an app/ layout as written.
What changed, by version
What an app on v0.1.0 has to know beyond these steps.[^upgrade-test] The release notes list every change.[^upgrade-releases]
- v0.2.0: each component's tests became an optional
<name>-testsitem, some helpers moved to new files (step 4), and v0.1.0's self-referential theme lines can go (step 2).[^upgrade-v0.2.0] - v0.3.0: sheets and dialogs stack at
--ouro-layer, a z-index of 50 by default, instead of level 0, so an app element at z-index 1 to 49 that used to show above an open sheet or dialog now sits under it.[^upgrade-v0.3.0] Raise the token in a:rootrule of your own if your app has fixed bars above z-50 (see "Theme" in the README), and drop any portal z-index workaround. A controlledDialogopened by a touch or pen tap focuses its popup instead of its first input, andConfirmDialog'sfinalFocusapplies only after a confirmed action; drop workarounds for either. Every coverage hint is/* istanbul ignore … @preserve */, so a build plugin that kept the hints can go.[^upgrade-hints] A-testsitem sets Vitest to^5. - v0.3.1: every component starts with
"use client", so Next.js App Router pages can render them, and the@ouronamespace serves the newest release instead ofmain.[^upgrade-v0.3.1] - After v0.7.0: a required field's
*is drawn with CSS on a new element around the label and is no longer a<span data-ouro-required>inside it, so tests that expected the label text "Email*" now find "Email".[^upgrade-v0.8.0] The clear, show-password and choose-date buttons in TextField and DatePicker are named by hidden text, notaria-label, so replacebutton[aria-label="…"]selectors with[data-ouro-clear], `[data-ouro-show-passwor...