---
title: "v0.3.0"
description: "Six built-in themes with a switcher; htmx-ui-upgrade, one command that migrates a project between versions; clipboard, timeline and password input components; copy buttons that work everywhere; scroll-preserving htmx extensions; a dev server that reflects every change; and components rebuilt on shared utilities."
url: "https://susonwaiba.github.io/htmx-ui/docs/changelog/0.3.0"
section: "Project"
---

# v0.3.0

Six built-in themes with a switcher; htmx-ui-upgrade, one command that migrates a project between versions; clipboard, timeline and password input components; copy buttons that work everywhere; scroll-preserving htmx extensions; a dev server that reflects every change; and components rebuilt on shared utilities.

[All releases](https://susonwaiba.github.io/htmx-ui/docs/changelog) Latest 2026-10-08

## Breaking changes

[`npx htmx-ui-upgrade@latest`](https://susonwaiba.github.io/htmx-ui/docs/upgrading) makes these edits for you, and lists what it couldn't.

One change needs an edit, and only in hand-written code blocks; several component classes also moved onto shared button markup (see [Components reuse each other](#reuse)).

### Code block copy buttons are clipboard buttons

`initCode()` is gone. The `code()`, `code_block()`, `code_tabs()` and `cli()` macros render the new button for you. In hand-written blocks, replace the old button with an icon-only [clipboard](https://susonwaiba.github.io/htmx-ui/docs/components/clipboard) button holding `.clipboard-idle` and `.clipboard-done` icons:

- Before: `<button class="btn btn-ghost btn-xs code-copy" data-copy>…</button>`
- After: `<button type="button" class="btn btn-ghost btn-xs btn-icon clipboard code-copy" data-clipboard data-clipboard-target="pre:not([hidden]) code" aria-label="Copy code">…</button>`

`.code-copy-idle`, `.code-copied` and `data-copy-label` are gone. `initComponents()` runs the clipboard, so drop any direct `initCode()` call. See [Code block](https://susonwaiba.github.io/htmx-ui/docs/components/code-block#markup) for the full markup.

## Added

### Components

- [Clipboard](https://susonwaiba.github.io/htmx-ui/docs/components/clipboard): copy buttons on any button variant and size, with an icon, a label or both, that swap to a green tick for two seconds without changing width, and copy tags. It copies a fixed text, the nearest element matching `data-clipboard-target` (its value or text), or a URL's text (`data-clipboard-url`). It uses the Clipboard API with an `execCommand` fallback for plain http, older Safari and iframes, is announced to screen readers, fires `clipboard:copy` and `clipboard:error`, and comes with `clipboard()` and `clipboard_tag()` macros.
- [Timeline](https://susonwaiba.github.io/htmx-ui/docs/components/timeline): CSS only, with dot, icon, number or avatar markers in five colours and outline, a current step, opposite content, alternating sides, vertical or horizontal, dashed or dotted lines, and three sizes.
- [Password input](https://susonwaiba.github.io/htmx-ui/docs/components/password-input): a show / hide toggle on an input group (an eye icon or Show / Hide text, or a checkbox for several fields). It keeps the caret, hides the password again on submit so password managers save it, hides Edge's own reveal button, has an optional Caps Lock warning, and a `password_input()` macro.

### Scroll position across swaps

- [preserve-scroll](https://susonwaiba.github.io/htmx-ui/docs/preserve-scroll), an htmx extension: `registerPreserveScroll()`, then `hx-preserve-scroll="<key>"` on any number of scroll containers keeps their position when htmx replaces them, boosted navigations included. Repeated keys match in order; `snapshotScroll()` and `restoreScroll()` do the same for DOM you replace yourself.
- [sidebar-active-scroll](https://susonwaiba.github.io/htmx-ui/docs/preserve-scroll#sidebar), for navigation sidebars: `hx-sidebar-active-scroll` keeps the position across swaps and the current page's link in view, centred on a fresh page load (a link opened in a new tab, a reload) and scrolled just into view after a swap that hid it. The docs sidebar uses it.

### Copying everywhere

- Every copy button is now a clipboard button that shows a green tick for two seconds: icon only on code blocks and the docs demos, icon and text on the docs plugin's "Copy Markdown" button.
- "Copy Markdown" reads "Copied" after copying and fetches the page's `.md` through `data-clipboard-url`, so it needs no plugin client. `initMarkdownCopy()` from `htmx-ui-plugin-docs/client` stays, deprecated, for pages built with the older markup.
- Copying from a code block now works over plain http too.

### Themes

- **Six built-in themes** as `htmx-ui/themes/<name>.css`: **aurora** (violet→indigo→cyan — the one this site runs on), **ember** (amber→orange→rose on warm paper), **lagoon** (teal→cyan→sky), **dusk** (pink→fuchsia→purple), **jade** (emerald→teal→lime) and **onyx** (monochrome graphite and silver). Each redefines the brand tokens — `--radius`, `--primary`, `--ring`, `--brand-gradient` — plus dark `--background`, `--surface` and `--border`. Opt-in both ways: no import, no CSS; no attribute, no styles. A switcher ships with them — buttons carrying `data-theme-palette` apply their palette and remember the choice (`initTheme()` wires them; `getPalette()`/`setPalette()` are exported) and the `theme-swatch` dot draws the current one. See [Theming](https://susonwaiba.github.io/htmx-ui/docs/theming#built-in-themes) for the mechanism and [Themes](https://susonwaiba.github.io/htmx-ui/docs/themes) for the catalogue — every theme previewed, its exact palette, a one-click apply.

### Docs

- [Input OTP](https://susonwaiba.github.io/htmx-ui/docs/components/input-otp) is documented for visible 4-digit authenticator codes and PINs, next to 6-digit codes.

## Changed

### The select() macro covers what the component does

The docs use it wherever they had a native `<select>` (field, button group, pagination, prompt input). New options:

- `id` on the trigger (for `<label for>`), `labelledby`, `describedby`, `size`, `align`, `side`, `invalid`, `required` and `class`.
- Option groups (`{label, options}`) and per-option `disabled`, `icon` and `keywords`. `attrs` also takes a dict.
- The preselected `value` is written into the trigger and the hidden field, so it shows and submits before JavaScript runs.

The [select's search](https://susonwaiba.github.io/htmx-ui/docs/components/select#search) now filters like the combobox's (accents, every word, `data-keywords`, empty groups hidden), and typing a letter in a list without a search box jumps to the next option starting with it. Button groups and `.prompt-input-select` accept the select component as well as a native select.

### Components reuse each other

- **Shared CSS:** shared looks are Tailwind utilities in the new `utilities.css`: `menu-item`, `menu-label`, `menu-separator`, `menu-empty`, `popup-surface`, `focus-outline`, `input-bare`, `label-text`, `description-text`, `choice-card`, `panel-title`, `chevron-mask`, `summary-trigger` and `details-animate`. Select, combobox, command, dropdown, prompt input, popover, hover card, navigation menu, label and field, the radio card, accordion, reasoning and chain of thought are built on them.
- **Button markup:** close buttons (dialog, sheet, alert), the code block's copy button, the sidebar trigger and the message scroller's jump button are `.btn` buttons. `.dialog-close`, `.sheet-close`, `.alert-close`, `.code-copy`, `.sidebar-trigger` and `.message-scroller-jump` now only position them. Add `btn btn-ghost btn-icon btn-sm` (copy: `btn btn-ghost btn-xs`; jump: `btn btn-outline btn-sm btn-rounded`) to hand-written markup. This also gives the alert's close and the copy button a focus ring.
- **Select trigger:** `.select-trigger` shares the `.input` rules, including `.input-warning` and `.input-success`.
- **Code tabs:** code block tabs are `.tabs-list.tabs-line.code-tabs` with `.tabs-trigger` buttons. `.code-tab` is gone.
- **Item separator:** `.item-separator` is gone; use `.separator`.
- **Reasoning and typing:** `.message-reasoning` and `.message-typing` are gone; use the [reasoning](https://susonwaiba.github.io/htmx-ui/docs/components/reasoning) component and `loader("typing")`. `reasoning()` renders its streaming label with `loader()`, and `loader(…, label=none)` on a text variant drops `role="status"`.
- **Toast icons** come from `src/icons`.

### Shared behaviour helpers

New modules in `packages/ui/src/utils/`:

- `listbox.ts`: option filtering and the `activeDescendant()` highlight, used by combobox, command and prompt input, and filtering in select.
- `dismiss.ts`: closing on an outside pointer down or when focus leaves.
- `position.ts`: `place()`, which flips a popup that would leave the viewport.
- `shared.ts`: ids, arrow-key indexes, small checks.
- `hotkey.ts`: `matchesHotkey`, still exported from `htmx-ui`.

As a result:

- Select, combobox and popover lists flip when they would leave the viewport, and so does the dropdown's own menu (before, only submenus did).
- Tabs and toggle groups swap ← and → in right-to-left layouts.
- The command menu's filter ignores accents.
- The listboxes scroll their own list, never the page.

### Also changed

- `dismissible` reacts to any `[data-dismiss]` inside it, including buttons added later, and fires a cancelable `dismissible:dismiss`. It sets `data-state="closing"` and waits for a transition before removing the element.
- Macro options are consistent. Every component macro takes `class=""` and `attrs` as a dict, through `macros/attrs.html`; string `attrs` still work. `alert`, `dialog`, `drawer`, `sheet`, `empty`, `code_block`, `code_tabs`, `cli`, `checkbox` and `input_otp` gained the options they lacked, and `checkbox` gained `invalid`.
- A new `--overlay` token (`bg-overlay`) dims the page behind modals, sheets, drawers and the mobile sidebar. Accordion, reasoning, chain of thought, collapsible, progress, toast and sidebar transitions use the [motion tokens](https://susonwaiba.github.io/htmx-ui/docs/theming#motion), so they stop under reduced motion.
- htmx-ui-plugin-search's palette is htmx-ui's [command menu](https://susonwaiba.github.io/htmx-ui/docs/components/command) in a dialog (`.dialog.command-dialog`, `.command-*`); its stylesheet only sizes it. `.search-field`, `.search-input`, `.search-group` and `.search-message` are gone.
- htmx-ui-plugin-versions' old-version banner shows the alert's warning icon.

## Fixed

- [Switching docs versions](https://susonwaiba.github.io/htmx-ui/docs/versions) loads the target page instead of swapping the body under the version you are leaving. Version links carry `hx-boost="false"` — the switcher's items, the ones its client rebuilds from the manifest, the old-version banner, and the links on the versions page — because every version brings its own theme, styles and scripts. Archiving bakes it into a snapshot, whose `<body>` carries `hx-boost="false"` as well: nothing inside a frozen page (its logo, footer, sidebar or content links) may boost the live site's code into it. The published v0.1 and v0.2 snapshots carry it all, and [`htmx-ui-upgrade`](https://susonwaiba.github.io/htmx-ui/docs/upgrading) adds the attribute to a hand-written switcher.
- Pages your server renders get their scripts and styles. A page from `site.render()` (or from `handle()` in development) linked its sources, such as `/app.ts`, which nothing served. In development they are now bundled from source on request, kept in memory and reloaded on change, never written to `dist/`; in production they link the build's bundles, from the manifest `htmx-ui build` now writes. See [Development and production](https://susonwaiba.github.io/htmx-ui/docs/servers#dev-prod).
- `NODE_ENV=production` works on Node: the engine's compiled code had the value it was built with baked in, so the adapters on Node always behaved as in development.
- The Express, Fastify and Koa adapters stream responses, so one that never ends (server-sent events) reaches the client.
- Server adapters in development show template edits on the next request, find pages added since the server started, and serve `public/` before a build has copied it.
- `htmx-ui dev` on Bun shows every change without a restart. Edits to a linked package (a workspace package, `file:` or `link:` dependency) were picked up once and then missed, and saves that replace the file were always missed; stylesheets that a Tailwind stylesheet `@import`s now reload the page; changes to `htmx-ui.config.ts`, plugins, the mock API or the engine now reach rendered pages; and a new page gets its route without a restart. If Bun's dev server crashes, `htmx-ui dev` starts it again and open pages reload. See [watching](https://susonwaiba.github.io/htmx-ui/docs/engine#roots-watch).
- [Carousel](https://susonwaiba.github.io/htmx-ui/docs/components/carousel) previous and next buttons respond to clicks again. A pressed `.btn` nudged down with `translate-y-px`, which replaced the buttons' own centring, so they jumped out from under the pointer. The press nudge is now a `transform`, which adds to `translate-*` positioning. New [`.btn-no-press`](https://susonwaiba.github.io/htmx-ui/docs/components/button#press) turns the nudge off.
- Scroll areas inside components show their scrollbar without hovering: popup lists and menus (select, combobox, dropdown, command, popover…), `.command-list` and `.card-scroll` use `--scrollbar-thumb` through the new `scrollbar-visible` utility, so a long list looks scrollable. Other [scrollbars](https://susonwaiba.github.io/htmx-ui/docs/theming#scrollbars) still appear on hover.
- The page no longer shifts sideways when a modal dialog, sheet or drawer opens: `html` reserves its scrollbar's room (`scrollbar-gutter: stable`), so hiding the scrollbar to lock the page doesn't widen it.
