Style package
Structure and appearance
axo-tabsLayout and visual states.axo-dialogLayer positioning and transitions.axo-drawerDrawer placement and open classes.
Behavior / Overview
data-axo-* interactive.Axoloth Style and Axoloth Behavior are deliberately separate. CSS supplies layout and presentation. The optional JavaScript package reads declarative attributes, manages accessible state, and returns controllers you can refresh or destroy.
Style package
axo-tabsLayout and visual states.axo-dialogLayer positioning and transitions.axo-drawerDrawer placement and open classes.Behavior package
data-axo-*Connects triggers, panels, and targets.
init*Attaches event listeners and ARIA state.destroy()Removes listeners during cleanup.Attributes do nothing until an initializer runs. The package never auto-initializes.
Install
npm install @quertys/axoloth-style @quertys/axoloth-behavior
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/@quertys/axoloth-style@0.9.0/src/axoloth.css"
/>
<script type="module">
import { initAxolothBehaviors } from
'https://cdn.jsdelivr.net/npm/@quertys/axoloth-behavior@0.6.0/src/index.js';
const axoloth = initAxolothBehaviors();
window.addEventListener('pagehide', () => axoloth.destroy(), { once: true });
</script>
Pin both package versions for predictable production deployments.
Initialize Everything
Call the root initializer after the DOM exists. It returns the individual controllers
plus one aggregate destroy() method.
import '@quertys/axoloth-style/axoloth.css';
import { initAxolothBehaviors } from '@quertys/axoloth-behavior';
const axoloth = initAxolothBehaviors();
// Available controllers:
axoloth.tabs.refresh();
axoloth.accordion.refresh();
// Remove every listener when this page or view is disposed.
window.addEventListener('pagehide', () => axoloth.destroy(), { once: true });
Initializing behavior demos...
Manual Initialization
Every initializer accepts an optional root. Scoping to a section keeps ownership and cleanup explicit in multi-page apps or component systems.
import { initTabs } from '@quertys/axoloth-behavior/tabs';
import { initAccordion } from '@quertys/axoloth-behavior/accordion';
import { initDialog } from '@quertys/axoloth-behavior/dialog';
import { initDrawer } from '@quertys/axoloth-behavior/drawer';
const feature = document.querySelector('#account-feature');
const controllers = [
initTabs(feature),
initAccordion(feature),
initDialog(feature),
initDrawer(feature),
];
// Call refresh() after inserting new matching markup.
controllers[0].refresh();
controllers[1].refresh();
// Clean up before replacing or unmounting the feature.
controllers.forEach((controller) => controller.destroy());
Vanilla Example 01
Matching data-axo-tab and data-axo-tab-panel values connect
each trigger to its panel. Arrow keys, Home, and End are supported.
<div id="account-tabs" class="axo-tabs" data-axo-tabs>
<div class="axo-tab-list" aria-label="Account sections">
<button class="axo-tab" type="button"
data-axo-tab="profile" aria-selected="true">Profile</button>
<button class="axo-tab" type="button"
data-axo-tab="security">Security</button>
</div>
<section class="axo-tab-panel" data-axo-tab-panel="profile">
Profile content
</section>
<section class="axo-tab-panel" data-axo-tab-panel="security">
Security content
</section>
</div>
<script type="module">
import { initTabs } from
'https://cdn.jsdelivr.net/npm/@quertys/axoloth-behavior@0.6.0/src/tabs.js';
const tabs = initTabs(document.querySelector('#account-tabs'));
window.addEventListener('pagehide', () => tabs.destroy(), { once: true });
</script>
Update the public information shown on your account.
Review passkeys and active sessions.
Vanilla Example 02
Each trigger and panel share an identifier. The default group keeps one item open and remains collapsible.
<div id="faq" class="axo-accordion" data-axo-accordion>
<section class="axo-accordion-item axo-surface">
<h3>
<button class="axo-accordion-trigger" type="button"
data-axo-accordion-trigger="install" aria-expanded="true">
How do I install it?
<span class="axo-accordion-indicator" aria-hidden="true">+</span>
</button>
</h3>
<div class="axo-accordion-panel"
data-axo-accordion-panel="install">Install both packages with npm.</div>
</section>
</div>
<script type="module">
import { initAccordion } from
'https://cdn.jsdelivr.net/npm/@quertys/axoloth-behavior@0.6.0/src/accordion.js';
const accordion = initAccordion(document.querySelector('#faq'));
window.addEventListener('pagehide', () => accordion.destroy(), { once: true });
</script>
Vanilla Example 03
The controller handles ARIA state, focus trapping, Escape, backdrop dismissal, body scroll locking, and focus return.
<div id="dialog-example">
<button type="button" class="axo-button"
data-axo-dialog-toggle="confirm-dialog"
aria-controls="confirm-dialog" aria-expanded="false">Open dialog</button>
<div id="confirm-dialog" class="axo-dialog"
data-axo-dialog-id="confirm-dialog"
aria-labelledby="confirm-title" aria-hidden="true">
<div class="axo-dialog-backdrop"
data-axo-dialog-dismiss="confirm-dialog" aria-hidden="true"></div>
<section class="axo-dialog-panel axo-surface">
<header class="axo-dialog-header">
<h2 id="confirm-title">Confirm action</h2>
</header>
<div class="axo-dialog-body">This dialog traps focus while open.</div>
<footer class="axo-dialog-footer">
<button class="axo-button" type="button"
data-axo-dialog-dismiss="confirm-dialog">Close</button>
</footer>
</section>
</div>
</div>
<script type="module">
import { initDialog } from
'https://cdn.jsdelivr.net/npm/@quertys/axoloth-behavior@0.6.0/src/dialog.js';
const dialog = initDialog(document.querySelector('#dialog-example'));
window.addEventListener('pagehide', () => dialog.destroy(), { once: true });
</script>
Open the layer, press Tab, then close it with Escape or the backdrop.
Vanilla Example 04
Matching toggle, target, and dismiss values create a temporary viewport panel with focus management and a dismissible backdrop.
<div id="drawer-example">
<button class="axo-button" type="button"
data-axo-drawer-toggle="main-drawer"
aria-controls="main-drawer" aria-expanded="false">Open drawer</button>
<aside id="main-drawer" class="axo-drawer axo-drawer-right axo-surface"
data-axo-drawer-id="main-drawer"
aria-label="Main navigation" aria-hidden="true">
<button class="axo-button" type="button"
data-axo-drawer-dismiss="main-drawer">Close</button>
<a class="axo-sidebar-item axo-link" href="#dashboard">Dashboard</a>
</aside>
<div class="axo-drawer-backdrop"
data-axo-drawer-dismiss="main-drawer"></div>
</div>
<script type="module">
import { initDrawer } from
'https://cdn.jsdelivr.net/npm/@quertys/axoloth-behavior@0.6.0/src/drawer.js';
const drawer = initDrawer(document.querySelector('#drawer-example'));
window.addEventListener('pagehide', () => drawer.destroy(), { once: true });
</script>
Open the right-side panel, then dismiss it with Escape or the backdrop.
Cleanup
Call destroy() before unmounting, replacing an initialized root, or
initializing it again. Use refresh() for newly inserted tabs or accordion
items.
const axoloth = initAxolothBehaviors(document.querySelector('#feature'));
// Later, after adding matching markup:
axoloth.tabs.refresh();
axoloth.accordion.refresh();
// Before the feature is removed:
axoloth.destroy();
Troubleshooting
The component is styled but clicks do nothing
Only Axoloth Style is loaded. Import Axoloth Behavior and call the matching
initializer after the markup exists.
The script is loaded but attributes still do nothing
The package does not auto-initialize. Call initAxolothBehaviors() or a
per-component initializer from a module script.
A trigger cannot find its panel
Match identifiers exactly: a drawer toggle value must equal its drawer ID, and tab
values must equal their panel values.
New markup is ignored
Call refresh() after inserting new tabs, accordion items, dropdowns, or
toast regions.
An interaction fires twice
The same root was initialized more than once. Destroy its previous controller before
initializing again.
SSR reports that document is missing
Initialize inside the client lifecycle after the DOM exists, and keep your own DOM
selectors out of server execution.