# oriUI > A layered Vue 3 UI library: styled components (@oriui/vue), a headless behavior layer (@oriui/headless, with @oriui/headless/vue bindings), and a standalone, framework-free CSS layer (@oriui/css) woven around shared design tokens. Single-class token utilities, zero-runtime theming. ## Documentation Sets - [oriUI — full documentation](https://oriui.vercel.app/llms-full.txt): Every oriUI documentation page concatenated, for single-fetch consumption. ## Overview What oriUI is, how to install it, get started, and the accessibility contract. - [Introduction](/overview/introduction) - [Applicability](/overview/applicability) - [Comparisons](/overview/comparisons) - [Installation](/overview/installation) - [Get started](/overview/get-started) - [Cheat sheet](/overview/cheat-sheet) - [Accessibility](/overview/accessibility) ## Guides The standalone CSS layer, design tokens, theming, and customization. - [Using the CSS layer](/guides/css) - [Design tokens](/guides/design-tokens) - [Theming](/guides/theming) - [Skin gallery](/guides/skins) - [Customization](/guides/customization) ## Components The 34 styled components — each page has the class table, props, slots, and a11y. - [Accordion](/components/accordion) - [Alert](/components/alert) - [Avatar](/components/avatar) - [Badge](/components/badge) - [Button](/components/button) - [Card](/components/card) - [Checkbox](/components/checkbox) - [Color picker](/components/color-picker) - [Combobox](/components/combobox) - [Dialog](/components/dialog) - [Divider](/components/divider) - [Field](/components/field) - [Icon](/components/icon) - [Input](/components/input) - [Join](/components/join) - [Kbd](/components/kbd) - [Link](/components/link) - [Menu](/components/menu) - [Popover](/components/popover) - [Progress](/components/progress) - [Radio](/components/radio) - [Select](/components/select) - [Skeleton](/components/skeleton) - [Slider](/components/slider) - [Spinner](/components/spinner) - [Stack](/components/stack) - [Surface](/components/surface) - [Switch](/components/switch) - [Tabs](/components/tabs) - [Tag](/components/tag) - [Textarea](/components/textarea) - [Toast](/components/toast) - [Toolbar](/components/toolbar) - [Tooltip](/components/tooltip) ## Headless The framework-agnostic behavior contract and the Vue composables. - [Core (@oriui/headless)](/headless/core) - [useDisclosure](/headless/use-disclosure) - [useDialog](/headless/use-dialog) - [useCombobox](/headless/use-combobox) - [useMenu](/headless/use-menu) - [useToolbar](/headless/use-toolbar) - [useTabs](/headless/use-tabs) - [useToast](/headless/use-toast) - [useDismissable](/headless/use-dismissable) - [useColorPicker](/headless/use-color-picker) - [useToken](/headless/use-token) - [useTheme](/headless/use-theme) ## Docs - [Accordion](https://oriui.vercel.app/raw/components/accordion.md): A disclosure list built entirely on native
/ elements — zero JavaScript, zero ARIA reinvention. The browser supplies the disclosure role, aria-expanded, Enter/Space toggling, focus, and find-in-page expansion for free. Single-open (exclusive) mode uses the platform exclusive-accordion feature (Baseline 2024): every
shares one auto-generated name, so the browser closes siblings when one opens. multiple drops the shared name so each item opens independently. - [Alert](https://oriui.vercel.app/raw/components/alert.md): A styled, accessible notification banner. Its live-region politeness is derived from the color — urgent danger / warn render role="alert" (assertive), everything else role="status" (polite) — so a non-urgent banner isn't announced assertively (override with the live prop). It supports an optional icon, a title, body text, an actions row, and a dismiss button. - [Avatar](https://oriui.vercel.app/raw/components/avatar.md): A data-display component that shows an image, or initials derived from text when there is no image (or while it loads). Supports a title / subtitle column for list and profile UIs. - [Badge](https://oriui.vercel.app/raw/components/badge.md): A small status or count indicator. Attach it to any element as a floating pip, drop it inline next to text, or render it standalone — all three modes share the same class surface. - [Button](https://oriui.vercel.app/raw/components/button.md): A styled, accessible button. Dynamic state is expressed through real attributes — disabled becomes a true disabled (or aria-disabled for link buttons), loading sets aria-busy, and it ships a visible :focus-visible ring. - [Card](https://oriui.vercel.app/raw/components/card.md): A surface container for grouping related content — a header (prepend slot · title · subtitle · append slot), a body, and optional action rows. It is the most composed styled component, yet the standalone markup is just nested .ori-card__* elements, so it works without Vue. - [Checkbox](https://oriui.vercel.app/raw/components/checkbox.md): A styled, accessible checkbox built on a real — kept in the DOM (visually hidden over a custom box) so keyboard, focus, and native form submission work for free. The accent color and :focus-visible ring are driven by the ori-color token; invalid sets aria-invalid; disabled is the native attribute. - [Color picker](https://oriui.vercel.app/raw/components/color-picker.md): An accessible color picker: a 2D saturation × brightness area, a hue slider, a hex field, and optional preset swatches — an inline panel you compose into an OriPopover for a swatch-triggered flow. Behaviour lives in the framework-agnostic useColorPicker (a zero-dependency sRGB + 2D-area engine, no adapter or machine); the styled component reuses OriSlider for the hue channel and OriInput for the hex field. v-model carries a lowercase color string; the internal working model is HSVA, so the hue survives when you drag into a corner and back. - [Combobox](https://oriui.vercel.app/raw/components/combobox.md): A filterable single-select listbox — type to filter, navigate with the keyboard, pick one. It is the first component driven end-to-end by @oriui/headless: the state machine + prop-getters own the behaviour (open/close, filtering, the active-descendant highlight, selection, and the full WAI-ARIA keyboard), and OriCombobox renders the styled shell on top. Unlike the pure-CSS components, the behaviour needs JavaScript — the standalone classes give you the look, and useCombobox gives you the behaviour for any UI. - [Dialog](https://oriui.vercel.app/raw/components/dialog.md): A modal dialog — the library's first genuinely interactive component, built on the native element. Unlike the styled-only components, a dialog needs behaviour: focus trap, scroll lock, Esc-to-close, click-outside dismissal, and the full WAI-ARIA keyboard contract. All of it comes from the platform via showModal() — no state-machine dependency and no adapter to wire. The styled OriDialog supplies only the markup and tokens. - [Divider](https://oriui.vercel.app/raw/components/divider.md): A horizontal (default) or vertical rule that separates content. An optional centered label turns it into a visual break like the classic "OR" between form sections. A pure layout/structure primitive — no interactive behaviour, no variants, no size scale. - [Field](https://oriui.vercel.app/raw/components/field.md): The shared shell for form controls — one source of truth for the label / hint / error / required contract that Input, Select, Textarea, Combobox, Slider, RadioGroup, and ColorPicker otherwise each wire by hand. Wrap a control in OriField and it adopts the field's id, aria-describedby, aria-invalid, required, disabled, and size — and stops rendering its own label and helper, so there is exactly one of each, wired identically every time. Group and composite controls (RadioGroup, Combobox's listbox, ColorPicker) name themselves via aria-labelledby pointing at the field's label. - [Icon](https://oriui.vercel.app/raw/components/icon.md): A styled, accessible SVG icon. Decorative by default — renders with aria-hidden="true" and no role. Pass a label to expose it to assistive technology as an image: the icon switches to role="img" + aria-label automatically. - [Input](https://oriui.vercel.app/raw/components/input.md): A labelled, accessible text field with v-model. State is expressed through real attributes — the native disabled, aria-invalid when there is an error, and an aria-describedby that points at the rendered hint or error. The label is wired to the field with for/id (auto-generated via useId when you don't pass one), and arbitrary native attributes (name, autocomplete, inputmode, …) fall through to the underlying . - [Join](https://oriui.vercel.app/raw/components/join.md): A thin wrapper that collapses the shared radii and borders of adjacent controls so they read as one segmented unit — button groups, input+button combos, segmented toolbars. No visual styling of its own: all colour, size, and variant tokens live on the children. - [Kbd](https://oriui.vercel.app/raw/components/kbd.md): A keyboard-key chip — a semantic element styled as a monospace bordered chip. Use it to represent individual keys, modifier keys, or multi-key shortcuts inline in prose or UI hints. - [Link](https://oriui.vercel.app/raw/components/link.md): An inline prose link — the right choice when a destination lives in running text. Distinct from OriButton rendered as="a": a button that navigates is still a button; a link in a sentence is OriLink. Underlined by default (the affordance survives without color), with a _hover mode that hides the underline at rest and reveals it on hover. A visible :focus-visible ring uses the same color as the text. - [Menu](https://oriui.vercel.app/raw/components/menu.md): A WAI-ARIA menu button: a trigger opens a roving-tabindex action list. Unlike the Popover, open/close, focus movement, and click-outside dismissal are driven entirely by the @oriui/headless menu machine in JavaScript, not the Popover API — so the panel is not top-layer and carries a z-index. The panel is still placed with the same CSS Anchor Positioning primitive as the Popover (ori-anchored), zero positioning JS. - [Popover](https://oriui.vercel.app/raw/components/popover.md): A positioned overlay for supplementary content — a menu, a filter panel, a rich hint — tethered to a trigger. It runs entirely on the platform: the trigger opens the panel through the Popover API (top-layer, light-dismiss, Esc — zero JS), and the panel is placed with CSS Anchor Positioning (anchor-name / position-anchor + position-area, with a collision flip via position-try-fallbacks) — zero positioning JS, no scroll/resize listeners. Baseline 2026, with graceful degradation: an older engine still opens the panel and places it, just without the collision-aware flip. - [Progress](https://oriui.vercel.app/raw/components/progress.md): A horizontal progress bar that communicates task completion. It supports a determinate mode (a known value out of a max) and an indeterminate mode (animated sweep) for tasks with no measurable progress. State is expressed through ARIA attributes — aria-valuenow is omitted when indeterminate, giving screen readers the correct cue automatically. - [Radio](https://oriui.vercel.app/raw/components/radio.md): A "choose one" compound control. A role="radiogroup" container names the set via aria-labelledby; each option is a real sharing a single name — so the browser enforces single-select and native form submission — visually hidden behind a styled circle. Pass the options as an array; per-option disabled is supported. - [Select](https://oriui.vercel.app/raw/components/select.md): A native-first styled select control with v-model. The browser owns the dropdown, full keyboard interaction, type-ahead, and all ARIA semantics — oriUI only styles the closed control and draws a decorative chevron. It shares the form-control contract of Input and Textarea: a built-in label, hint, error, and required asterisk, all wired for accessibility. State is expressed through real attributes — native disabled and aria-invalid="true" (not classes) — and arbitrary native attrs (name, autocomplete, …) fall through to the . Because the underlying element is real HTML, the browser supplies role="slider", the full keyboard contract (arrow keys, Home/End, PageUp/PageDown), and screen-reader value announcements for free — no JavaScript bridge needed. Colour and the filled-track portion are driven by CSS custom properties; v-model carries a number. - [Spinner](https://oriui.vercel.app/raw/components/spinner.md): An accessible loading indicator — renders role="status" with an aria-label (default "Loading") so assistive technology announces the busy state without any extra work. Use it standalone for a page-level or section-level loader, or embed it inline inside a button or label. - [Stack](https://oriui.vercel.app/raw/components/stack.md): The workhorse layout primitive — two flex modes in one component. A stack (ori-stack) is a flex column: children flow top-to-bottom with a consistent gap. A cluster (ori-cluster) is a wrapping flex row: children flow left-to-right, wrap onto new lines, and are centered on the cross-axis — ideal for tag clouds, button groups, and badge sets. - [Surface](https://oriui.vercel.app/raw/components/surface.md): An elevated floating surface — a lifted panel with a surface background, an optional hairline, a corner radius, and a mode-aware shadow, and nothing else. No padding, no header/title semantics: it is just the box, and the caller owns the layout inside. It's the building block for chrome that floats over content — a toolbar island, a zoom control, a side panel, a menu popout's container — and the elevation counterpart to Card, which is a content card (padding + header / title / body / actions). - [Switch](https://oriui.vercel.app/raw/components/switch.md): An on/off toggle built on a real — kept in the DOM (visually hidden over a track + thumb) so keyboard, focus, and form submission work for free, and assistive technology announces it as a switch. v-model is a boolean. The "on" track color and the :focus-visible ring both come from the ori-color token; the track and thumb scale with ori-font-size so size is a single prop. - [Tabs](https://oriui.vercel.app/raw/components/tabs.md): An accessible tabs widget with automatic activation. The tablist holds a roving tabindex so Tab / Shift+Tab enter and leave the group as a single keyboard stop; arrow keys move between tabs and immediately select them. State is expressed through real ARIA attributes — aria-selected drives both the active indicator and the label colour; disabled is the native button attribute. - [Tag](https://oriui.vercel.app/raw/components/tag.md): A compact label for categorising, filtering, or annotating content — status chips, keyword badges, and removable filter pills. State is expressed through real attributes: aria-disabled disables the tag and its close button without removing it from the DOM. - [Textarea](https://oriui.vercel.app/raw/components/textarea.md): A labelled, accessible multiline text field with v-model. State is expressed through real attributes — the native disabled, aria-invalid when there is an error, and an aria-describedby that points at the rendered hint or error. The label is wired to the field with for/id (auto-generated via useId when you don't pass one). Unlike a single-line input the field has no fixed height — it grows from a rows-based min-height and stays user-resizable. - [Toast](https://oriui.vercel.app/raw/components/toast.md): An imperative notification system. Push a toast from anywhere with useToast() and render once near the app root to display the queue. No event bus, no Vuex, no provide/inject — just a module-level reactive queue that every call site shares. - [Toolbar](https://oriui.vercel.app/raw/components/toolbar.md): A WAI-ARIA toolbar: a set of related controls that share a single Tab stop, navigated internally with the arrow keys via roving tabindex (real DOM focus, not aria-activedescendant). Five components make it up — OriToolbar (the root, role="toolbar"), OriToolbarButton (an item — composes OriButton), OriToolbarSeparator (a perpendicular divider), and a paired OriToolbarToggleGroup / OriToolbarToggleItem for a segmented single- or multi-select toggle. Unlike the data-driven OriMenu (an items array plus a machine), a toolbar is compositional — you assemble it from slotted components in whatever order and combination you need — so the behaviour underneath, useToolbar from @oriui/headless, is a provide/inject roving-focus context rather than a state machine over an array. - [Tooltip](https://oriui.vercel.app/raw/components/tooltip.md): A CSS-driven tooltip that shows additional context when a control is focused or hovered. The bubble is always in the DOM (so the aria-describedby reference is never dangling) and shown/hidden entirely in CSS — no JS state machine, no positioning engine. Show is pure :focus-within (keyboard) and :hover wrapped in @media (hover: hover) (pointer, without sticking open on touch). - [Using the CSS layer](https://oriui.vercel.app/raw/guides/css.md): oriUI is three independent layers woven around one set of design tokens. The @oriui/css layer is just standalone .ori-* classes plus the token theming — no Vue, no build step, no Tailwind. You ship the stylesheet and write classes. This is how oriUI works with htmx, Astro, or plain HTML — the DaisyUI niche, minus Tailwind. - [Customization](https://oriui.vercel.app/raw/guides/customization.md): Make it yours — without authoring a whole skin. Most products need one thing: their brand color and their corner radius on top of accessible, ready-made components. oriUI lets you do that by repointing a handful of design tokens — a few CSS custom properties in your own stylesheet, no rewrite, no fork. This is the prototype fast, scale without rewriting payoff: the same you shipped on day one becomes on-brand by the time you ship to users, and nothing about the component changes. - [Design tokens](https://oriui.vercel.app/raw/guides/design-tokens.md): oriUI is three independent layers woven around one set of design tokens. This page is the catalog — every token, and the resolution model that turns a raw value into the colour a component paints. The chain is short and deliberate: a primitive feeds a semantic role, a role feeds a per-instance alias, and components read only the alias. That indirection is what lets a single class repoint a single token — the whole zero-runtime theming story rests on it. - [Skin gallery](https://oriui.vercel.app/raw/guides/skins.md): A skin is a named palette — a set of token overrides applied with one attribute on (data-ori-skin). Every skin repoints only the four skinnable roles (primary · secondary · surface · background, each with a -light and -dark source); the status hues (success · warn · danger · info) stay shared, and every pairing targets WCAG AA for body text. Switching a skin is a single attribute write — zero runtime, no recompute, no flash. - [Theming](https://oriui.vercel.app/raw/guides/theming.md): A theme is a mode (light or dark) layered over a skin (a named palette). Both are pure attribute toggles on — flipping either repoints the active --ori-color-* aliases through CSS custom properties, so everything reskins at once with no JavaScript, no recompute, no flash. This is the same zero-runtime story across every layer: a Vue app, the standalone @oriui/css classes, htmx fragments, or hand-written HTML all theme identically. - [Core](https://oriui.vercel.app/raw/headless/core.md): The framework-agnostic heart of the headless layer: a behaviour contract plus a zero-dependency native engine, written in vanilla TypeScript. Every framework binding consumes the same contract, so a primitive behaves identically wherever it runs — and the behaviour stays swappable per primitive, without touching markup. - [useColorPicker](https://oriui.vercel.app/raw/headless/use-color-picker.md): A headless sRGB color picker — a 2D saturation × brightness area, a hue channel, an optional alpha channel, a hex field, and optional preset swatches, assembled from ready-to-bind prop-getters. It owns the working color as an internal HSVA object (so the hue survives a grayscale round-trip when you drag into a corner and back) and composes a zero-dependency sRGB engine plus pure 2D-area math from the core: parseColor / formatColor (loose parse in, lowercase string out), hsvToRgb / rgbToHex / rgbToHsl (hex / rgb / hsv / hsl conversion), readableInk (WCAG-luminance ink over a swatch), and resolveAreaPosition / stepAreaPosition (pointer → coordinate and keyboard stepping across the two axes). - [useCombobox](https://oriui.vercel.app/raw/headless/use-combobox.md): A headless combobox primitive — an editable text input paired with a popup listbox — implementing the WAI-ARIA combobox with listbox popup pattern: type to filter, navigate with the keyboard via active-descendant, and pick a single option. It owns the behaviour (open/close, filtering, the highlight, selection, and the full keyboard) and the ARIA wiring as ready-to-bind prop bags; you own the markup and styles. - [useDialog](https://oriui.vercel.app/raw/headless/use-dialog.md): A headless modal dialog primitive built on the native element — focus trap, scroll lock, Escape to close, focus return, and aria-modal semantics all come from the platform via showModal(). It owns the open state and the ARIA wiring as ready-to-bind prop bags; you own the markup and styles (you render the and drive showModal() / close() from open). - [useDisclosure](https://oriui.vercel.app/raw/headless/use-disclosure.md): A headless show / hide primitive for disclosure widgets — expandable panels, accordions, menus, the collapsible sidebar groups on this very site. It owns the open state, the WAI-ARIA wiring, and the toggle handler; you own the markup and the styles. - [useDismissable](https://oriui.vercel.app/raw/headless/use-dismissable.md): A headless dismiss layer — the "close the overlay on an outside interaction" glue a non-platform overlay needs, the pattern Radix DismissableLayer / Floating-UI useDismiss standardise. While enabled, it attaches document listeners and calls onDismiss() when an interaction lands outside the overlay's own elements. Each overlay picks the strategy that fits it: - [useMenu](https://oriui.vercel.app/raw/headless/use-menu.md): A headless menu button primitive built on the WAI-ARIA menu-button pattern — a trigger that opens a popup menu of actions, with roving tabindex, arrow / Home / End navigation, and Enter / Space activation. It owns the open state and the highlighted item and exposes the ARIA wiring as ready-to-bind prop bags; you own the markup, the styles, and the DOM focus (roving needs real focus, and click-outside / focus-return are host concerns a state projection can't reach). - [useTabs](https://oriui.vercel.app/raw/headless/use-tabs.md): A headless WAI-ARIA tabs widget with automatic activation — a tablist of tabs behind a single Tab stop, navigated with the arrow keys via roving tabindex (real DOM focus, not aria-activedescendant), where moving focus also selects. It is data-driven: you pass the tabs array plus the selected value, and it returns the tablist prop bag plus per-tab and per-panel prop-getters (ids, aria-selected, aria-controls, aria-labelledby, roving tabindex, hidden). - [useTheme](https://oriui.vercel.app/raw/headless/use-theme.md): A headless light / dark theme controller — it owns the mode setting, resolves auto against the OS scheme live, persists the choice, and applies it to the DOM. It applies through applyTheme, which sets the ori-theme_{light,dark} class (removing the opposite — exactly one is on at a time) and works around a bug in current Chromium where a runtime theme toggle otherwise leaves styled components painting the previous theme's colours until they re-render. - [useToast](https://oriui.vercel.app/raw/headless/use-toast.md): A headless toast queue — an imperative notification store you call from anywhere (toast('Saved')) and render once near the app root. The behaviour is a framework-agnostic engine (a plain array + a Set of listeners + auto-dismiss timers) projected into the host framework's reactivity: a Vue reactive array and a Svelte readable store. It is a client-side singleton — every useToast() call shares the one queue, so a push from anywhere reaches the single renderer. - [useToken](https://oriui.vercel.app/raw/headless/use-token.md): A reactive token bridge — it resolves an --ori-* design token to its computed value in JS, for canvas / WebGL / chart painters (Konva, ECharts, …) that draw outside the CSS cascade yet must follow the active skin. It reads through a hidden probe, because the obvious API is a trap: getComputedStyle(el).getPropertyValue('--x') hands back the unresolved var() chain (oriUI aliases chain), not a color — substitution only happens at a real property's computed-value time. - [useToolbar](https://oriui.vercel.app/raw/headless/use-toolbar.md): A headless WAI-ARIA toolbar — a set of related controls behind a single Tab stop, navigated internally with the arrow keys via roving tabindex (real DOM focus, not aria-activedescendant). The root owns orientation / loop / direction and a single keydown handler; each item registers itself and receives its roving tabindex, so a toolbar is assembled from arbitrary slotted components rather than a fixed data array. - [Accessibility](https://oriui.vercel.app/raw/overview/accessibility.md): Accessibility in oriUI is structural, not a coat of polish on top. The same decisions that make the components correct for assistive technology also make them themeable and framework-agnostic — they are the same source of truth, not a separate effort. - [Applicability](https://oriui.vercel.app/raw/overview/applicability.md): oriUI is three independently-consumable layers woven around one token contract. The promise is prototype fast, scale without rewriting: reach for the styled Vue components first, drop to the headless behaviour when you need control, or drop to the standalone CSS when you leave Vue entirely — without changing your design tokens. This page maps each layer to where it runs. - [Cheat sheet](https://oriui.vercel.app/raw/overview/cheat-sheet.md): Everything in one page — install, the class model, every component, and the design tokens. For the machine-readable version (e.g. to paste into an AI), fetch /llms.txt (index) or /llms-full.txt (every page concatenated). - [Idea & comparisons](https://oriui.vercel.app/raw/overview/comparisons.md): oriUI's one-line idea — prototype fast, scale without rewriting — falls out of a single architectural choice: three layers woven around one token contract, so you can change depth (styled → headless → CSS) without changing your design system. This page sets that idea next to the libraries oriUI learns from, and is honest about where it does and doesn't compete. - [Get started](https://oriui.vercel.app/raw/overview/get-started.md): oriUI is a layered Vue 3 UI library. Install it and import only what you need. - [Installation](https://oriui.vercel.app/raw/overview/installation.md): oriUI is three layers in two packages. Install only the layer you need — they share one token contract, so you can add another later without reworking what you built. - [Introduction](https://oriui.vercel.app/raw/overview/introduction.md): oriUI (織り, ori — "weaving") is a layered Vue 3 UI library. The idea in one line: prototype fast, scale without rewriting. Start with ready-made styled components; when you need more control, drop down to the behavior or the raw CSS — without throwing away your work, because every layer is woven around the same design tokens.