Docs
htmx
Optional styles for htmx lifecycles, errors, and out-of-band updates.
EldanCSS stays classless and dependency-free. The htmx companion is a small, optional stylesheet for the classes htmx adds while a request is in flight and while content is swapped into the page.
It targets the stable class names shared by htmx 1.x and 2.x. The companion adds no JavaScript and does not require htmx extensions.
Install
Download eldan-htmx.css and link it after the core stylesheet:
<link rel="stylesheet" href="/eldan.css">
<link rel="stylesheet" href="/eldan-htmx.css">
The hosted copies work the same way as the core stylesheet:
<link rel="stylesheet" href="https://eldancss.com/eldan-htmx.css">
For a stable build, use /eldan/<version>/eldan-htmx.css. The versioned path is immutable and cached for a year.
If you want EldanCSS to own the indicator styles, turn off htmx’s built-in copies before loading htmx:
<meta name="htmx-config" content='{"includeIndicatorStyles": false}'>
The built-in rules are compatible, so this is optional. Disabling them avoids duplicate declarations and keeps the fade timings consistent.
Loading indicators
An element with htmx-indicator is transparent by default. It becomes visible when the requesting element has htmx-request, or when the indicator itself receives that class.
Use a nested indicator when you want the trigger itself to show the busy state:
<button hx-get="/notes" hx-target="#notes">
Refresh notes <span class="htmx-indicator" aria-hidden="true"></span>
</button>
<section id="notes" aria-live="polite">...</section>
An empty indicator renders a small spinner. You can also use a native progress element or your own markup:
<button hx-get="/notes" hx-target="#notes" hx-indicator="#notes-progress">
Refresh notes
</button>
<progress id="notes-progress" class="htmx-indicator" aria-label="Loading notes"></progress>
When hx-indicator points at another element, htmx puts the request class on that element instead of the button. Use a nested indicator if you want both the busy button styles and an external status indicator.
Choose “Refresh notes” to swap in a partial. The status line outside the target is updated with an out-of-band swap.
Busy state
While a request is in flight, the requesting button gets a progress cursor, dims slightly, and stops accepting pointer events. A requesting form does the same for its submit and button controls. The styles also cooperate with hx-disabled-elt and the disabled styles from the core stylesheet.
Set aria-busy="true" on the region that is being replaced when the server or your script can do so. The core stylesheet adds the animated ellipsis and a progress cursor to that region:
<form hx-post="/save" hx-target="#result" hx-disabled-elt="find button">
<label for="title">Title</label>
<input id="title" name="title">
<button type="submit">Save</button>
</form>
<section id="result" aria-live="polite"></section>
The CSS busy state prevents accidental double clicks even when you do not use hx-disabled-elt.
Swap transitions
htmx marks the outgoing target with htmx-swapping, the incoming content with htmx-added, and the target with htmx-settling after the swap. The companion fades the outgoing target out and the new content in, including out-of-band content.
htmx removes those classes on its own schedule, so match the markup delays to the CSS durations:
<button hx-get="/notes" hx-target="#notes"
hx-swap="innerHTML swap:160ms settle:200ms">
Refresh notes
</button>
swap:160ms leaves time for the fade-out and settle:200ms leaves time for the fade-in. Without them, htmx’s defaults finish almost immediately and the transitions are not visible.
The timings are custom properties:
:root {
--htmx-swap-duration: 160ms;
--htmx-settle-duration: 200ms;
--htmx-indicator-in: 120ms;
--htmx-indicator-out: 240ms;
}
If you change a duration, change the matching hx-swap option or defaultSwapDelay/defaultSettleDelay config too. All motion is skipped under prefers-reduced-motion: reduce.
Errors
htmx does not add an error class when a response has a 4xx or 5xx status. Return an error fragment from the server and mark it with htmx-error or data-htmx-error. A role="alert" announcement makes the message useful to assistive technology:
<div class="htmx-error" role="alert">
<p><strong>Could not save.</strong> Enter a valid email address.</p>
</div>
Fields marked with aria-invalid="true" get the danger border. If you use the response-targets extension, point the error status at a region and swap the same fragment into it.
No error yet.
Out-of-band swaps and partials
Return semantic fragments: a list, an article, a form, or the element that belongs at the target. The companion styles them the same way as any other swapped content. Keep ids stable so htmx can settle attribute changes and preserve focus.
An out-of-band element is swapped before the main target and gets the same fade-in treatment:
<ul>
<li>Draft the release notes.</li>
<li>Tag the release.</li>
</ul>
<p id="note-count" hx-swap-oob="true">2 entries</p>
The response above replaces the main target with the list and updates #note-count wherever it appears on the page. Table rows and cells that cannot stand alone should be wrapped in a template:
<template>
<tr id="note-row" hx-swap-oob="true">
<td>Draft the release notes.</td>
<td>Done</td>
</tr>
</template>
htmx 2 also supports <hx-partial> for server-driven multi-target swaps. It is an envelope that htmx removes before the content reaches the page, so there is nothing for the stylesheet to style. Out-of-band swaps remain the portable option for htmx 1.x and 2.x.
Use aria-live on the main target, or role="status" on an out-of-band status line, so updates are announced without moving focus.
Class names and versions
The companion uses htmx’s default class names:
| Class | Purpose |
|---|---|
htmx-indicator | Hidden until a request is in flight |
htmx-request | Set on the triggering element or the indicator |
htmx-swapping | Set on the target before content is removed |
htmx-added | Set on new content before it settles |
htmx-settling | Set on the target while new content settles |
If you change indicatorClass, requestClass, swappingClass, addedClass, or settlingClass in the htmx config, mirror the change in your own CSS. The companion does not depend on a specific htmx version.