Installation

Link the stylesheet and you have the full design system — no build step. The interactive components (lightbox, modals, dropdowns, syntax highlighting) add one dependency: jQuery 4, loaded before js/arx.js.

Arx does not bundle jQuery — pull it from a CDN as shown. Skip the scripts if you only need the CSS.

HTML
<!-- Styles -->
<link rel="stylesheet" href="path/to/arx.css">

<!-- Interactive components (optional) -->
<script src="https://code.jquery.com/jquery-4.0.0.min.js"></script>
<script src="path/to/arx.js"></script>
Theming

Add arx-dark to <html> or <body> to activate the dark theme. The bundled ArxTheme plugin handles toggling, persistence, and a system-following auto mode.

Auto mode. ArxTheme.set('auto') follows the OS prefers-color-scheme and updates live when it changes. 'light'/'dark' set an explicit choice; all three persist to localStorage.

Density. Add arx-compact to a scope (e.g. <html>) to tighten the padding of inputs, buttons, table cells and list-group items; the default ("cozy") needs no class.

RTL. Set dir="rtl" on <html> (or any subtree) and the layout mirrors — components use CSS logical properties throughout. The physical spacing/text utilities (.arx-ml-*, .arx-text-left) intentionally stay fixed.

Motion & print. Arx honours prefers-reduced-motion (animations collapse) and ships a print stylesheet (interactive chrome hidden, ink-friendly). See Keyboard & A11y.

Avoid the flash. Because the theme class is applied by JavaScript, add this tiny synchronous snippet in <head> before your stylesheet so a dark-mode visitor never sees a light flash on load.

HTML
<html class="arx-dark">              <!-- dark theme -->
<html class="arx-compact">           <!-- compact density -->
<html dir="rtl">                     <!-- right-to-left -->
<html class="arx-dark arx-compact" dir="rtl">  <!-- combine -->
JavaScript — ArxTheme
<!-- Any element auto-toggles light/dark: -->
<button data-arx-theme-toggle>Theme</button>

// Or drive it directly:
ArxTheme.set('auto');    // follow the OS, live
ArxTheme.set('dark');    // explicit
ArxTheme.toggle();       // flip light/dark
ArxTheme.get();          // → true if dark
ArxTheme.mode();         // → 'light' | 'dark' | 'auto'

// The saved preference is restored automatically on load.
HTML — anti-flash (in <head>, before the CSS)
<script>(function(){try{var m=localStorage.getItem('arx-theme');
  if(m==='dark'||(m==='auto'&&matchMedia('(prefers-color-scheme: dark)').matches))
    document.documentElement.classList.add('arx-dark');}catch(e){}})();</script>
Colors

All colour tokens are CSS custom properties defined on :root (light theme) and overridden on .arx-dark. Every component reads from these variables, so switching themes requires only a class change — no CSS recompile.

Primary
Subtle
Secondary
Subtle
Success
Subtle
Danger
Subtle
Warning
Subtle
Info
Subtle
--arx-bg
--arx-surface
Raised
--arx-border
TokenLightDarkUsage
--arx-bg#ffffff#070a0ePage background
--arx-bg-secondary#f8f9fa#0c1017Secondary page bg
--arx-surface#ffffff#0c1017Card / panel bg
--arx-surface-raised#f1f3f5#111820Elevated surface (sidebar, hover)
--arx-border#dee2e6#1c2a3aDefault border
--arx-border-focus#4a9eff#00c8ffFocus ring colour
--arx-text#212529#a8bfccDefault body text
--arx-text-secondary#6c757d#6b8899Subdued text
--arx-text-muted#adb5bd#3d5568Placeholder / metadata
--arx-text-inverse#ffffff#070a0eText on filled backgrounds
--arx-primary#0d6efd#00c8ffBrand accent
--arx-primary-hover#0b5ed7#33d4ffButton hover state
--arx-primary-subtle#cfe2ffrgba(0,200,255,.10)Tinted bg (badges, pills)
--arx-primary-text#084298#070a0eText on subtle bg
--arx-success#198754#00e87aPositive / confirmed
--arx-danger#dc3545#ff4d5eDestructive / error
--arx-warning#ffc107#ffb627Caution
--arx-info#0dcaf0#00c8ffInformational
.arx-bg-primary
.arx-bg-success
.arx-bg-danger
.arx-bg-warning
.arx-bg-surface
.arx-bg-raised
.arx-bg-body
.arx-text-primary .arx-text-secondary .arx-text-muted .arx-text-success .arx-text-danger .arx-text-warning .arx-text-info
100
75
50
25
0

To override a token for a specific component or scope, set it directly in your CSS:

Background utilities
<div class="arx-bg-primary">...</div>
<div class="arx-bg-success">...</div>
<div class="arx-bg-danger">...</div>
<div class="arx-bg-warning">...</div>
<div class="arx-bg-info">...</div>
<div class="arx-bg-secondary">...</div>
<div class="arx-bg-surface">...</div>
<div class="arx-bg-raised">...</div>
<div class="arx-bg-body">...</div>
Text colour utilities
<span class="arx-text-primary">Primary</span>
<span class="arx-text-secondary">Secondary</span>
<span class="arx-text-muted">Muted</span>
<span class="arx-text-success">Success</span>
<span class="arx-text-danger">Danger</span>
<span class="arx-text-warning">Warning</span>
<span class="arx-text-info">Info</span>
Opacity utilities
<div class="arx-opacity-100">...</div>
<div class="arx-opacity-75">...</div>
<div class="arx-opacity-50">...</div>
<div class="arx-opacity-25">...</div>
<div class="arx-opacity-0">...</div>
CSS — token override
.my-component {
  --arx-primary: #7c3aed;        /* purple accent in this scope */
  --arx-primary-subtle: #ede9fe;
}
Gradients

Gradients are tokens too. Each --arx-gradient-* is composed from the accent variables, so it follows the active theme (light / dark / custom) automatically — no extra definitions. Re-aim them all by overriding --arx-gradient-angle, or redefine any single token for a bespoke blend.

.arx-bg-gradient-primary
.arx-bg-gradient-success
.arx-bg-gradient-info
.arx-bg-gradient-warning
.arx-bg-gradient-danger
.arx-bg-gradient
Primary Success Danger Brand
.arx-border-gradient-primary
.arx-border-gradient-success
.arx-border-gradient

Variants: primary, secondary, success, info, warning, danger, plus the multi-stop brand blend (the bare .arx-bg-gradient / .arx-text-gradient / .arx-border-gradient classes). Background utilities don't set a text colour — pair them with .arx-text-* as needed.

Background & text
<div class="arx-bg-gradient-primary arx-text-inverse">...</div>
<div class="arx-bg-gradient">brand blend</div>

<h1 class="arx-text-gradient">Clipped to glyphs</h1>
<span class="arx-text-gradient-success">Success</span>

<div class="arx-border-gradient arx-rounded">
  Rounded corners preserved
</div>
CSS — tune the tokens
:root {
  /* re-aim every gradient at once */
  --arx-gradient-angle: 90deg;

  /* or ship a bespoke blend */
  --arx-gradient-primary:
    linear-gradient(135deg, #7c3aed, #ec4899);
}
Grid System

12-column flexbox grid. Wrap columns in .arx-row inside an .arx-container. Columns are full-width by default and snap at named breakpoints.

col-12
col-6
col-6
col-4
col-4
col-4
col-3
col-3
col-3
col-3
col-md-8
col-md-4
col
col
col
BreakpointInfixMin-width
Extra small(none)0
Smallsm576px
Mediummd768px
Largelg992px
Extra largexl1200px
HTML
<div class="arx-container">
    <div class="arx-row">
        <div class="arx-col-md-8">Main content</div>
        <div class="arx-col-md-4">Sidebar</div>
    </div>
</div>

<!-- Full width on mobile, thirds on md+ -->
<div class="arx-row">
    <div class="arx-col-12 arx-col-md-4">One</div>
    <div class="arx-col-12 arx-col-md-4">Two</div>
    <div class="arx-col-12 arx-col-md-4">Three</div>
</div>

<!-- Equal-width auto columns -->
<div class="arx-row">
    <div class="arx-col">Auto</div>
    <div class="arx-col">Auto</div>
</div>
Stack

.arx-stack is a vertical flex column; .arx-hstack is a horizontal row with centred items. Use gap modifiers to control spacing, and .arx-vr for divider lines inside an hstack.

Step 2 of 4
HTML
<!-- Horizontal stack with divider -->
<div class="arx-hstack arx-hstack-gap-3">
    <button class="arx-btn arx-btn-outline">Back</button>
    <div class="arx-vr"></div>
    <span>Step 2 of 4</span>
    <div class="arx-flex-1"></div>
    <button class="arx-btn arx-btn-primary">Next</button>
</div>

<!-- Vertical stack -->
<div class="arx-stack arx-stack-gap-2">
    <input class="arx-input" placeholder="First name">
    <input class="arx-input" placeholder="Last name">
    <button class="arx-btn arx-btn-primary">Submit</button>
</div>
Split / Two-pane

A list + detail layout (inbox, chat, file manager): a fixed-width .arx-split-list and a flexible .arx-split-detail, each independently scrollable, inside .arx-split. Set the list width with --arx-split-list-width. Collapses to one column under 768px.

Resizable. Add data-arx-split-resizable for a draggable divider (the splitter plugin inserts a gutter). Bound it with data-arx-split-min/-max and persist the size with data-arx-split-key. The inbox example uses it.

  • Inbox
  • Sent
  • Drafts
Detail pane — scrolls independently.
HTML
<div class="arx-split" style="--arx-split-list-width:320px">
  <div class="arx-split-list">…</div>
  <div class="arx-split-detail">…</div>
</div>
Tree View

Collapsible hierarchy (file trees, nav). Add data-arx-tree to a <ul> of .arx-tree-items; an item with a nested .arx-tree-group becomes an expandable parent (chevron). Mark a parent .arx-collapsed to start closed. Keyboard: Up/Down move, Left/Right collapse/expand, Enter/Space toggle. Fires arx:tree-toggle / arx:tree-select.

  • src
    • components
      • button.js
    • index.js
  • tests
    • run.mjs
HTML
<ul class="arx-tree" data-arx-tree>
  <li class="arx-tree-item">
    <div class="arx-tree-node">src</div>
    <ul class="arx-tree-group">
      <li class="arx-tree-item"><div class="arx-tree-node">index.js</div></li>
    </ul>
  </li>
</ul>

// ArxTree.expand('#el') · .collapse('#el')
Sortable

Drag-to-reorder. Add data-arx-sortable to a container of .arx-sortable-items (set ="horizontal" for a row). Add an .arx-sortable-handle inside an item to restrict the grip. Pointer-based (mouse + touch); items can move between sortable containers. Fires arx:sort with each item's data-value.

  • Draft the proposal
  • Review with the team
  • Ship it
HTML
<ul data-arx-sortable>
  <li class="arx-sortable-item" data-value="a">
    <span class="arx-sortable-handle">⠿</span> Item A
  </li>
</ul>

// $(el).on('arx:sort', (e, order) => { … });
Prose

Long-form typography container for articles and rendered Markdown. Wrap body content in .arx-prose and child elements (headings, paragraphs, lists, blockquotes, inline code, links, images, hr) get readable defaults — no per-element classes. .arx-prose-sm for a denser scale.

Heading

Body copy with a link, inline code, and a list:

  • First point
  • Second point
A pull quote sets the tone.
HTML
<article class="arx-prose">
  <h2>…</h2> <p>…</p> <blockquote>…</blockquote>
</article>
Description List

Key/value pairs on a <dl>. Default is a two-column grid; add .arx-dl-inline for right-aligned, tabular values (order totals), .arx-dl-divided for ruled rows, and .arx-dl-total on the emphasised final pair.

Subtotal
$336.00
Tax
$27.60
Total
$363.60
HTML
<dl class="arx-dl arx-dl-inline">
  <dt>Subtotal</dt><dd>$336.00</dd>
  <dt class="arx-dl-total">Total</dt><dd class="arx-dl-total">$363.60</dd>
</dl>
Cover Header

A profile / page banner with an avatar that overlaps the banner edge, plus a meta block and optional actions. Set a background/gradient on .arx-cover or drop in an <img class="arx-cover-img">. Sizes: .arx-cover-sm / .arx-cover-lg. It stacks cleanly on small screens.

AM
Ada Mathison
@ada
AM
Ada Mathison
Mathematician
Engineering

Designing a dual-theme token system

Ada Mathison · 6 min read
HTML
<div class="arx-cover">
  <img class="arx-cover-img" src="banner.jpg" alt="">  <!-- optional -->
</div>
<div class="arx-cover-body">            <!-- + .arx-cover-center -->
  <span class="arx-avatar arx-avatar-xl">…</span>
  <div class="arx-cover-meta">…</div>
  <div class="arx-cover-actions">…</div>
</div>

<!-- Hero: content sits on the banner over a scrim -->
<div class="arx-cover arx-cover-hero">
  <h3>Title</h3> <p>…</p>
</div>

Pairs naturally with a tab strip below the body — see the profile example.

Radio Card

A card that is a radio/checkbox option (payment method, plan). Wrap an input + content in <label class="arx-radio-card"> — the whole card is the click target and reflects the checked state via :has(). .arx-radio-card-tile stacks it vertically.

HTML
<label class="arx-radio-card">
  <input type="radio" name="pay" checked> Card
</label>
Typography

Semantic HTML heading tags styled automatically. Use .arx-h1.arx-h6 utility classes when you need a heading style on a non-heading element.

h1

Display Heading One

h2

Section Heading

h3

Subsection Three

h4

Card Title Four

lead

Larger introductory paragraph text.

body

Regular body text. The quick brown fox jumps over the lazy dog.

muted

De-emphasized secondary text for supplemental context.

code const framework = 'Arx';
quote
Design is not just what it looks like — it's how it works.
HTML
<h1>Display Heading One</h1>
<h2>Section Heading</h2>

<!-- Utility classes apply heading styles to any element -->
<p class="arx-h1">Styled as h1</p>

<p class="arx-lead">Larger intro paragraph.</p>
<p class="arx-muted">De-emphasized text.</p>

<code>const x = 1;</code>

<blockquote>A thoughtful quote.</blockquote>
Keyboard Key

Styles the native <kbd> element (and .arx-kbd) as a pressable key. Nest keys to show combinations.

Press Ctrl + K to search, Esc to close, or P for the command palette.
HTML
Press <kbd>Ctrl</kbd> + <kbd>K</kbd> to search.
Divider

A plain rule is the .arx-divider utility. .arx-divider-label adds centred text (align it with -start/-end), and .arx-divider-vertical separates inline items.

OR CONTINUE WITH
Section
Home Docs About
HTML
<hr class="arx-divider">                  <!-- plain rule -->

<div class="arx-divider-label">OR</div>
<div class="arx-divider-label arx-divider-label-start">Section</div>

<span>A</span><span class="arx-divider-vertical"></span><span>B</span>
Figure

Image + caption wrapper. Use .arx-figure-block to make it full-width.

Image placeholder
Fig. 1 — A short description of the image above.
HTML
<figure class="arx-figure">
    <img src="photo.jpg" alt="..." class="arx-figure-img">
    <figcaption class="arx-figure-caption">
        Fig. 1 — Description of the image.
    </figcaption>
</figure>
Tables

Wrap in .arx-table-responsive for horizontal scrolling on small screens.

# Plugin Status Version
1 Framework (Arx) Stable 1.0.0
2 Modal In Progress
3 Dropdown Planned
4 Tooltip Planned
ModifierEffect
.arx-table-borderedBorder on all cells and outer edge
.arx-table-stripedAlternating row backgrounds
.arx-table-hoverRow highlight on hover
.arx-table-smReduced cell padding
.arx-row-success/danger/warning/infoRow background color
HTML
<div class="arx-table-responsive">
    <table class="arx-table arx-table-bordered arx-table-hover">
        <thead>
            <tr><th>Name</th><th>Status</th></tr>
        </thead>
        <tbody>
            <tr>
                <td>Framework</td>
                <td><span class="arx-badge arx-badge-success">Stable</span></td>
            </tr>
            <tr class="arx-row-warning">
                <td>Modal</td>
                <td><span class="arx-badge arx-badge-warning">WIP</span></td>
            </tr>
        </tbody>
    </table>
</div>
Data Table

Add data-arx-table to an .arx-table to make it sortable, searchable and paginated — all client-side, reading the existing <tbody> rows. Mark sortable columns with data-arx-sort (optionally ="number"/"date"/"text"); clicking a header cycles asc → desc → off. Add data-arx-table-search for a filter box and data-arx-table-page-size="N" for pagination (it reuses the .arx-pagination component).

Name Score Role
Charlie30Admin
Alice100Owner
Bob2Member
Dave45Member
Erin78Admin
HTML
<table class="arx-table arx-table-hover" data-arx-table
       data-arx-table-search data-arx-table-page-size="10">
  <thead><tr>
    <th data-arx-sort>Name</th>
    <th data-arx-sort="number">Score</th>
    <th>Role</th>            <!-- not sortable -->
  </tr></thead>
  <tbody> … </tbody>
</table>

// $('#el').arxTable('sort', 2, 'desc')  // column (1-based), dir
// $('#el').arxTable('search', 'ali')  ·  ('page', 2)  ·  ('refresh')
// $(el).on('arx:table-sort arx:table-page arx:table-search', …);
Sidenav

Vertical sidebar menu. Use .arx-sidenav as the container, .arx-sidenav-group for labelled sections, and .arx-sidenav-link for individual items. Add .arx-sidenav-sticky to pin it as a full-height scrolling panel.

ClassDescription
.arx-sidenavContainer — sets width, background, and right border
.arx-sidenav-stickyModifier — position:sticky; height:100vh; overflow-y:auto
.arx-sidenav-brandLogo / title area at the top
.arx-sidenav-groupWraps a labelled set of links with horizontal padding
.arx-sidenav-labelAll-caps section heading within a group
.arx-sidenav-linkNavigation item — accepts icon + text + optional badge
.arx-sidenav-badgeRight-aligned counter inside a link (pairs with .arx-badge)
.arx-sidenav-dividerHorizontal rule separator between groups
.arx-sidenav-sm / -lgWidth variants — 180px / 288px (default 240px)
.arx-sidenav-flushNo background or border — for inline vertical menus
--arx-sidenav-widthCSS token to set a custom width on the element
Basic sidenav
<nav class="arx-sidenav">

    <!-- Optional brand / logo -->
    <div class="arx-sidenav-brand">
        <i data-lucide="layers"></i>
        Arx
    </div>

    <div class="arx-sidenav-group">
        <span class="arx-sidenav-label">Main</span>

        <a href="#" class="arx-sidenav-link arx-active">
            <i data-lucide="home"></i> Dashboard
        </a>
        <a href="#" class="arx-sidenav-link">
            <i data-lucide="bar-chart-2"></i> Analytics
        </a>

        <!-- Link with a badge counter -->
        <a href="#" class="arx-sidenav-link">
            <i data-lucide="users"></i> Users
            <span class="arx-sidenav-badge arx-badge arx-badge-solid-primary">24</span>
        </a>

        <!-- Disabled item -->
        <a href="#" class="arx-sidenav-link arx-disabled">
            <i data-lucide="credit-card"></i> Billing
        </a>
    </div>

    <hr class="arx-sidenav-divider">

    <div class="arx-sidenav-group">
        <span class="arx-sidenav-label">Settings</span>
        <a href="#" class="arx-sidenav-link">
            <i data-lucide="settings"></i> Preferences
        </a>
    </div>
</nav>
Sticky full-height sidebar layout
<!-- Page layout: sidebar + main content -->
<div style="display:flex; min-height:100vh;">

    <nav class="arx-sidenav arx-sidenav-sticky">
        <!-- nav content -->
    </nav>

    <main style="flex:1; padding:2rem;">
        <!-- page content -->
    </main>

</div>
Width variants & flush
<!-- Narrow -->
<nav class="arx-sidenav arx-sidenav-sm">...</nav>

<!-- Wide -->
<nav class="arx-sidenav arx-sidenav-lg">...</nav>

<!-- Custom width via token -->
<nav class="arx-sidenav" style="--arx-sidenav-width:320px;">...</nav>

<!-- No background/border — inline vertical menu -->
<nav class="arx-sidenav arx-sidenav-flush">...</nav>
Initialise Lucide icons
<script src="vendor/lucide/lucide.min.js"></script>
<script>lucide.createIcons();</script>
Tabs

Switch .arx-tab-pane panels from any .arx-nav tab strip. Mark the active trigger with data-arx-tab="#paneId"; add data-arx-tabs to the strip to opt into ARIA roles and arrow-key navigation. Click handling is auto-wired by js/arx.js.

A token-driven CSS framework with a jQuery 4 plugin layer and built-in light/dark themes.
12-column flexbox grid, sm/md/lg/xl breakpoints, and a full utility layer.
Use the keyboard arrows to move between tabs once one is focused.
Every item, regardless of status.
Only items still in progress.
Items that have been archived.
HTML
<ul class="arx-nav arx-nav-tabs" data-arx-tabs>
    <li class="arx-nav-item">
        <a class="arx-nav-link arx-active" data-arx-tab="#one">One</a></li>
    <li class="arx-nav-item">
        <a class="arx-nav-link" data-arx-tab="#two">Two</a></li>
</ul>
<div class="arx-tab-content">
    <div class="arx-tab-pane arx-active" id="one">First panel</div>
    <div class="arx-tab-pane" id="two">Second panel</div>
</div>

<!-- Works with arx-nav-tabs / -pills / -underline / -vertical -->
JS
// Clicks are auto-wired. To switch programmatically:
$('[data-arx-tabs]').tabs('show', '#two');
ArxTabs.show('#two');

// React to changes:
$('[data-arx-tabs]').on('arx:tabchange', function (e, tab, pane) {
    console.log('Showing', pane.id);
});
Forms

Wrap label + input pairs in .arx-form-group for consistent vertical spacing. All input types share a consistent focus ring via .arx-input, .arx-textarea, and .arx-select.

We never share your email.
Looks good!
Please enter a valid email address.
https://
ClassElementDescription
.arx-form-groupdivVertical spacing wrapper
.arx-labellabelStyled form label
.arx-inputinputText, email, password, number
.arx-textareatextareaMulti-line input
.arx-selectselectDropdown with custom arrow
.arx-input-sm / -lginput/selectSize modifiers
.arx-is-valid / -invalidinput etc.Validation border colors
.arx-valid-feedbackdivSuccess message below field
.arx-invalid-feedbackdivError message below field
.arx-form-textdivHelp text below field
.arx-checklabelCheckbox/radio wrapper
.arx-check-inputinputCustom-styled toggle
.arx-input-groupdivHorizontal input + addon/button
.arx-input-addonspanText prefix/suffix
.arx-btn (in group)buttonJoins seamlessly as a leading/trailing action
Basic form group
<div class="arx-form-group">
    <label class="arx-label">Email</label>
    <input type="email" class="arx-input" placeholder="you@example.com">
    <div class="arx-form-text">We never share your email.</div>
</div>

<div class="arx-form-group">
    <label class="arx-label">Role</label>
    <select class="arx-select">
        <option>Admin</option>
    </select>
</div>

<div class="arx-form-group">
    <label class="arx-label">Notes</label>
    <textarea class="arx-textarea"></textarea>
</div>
Validation
<input class="arx-input arx-is-valid" value="correct@email.com">
<div class="arx-valid-feedback">Looks good!</div>

<input class="arx-input arx-is-invalid" value="bad input">
<div class="arx-invalid-feedback">Please enter a valid email.</div>
Checkbox & Radio
<label class="arx-check">
    <input type="checkbox" class="arx-check-input">
    Enable notifications
</label>

<label class="arx-check">
    <input type="radio" class="arx-check-input" name="plan">
    Starter plan
</label>
Input group
<div class="arx-input-group">
    <span class="arx-input-addon">https://</span>
    <input type="text" class="arx-input" placeholder="yourdomain.com">
</div>

<div class="arx-input-group">
    <input type="text" class="arx-input" placeholder="Search...">
    <button class="arx-btn arx-btn-primary">Go</button>
</div>
Switch, Range & Floating Labels

Additional form controls that complement the core form elements.

Switch
<label class="arx-form-switch">
    <input type="checkbox" class="arx-switch-input">
    Enable notifications
</label>

<!-- Sizes: arx-switch-sm, arx-switch-lg -->
Range
<div class="arx-form-group">
    <label class="arx-label">Volume</label>
    <input type="range" class="arx-range" min="0" max="100" value="60">
</div>
Floating labels
<!-- Requires placeholder=" " (space) for :not(:placeholder-shown) state -->
<div class="arx-float">
    <input type="text" class="arx-input" placeholder=" ">
    <label>Full name</label>
</div>

<div class="arx-float">
    <select class="arx-select">
        <option>Admin</option>
    </select>
    <label>Role</label>
</div>
Segmented Control

A radio-backed toggle group for one mutually-exclusive choice — the checked option lifts into a "thumb". Pure CSS: the native radios hold the state (and post with a form), so no JS is needed. Use .arx-segmented-primary for an accent thumb, -sm/-lg for size, and -block to fill the width.

HTML
<div class="arx-segmented">
    <label class="arx-segment">
        <input type="radio" name="view" checked><span>Day</span>
    </label>
    <label class="arx-segment">
        <input type="radio" name="view"><span>Week</span>
    </label>
</div>

<!-- Accent thumb: arx-segmented-primary -->
<!-- Sizes: arx-segmented-sm / -lg   Full width: -block -->
Tag Input

A field that collects values as removable chips. Enter or comma adds the typed value; Backspace on an empty field removes the last chip; pasting a comma list adds several at once. Set data-arx-tags-name to keep a hidden input synced (comma-joined) for form posts. Auto-wired by js/arx.js.

Type a word and press Enter. Try pasting "a, b, c".
primary success danger neutral
HTML
<div class="arx-tag-input" data-arx-tags
     data-arx-tags-name="labels"
     data-arx-tags-value="design,frontend">
    <input class="arx-tag-input-field" placeholder="Add a tag…">
</div>

<!-- Options: data-arx-tags-max, data-arx-tags-dupes="true" -->
<!-- Standalone chip: <span class="arx-tag arx-tag-primary"> -->
API
$('#el').tags('add', 'newtag');
$('#el').tags('remove', 'oldtag');
$('#el').tags('get');     // → ['design', 'frontend']
$('#el').tags('clear');
// Event: $('#el').on('arx:tagschange', (e, list) => { … });
File Upload

Two flavours: .arx-file styles the native file input with no JS, and .arx-dropzone (auto-wired) adds drag-and-drop with a removable file list. The dropzone rebuilds the input's FileList via DataTransfer, so it still posts correctly.

Browse or drop files here
Drag files in, or click to pick
HTML
<!-- Styled native input (no JS) -->
<input type="file" class="arx-file" multiple>

<!-- Dropzone with drag & drop -->
<div class="arx-dropzone" data-arx-dropzone>
    <div class="arx-dropzone-icon">⬆</div>
    <div class="arx-dropzone-text"><strong>Browse</strong> or drop</div>
    <div class="arx-dropzone-hint">PNG, JPG up to 5 MB</div>
    <input type="file" class="arx-dropzone-input" multiple>
</div>

// $('#dz').dropzone('files')  → [File]   ·   $('#dz').dropzone('clear')
// $('#dz').on('arx:fileschange', (e, files) => { … });
Rating

Pure-CSS star rating. .arx-rating is interactive and radio-backed (the inputs go in reverse 5→1 order so the fill works; the flex flips them visually). .arx-rating-display shows a read-only value and supports fractions via --arx-rating. Sizes -sm/-lg.

HTML
<!-- Interactive — note the reverse 5→1 order -->
<div class="arx-rating">
    <input type="radio" name="r" id="r5"><label for="r5"></label>
    <input type="radio" name="r" id="r4" checked><label for="r4"></label>
    <!-- … r3, r2, r1 … -->
</div>

<!-- Read-only, fractional -->
<span class="arx-rating-display" style="--arx-rating:3.5"
      role="img" aria-label="3.5 out of 5"></span>
Quantity

A number input flanked by −/+ buttons. Auto-wired via data-arx-quantity; it reads min/max/step from the input, clamps typed values, and disables a button at its bound. Sizes -sm/-lg.

HTML
<div class="arx-quantity" data-arx-quantity>
    <button class="arx-quantity-btn" data-arx-quantity-dec
            aria-label="Decrease">−</button>
    <input class="arx-quantity-input" type="number"
           value="1" min="0" max="10" step="1">
    <button class="arx-quantity-btn" data-arx-quantity-inc
            aria-label="Increase">+</button>
</div>

// $('#q').quantity('get')  ·  $('#q').quantity('set', 5)
// $('#q').on('arx:quantitychange', (e, val) => { … });
Autocomplete

A combobox with filter-as-you-type suggestions, match highlighting, and full keyboard control (↑/↓ to move, Enter to pick, Esc to close). Auto-wired via data-arx-autocomplete; the list comes from an inline JSON <script>, or pass an array / async function to $(el).autocomplete().

HTML
<div class="arx-autocomplete" data-arx-autocomplete>
    <input class="arx-input" placeholder="Search…">
    <script type="application/json" class="arx-autocomplete-source">
        ["Apple", "Apricot", "Banana"]
    </script>
</div>
JavaScript
// Array of strings or { label, value } objects
$('#ac').autocomplete({ source: ['Apple', 'Banana'], minChars: 1, limit: 8 });

// Async (remote) source
$('#ac').autocomplete({ source: function (q, done) {
    fetch('/search?q=' + q).then(r => r.json()).then(done);
}});

// $('#ac').on('arx:autocomplete-select', (e, item) => { … });
Select

A styled, optionally-searchable replacement for a native <select>. Add data-arx-select to the select — it stays the value holder, so forms submit normally. Add multiple for a multiselect with removable chips; the search box appears automatically past ~8 options (or force it with data-arx-select-search).

HTML
<select data-arx-select data-arx-select-search>
    <option value="">Choose…</option>
    <option value="apple">Apple</option>
</select>

<!-- Multiselect (chips) -->
<select multiple data-arx-select> … </select>

// $('#el').arxSelect('open'|'close'|'refresh')
// $(el).on('arx:select-change', (e, val) => { … });
Date Picker

A calendar popover on a text input — add data-arx-datepicker. Values are ISO YYYY-MM-DD. Keyboard: arrows move, Enter selects, Esc closes. Bound with data-arx-datepicker-min/-max; add data-arx-datepicker-range for a start–end range.

HTML
<input class="arx-input" data-arx-datepicker
       placeholder="YYYY-MM-DD">

<!-- Range + bounds -->
<input class="arx-input" data-arx-datepicker
       data-arx-datepicker-range
       data-arx-datepicker-min="2026-01-01"
       data-arx-datepicker-max="2026-12-31">

// $('#el').datepicker('open'|'close')
// $(el).on('arx:datepicker-change', (e, val) => { … });
Time Picker

A column popup on a text input — add data-arx-timepicker. The value is always written as 24-hour HH:MM; add data-arx-timepicker-12h for an AM/PM display. Set the minute granularity with data-arx-timepicker-step and bounds with data-arx-timepicker-min/-max (out-of-range options are disabled). Keyboard: ↑/↓ move within a column, Enter selects, Esc closes.

HTML
<input class="arx-input" data-arx-timepicker
       data-arx-timepicker-step="15">

<!-- 12-hour display, business-hours bounds -->
<input class="arx-input" data-arx-timepicker
       data-arx-timepicker-12h
       data-arx-timepicker-min="09:00"
       data-arx-timepicker-max="17:00">

// value is 24h HH:MM regardless of display
// $('#el').timepicker('open'|'close')
// $(el).on('arx:timepicker-change', (e, val) => { … });
Range Slider

A styled single or dual-handle slider that enhances a native <input type="range"> (kept as the form value-holder). Add data-arx-range; for a min–max selection wrap two range inputs in a data-arx-range-dual element. Drag, click the track, or use the keyboard (arrows, Home/End, PageUp/Down). Optional data-arx-range-tooltip and data-arx-range-ticks="N".

HTML
<input type="range" min="0" max="100" value="40"
       data-arx-range data-arx-range-tooltip
       data-arx-range-ticks="5">

<!-- Dual handle (min–max) -->
<div data-arx-range data-arx-range-dual data-arx-range-tooltip>
  <input type="range" min="0" max="100" value="25">
  <input type="range" min="0" max="100" value="75">
</div>

// $('#el').arxRange('get')  → 40  or  [25, 75]
// $('#el').arxRange('set', 60)  ·  ('set', [20, 80])
// $(el).on('arx:range-change', (e, v) => { … });
OTP / PIN Input

Segmented one-time-password entry — add data-arx-otp to an empty element and it builds the cells. Typing auto-advances, Backspace steps back, arrows move, and pasting a code distributes it across cells. A hidden input (data-arx-otp-name) holds the concatenated value for form submission. Numeric by default; set data-arx-otp-type="text" to allow letters.

Fires arx:otp-change on every edit and arx:otp-complete once all cells are filled.

HTML
<div data-arx-otp data-arx-otp-length="6"
     data-arx-otp-name="code"></div>

// $('#el').otp('get')  → "123456"
// $('#el').otp('set', '000000')  ·  ('clear')
// $(el).on('arx:otp-complete', (e, code) => { … });
Password Field

Enhance a <input type="password"> with data-arx-password: a show/hide eye toggle (on by default), an optional 4-segment strength meter (data-arx-password-meter), and an optional satisfied/unsatisfied rules checklist (data-arx-password-rules). Scoring is dependency-free (length tiers + character-class variety).

HTML
<input type="password" class="arx-input"
       data-arx-password
       data-arx-password-meter
       data-arx-password-rules
       data-arx-password-min="8">

// $('#el').arxPassword('score')  → 0–4
// $('#el').arxPassword('toggle')  // show/hide
// $(el).on('arx:password-strength', (e, s) => s.score, s.label);
Input Mask

Format-as-you-type with data-arx-mask. Use a token pattern (9 digit, A letter, * alphanumeric; any other character is a literal) or a named preset: phone, card, date, currency. Set data-arx-mask-name to mirror the unmasked value to a hidden input for the server.

HTML
<input class="arx-input" data-arx-mask="(999) 999-9999">
<input class="arx-input" data-arx-mask="phone">
<input class="arx-input" data-arx-mask="currency"
       data-arx-mask-name="amount_raw">

// $('#el').mask('get')  → "(555) 123-4567"
// $('#el').mask('raw')  → "5551234567"
// $(el).on('arx:mask-change', (e, v) => { … });
Color Picker

A saturation/value square + hue slider on a hex <input> (kept as the value holder). Add data-arx-colorpicker; a swatch opens the popover, and typing a hex updates the picker (and vice-versa). Fires native change + arx:colorchange.

HTML
<input class="arx-input" data-arx-colorpicker value="#0d6efd">

// $('#el').colorpicker('get')  → "#0d6efd"
// $('#el').colorpicker('set', '#ff0000')
// $(el).on('arx:colorchange', (e, hex) => { … });
Buttons

Use .arx-btn as the base. Add a variant class for color. Works on <button>, <a>, and <input type="submit">.

Variants
<button class="arx-btn arx-btn-primary">Primary</button>
<button class="arx-btn arx-btn-secondary">Secondary</button>
<button class="arx-btn arx-btn-success">Success</button>
<button class="arx-btn arx-btn-danger">Danger</button>
<button class="arx-btn arx-btn-warning">Warning</button>
<button class="arx-btn arx-btn-info">Info</button>
<button class="arx-btn arx-btn-ghost">Ghost</button>
<button class="arx-btn arx-btn-outline">Outline</button>
<button class="arx-btn arx-btn-outline-primary">Outline Primary</button>
Sizes
<button class="arx-btn arx-btn-primary arx-btn-sm">Small</button>
<button class="arx-btn arx-btn-primary">Default</button>
<button class="arx-btn arx-btn-primary arx-btn-lg">Large</button>
States
<button class="arx-btn arx-btn-primary" disabled>Disabled</button>
<button class="arx-btn arx-btn-primary arx-disabled">Disabled (class)</button>
<button class="arx-btn arx-btn-primary arx-btn-loading">Loading</button>
Button group
<div class="arx-btn-group">
    <button class="arx-btn arx-btn-outline">Left</button>
    <button class="arx-btn arx-btn-outline">Center</button>
    <button class="arx-btn arx-btn-outline">Right</button>
</div>
Close Button

Generic dismissal button. Used inside modals, alerts, offcanvas panels, and anywhere an × is needed.

Dismissible card

Close button positioned top-right.

HTML
<button class="arx-close">✕</button>
<button class="arx-close" disabled>✕</button>
Cards

Flexible content containers with optional header and footer slots.

User Profile
Lachlan Janke

Full-stack developer working on the Arx Framework.

No Header

A card with only a body — no header or footer.

HTML
<div class="arx-card">
    <div class="arx-card-header">Card Title</div>
    <div class="arx-card-body">
        <div class="arx-card-title">Heading</div>
        <p class="arx-card-text">Body text goes here.</p>
        <button class="arx-btn arx-btn-primary arx-btn-sm">Action</button>
    </div>
    <div class="arx-card-footer">Footer note</div>
</div>
Card Groups

Three strategies for keeping cards at equal height in a row: .arx-card-deck (gapped), .arx-card-group (flush), and .arx-row-equal with column classes.

Card One

Short body text so this card has less content.

Card Two

This card has more text to demonstrate that the deck keeps all cards at the same height automatically.

Card Three

Medium length body.

Hover Me

Lifts on hover via .arx-card-hover.

Same Height

Row stretches cards to equal height with .arx-row-equal.

Card Deck (gapped, equal height)
<div class="arx-card-deck">
    <div class="arx-card">
        <img class="arx-card-img-top" src="…" style="height:160px;">
        <div class="arx-card-body">
            <div class="arx-card-title">Title</div>
            <p class="arx-card-text">…</p>
            <button class="arx-btn arx-btn-primary arx-btn-sm">Action</button>
        </div>
    </div>
    <div class="arx-card">…</div>
    <div class="arx-card">…</div>
</div>
Row Equal (with grid columns)
<!-- .arx-row-equal turns each .arx-col into a flex
     container so the card fills the full column height -->
<div class="arx-row arx-row-equal">
    <div class="arx-col-md-4">
        <div class="arx-card arx-card-hover">
            <div class="arx-card-body">…</div>
        </div>
    </div>
    <div class="arx-col-md-4">…</div>
    <div class="arx-col-md-4">…</div>
</div>
Card Group (flush, no gap)
<div class="arx-card-group">
    <div class="arx-card">…</div>
    <div class="arx-card">…</div>
</div>
Card Image & Extras
<!-- Image at top -->
<img class="arx-card-img-top" src="…" style="height:200px;">

<!-- Image at bottom -->
<img class="arx-card-img-bottom" src="…" style="height:200px;">

<!-- Horizontal layout (image on the left) -->
<div class="arx-card arx-card-horizontal">
    <img class="arx-card-img-side" src="…">
    <div class="arx-card-body">…</div>
</div>

<!-- Lift + shadow on hover -->
<div class="arx-card arx-card-hover">…</div>
Offcanvas / Drawer

A panel that slides in from any screen edge over a backdrop — for menus, filters, and detail views. Author the panel inline; a [data-arx-offcanvas="#id"] trigger toggles it. The drawer traps focus, restores it to the trigger on close, locks body scroll, and closes on Esc or backdrop click. Edge: -start (default), -end, -top, -bottom.

HTML
<button data-arx-offcanvas="#menu">Menu</button>

<aside id="menu" class="arx-offcanvas arx-offcanvas-start" tabindex="-1">
    <div class="arx-offcanvas-header">
        <h3 class="arx-offcanvas-title">Navigation</h3>
        <button class="arx-close" data-arx-offcanvas-close
                aria-label="Close"></button>
    </div>
    <div class="arx-offcanvas-body"> … </div>
</aside>
Options & API
data-arx-offcanvas="#id"   Trigger — toggles the panel
data-arx-offcanvas-close   Closes the panel (on any element inside)
data-arx-backdrop="false"  Open without a backdrop
data-arx-scroll="true"     Don't lock body scroll while open
Sizes: arx-offcanvas-sm / -lg   Edges: -start -end -top -bottom

$('#menu').offcanvas('show');   // or 'hide', 'toggle'
ArxOffcanvas.open('#menu');  ArxOffcanvas.close();
Scrollspy

Highlights the nav link for the section currently in view. Add data-arx-scrollspy to any nav whose links point at section ids (href="#id"); the matching link gets .arx-active as you scroll. Works with .arx-nav, .arx-sidenav, or plain links. This very page's sidebar is a scrollspy.

Set data-arx-scrollspy-offset to clear a fixed navbar, and data-arx-scrollspy-root to spy inside a scrollable container instead of the page.

HTML
<nav class="arx-sidenav" data-arx-scrollspy
     data-arx-scrollspy-offset="80">
    <a class="arx-sidenav-link" href="#intro">Intro</a>
    <a class="arx-sidenav-link" href="#usage">Usage</a>
</nav>

<section id="intro"> … </section>
<section id="usage"> … </section>

<!-- Spy a scroll container: data-arx-scrollspy-root="#box" -->
<!-- Recompute after DOM changes: ArxScrollspy.refresh() -->
Syntax Highlighter

Lightweight code highlighting from js/arx.js. Call .syntax() on any pre code; the language is auto-detected from a class, data-lang, or the content itself. Pass { lineNumbers: true } for a gutter — a copy button is added automatically.

function debounce(fn, wait) {
    let t;
    return function (...args) {
        clearTimeout(t);
        t = setTimeout(() => fn.apply(this, args), wait);
    };
}

const log = debounce(msg => console.log(msg), 200);
log('hello'); // fires once, 200ms after the last call
JavaScript
<pre><code class="language-javascript">…your code…</code></pre>

<script>
// Highlight one block, with line numbers + a copy button
$('#docs-syntax-demo code').syntax({ lineNumbers: true, copyButton: true });

// …or every code block on the page
$('pre code').syntax();
</script>
Alerts

Contextual feedback messages. Add .arx-alert-dismissible and an .arx-alert-close button to make alerts closeable — dismissal is auto-wired by js/arx.js (it fades out and removes the alert on click; no handler required).

Information
Your account settings have been updated successfully.
Success
Plugin deployed to production. All systems nominal.
Warning
Your API rate limit is at 80%. Consider upgrading your plan.
Error
Authentication failed. Please check your credentials.
HTML
<div class="arx-alert arx-alert-info">
    <span class="arx-alert-icon">ℹ</span>
    <div class="arx-alert-body">
        <div class="arx-alert-title">Information</div>
        Your account has been updated.
    </div>
</div>

<!-- Variants: arx-alert-info/success/warning/danger -->

<!-- Dismissible — close is auto-wired by js/arx.js -->
<div class="arx-alert arx-alert-danger arx-alert-dismissible">
    <div class="arx-alert-body">Authentication failed.</div>
    <button class="arx-alert-close" aria-label="Dismiss">✕</button>
</div>
JS
// Auto-wired: clicking .arx-alert-close (or any [data-arx-dismiss]
// inside an alert) fades it out and removes it. To dismiss in code:
$('#my-alert').alert('close');
ArxAlert.dismiss('#my-alert');
Badges

Small inline labels for status, counts, and metadata. Three style tiers — subtle (default), solid, and outline — plus dot indicators and positioned notification counters.

Subtle (default)

Primary Secondary Success Danger Warning Info

Solid

Primary Secondary Success Danger Warning Info

Outline

Primary Secondary Success Danger Warning Info

Sizes & Shapes

Small Default Large 12 v2

Dot Indicators

Online (pulse)
Error (pulse)
Idle
Offline

Positioned (notification counter)

9
24

In-context Usage

New Dashboard Beta

Releases v1.1.0
HTML
<span class="arx-badge arx-badge-primary">Primary</span>
<span class="arx-badge arx-badge-success">Success</span>
<span class="arx-badge arx-badge-danger">Danger</span>
<span class="arx-badge arx-badge-warning">Warning</span>
<span class="arx-badge arx-badge-info">Info</span>
<span class="arx-badge arx-badge-secondary">Secondary</span>
HTML
<span class="arx-badge arx-badge-solid-primary">Primary</span>
<span class="arx-badge arx-badge-solid-success">Success</span>
<span class="arx-badge arx-badge-solid-danger">Danger</span>
HTML
<span class="arx-badge arx-badge-outline-primary">Primary</span>
<span class="arx-badge arx-badge-outline-success">Success</span>
<span class="arx-badge arx-badge-outline-danger">Danger</span>
HTML
<span class="arx-badge arx-badge-primary arx-badge-sm">Small</span>
<span class="arx-badge arx-badge-primary">Default</span>
<span class="arx-badge arx-badge-primary arx-badge-lg">Large</span>

<!-- Square (non-pill) shape -->
<span class="arx-badge arx-badge-solid-danger arx-badge-square">12</span>
HTML
<!-- Static dot -->
<span class="arx-badge arx-badge-dot arx-badge-solid-success"></span>

<!-- Animated ping for live status -->
<span class="arx-badge arx-badge-dot arx-badge-solid-success arx-badge-pulse"></span>
HTML
<div class="arx-badge-wrap">
    <button class="arx-btn arx-btn-outline">Notifications</button>
    <span class="arx-badge arx-badge-solid-danger arx-badge-pos">9</span>
</div>
HTML
<!-- Badge inline in a heading -->
<h4>Dashboard <span class="arx-badge arx-badge-success arx-badge-sm">Beta</span></h4>

<!-- Badge inside a button -->
<button class="arx-btn arx-btn-outline">
    Inbox <span class="arx-badge arx-badge-solid-danger">3</span>
</button>
Avatar

Circular user image or initials. Size with .arx-avatar-xs … -xl, colour initials with a variant (.arx-avatar-primary, etc.), add a corner .arx-avatar-status dot, square it with .arx-avatar-square, or overlap several in an .arx-avatar-group.

XS SM MD LG XL SQ JL A B C +5
HTML
<!-- Initials -->
<span class="arx-avatar arx-avatar-success">JL</span>

<!-- Image -->
<span class="arx-avatar"><img src="me.jpg" alt="Me"></span>

<!-- With a status dot -->
<span class="arx-avatar">SP
    <span class="arx-avatar-status arx-avatar-status-online"></span>
</span>

<!-- Stacked group -->
<span class="arx-avatar-group">
    <span class="arx-avatar arx-avatar-sm">A</span>
    <span class="arx-avatar arx-avatar-sm arx-avatar-info">B</span>
</span>

<!-- Sizes: -xs -sm (default) -lg -xl · Shape: -square
     Status: -online -busy -away · Variants: -primary…-info, -muted -->
Stat / KPI

A metric block — mono .arx-stat-label, large .arx-stat-value, and an optional .arx-stat-trend (add -up / -down for colour). Drops straight into a card body for dashboards.

Revenue $48.2k ▲ 12.4% vs last month
Churn 2.3% ▼ 0.6% improvement
HTML
<div class="arx-card"><div class="arx-card-body">
    <div class="arx-stat">
        <span class="arx-stat-label">Revenue</span>
        <span class="arx-stat-value">$48.2k</span>
        <span class="arx-stat-trend arx-stat-trend-up">▲ 12.4%</span>
    </div>
</div></div>

<!-- Value sizes: .arx-stat-value-sm / -lg -->
Chat Bubble

Message bubbles for conversation UIs. Wrap a series in .arx-chat; mark own messages with .arx-bubble-sent (accent, right-aligned) and replies with .arx-bubble-received.

Did the avatar component land?
Yep — sizes, status dots and groups.
Perfect 🎉
HTML
<div class="arx-chat">
    <div class="arx-bubble arx-bubble-received">Hello!</div>
    <div class="arx-bubble arx-bubble-sent">Hi there 👋</div>
</div>
List Group

Flexible component for displaying a series of content. Add .arx-list-group-action to make rows clickable.

  • Profile
  • Settings
  • Billing New
  • Archived
  1. First item
  2. Second item
  3. Third item
HTML
<ul class="arx-list-group">
    <li class="arx-list-group-item">Profile</li>
    <li class="arx-list-group-item">Settings</li>
</ul>

<!-- Clickable rows -->
<ul class="arx-list-group">
    <a class="arx-list-group-item arx-list-group-action arx-active" href="#">Active</a>
    <a class="arx-list-group-item arx-list-group-action" href="#">Item</a>
</ul>

<!-- Variants: arx-list-group-flush, arx-list-group-numbered, arx-list-group-horizontal -->
<!-- Item colors: arx-list-group-success/danger/warning/info/primary -->
Timeline

A vertical sequence of events on a rail — for activity feeds and histories. Colour a node's dot with .arx-timeline-{primary…info} on the item.

  • 09:24
    Deployed v1.2.0
    All checks green; rolled out to production.
  • 08:50
    Merged pull request #214
    Add toast & popover components.
  • Yesterday
    Opened the milestone
HTML
<ul class="arx-timeline">
    <li class="arx-timeline-item arx-timeline-success">
        <div class="arx-timeline-time">09:24</div>
        <div class="arx-timeline-title">Deployed v1.2.0</div>
        <div class="arx-timeline-body">All checks green.</div>
    </li>
</ul>

<!-- Dot colour: arx-timeline-primary/success/danger/warning/info -->
Empty State

A centred placeholder for "no data / no results" zones — icon, headline, supporting text, and an optional action. Sizes -sm/-lg. It sizes to its content by default; add .arx-empty-fill to stretch it to a card body or grid column's height (content stays centred).

📭
No messages yet

When someone writes to you, it'll show up right here.

HTML
<div class="arx-empty">
    <div class="arx-empty-icon">📭</div>
    <div class="arx-empty-title">No messages yet</div>
    <p class="arx-empty-text">When someone writes, it shows here.</p>
    <div class="arx-empty-action">
        <button class="arx-btn arx-btn-primary">Compose</button>
    </div>
</div>
Stepper / Wizard

A numbered progress indicator. Steps auto-number via a CSS counter; mark progress with .arx-step-complete and .arx-step-active. The visual is pure CSS — add data-arx-stepper and the JS drives it as a wizard (next/prev buttons, optional panels, .arx-stepper-clickable steps). Add .arx-stepper-vertical for a vertical rail.

  1. Account
  2. Profile
  3. Review
Step 1 — create your account.
Step 2 — fill in your profile.
Step 3 — review and finish.
HTML
<ol class="arx-stepper" data-arx-stepper
    data-arx-step-panels="#panes">
    <li class="arx-step"><span class="arx-step-marker"></span>
        <span class="arx-step-label">Account</span></li>
    <li class="arx-step"> … </li>
</ol>
<div id="panes">
    <div class="arx-step-pane"> … </div>   <!-- one per step -->
</div>
<button data-arx-step-prev="#…">Back</button>
<button data-arx-step-next="#…">Next</button>
States & API
<!-- Mark steps directly (no JS) -->
<li class="arx-step arx-step-complete"> … </li>
<li class="arx-step arx-step-active"> … </li>

$('#wizard').stepper('next');   // or 'prev'
$('#wizard').stepper('goto', 2);
ArxStepper.goTo('#wizard', 0);
// Event: $('#wizard').on('arx:stepchange', (e, i) => { … });
Accordion

Collapsible panels built on the native <details> element — works with no JavaScript. Add the open attribute to expand by default, or .arx-accordion-flush for a borderless variant. The optional JS plugin adds single-open (exclusive) mode and a slide animation — opt in with data-arx-accordion="single" or $('.arx-accordion').accordion().

What is Arx?
A neon-themed, token-driven CSS framework with a jQuery 4 plugin layer and built-in light and dark modes.
Is it responsive?
Yes — it ships a 12-column flexbox grid with sm, md, lg and xl breakpoints.
Does it need JavaScript?
No. The accordion is pure CSS, driven by the browser's native disclosure element.

Single-open (animated) — only one panel stays open at a time:

Shipping
Free worldwide shipping on orders over $50.
Returns
30-day no-questions-asked returns on all items.
Warranty
Every product is covered by a two-year warranty.
HTML
<div class="arx-accordion">
    <details class="arx-accordion-item" open>
        <summary class="arx-accordion-header">Title</summary>
        <div class="arx-accordion-body">Content…</div>
    </details>
    <details class="arx-accordion-item">
        <summary class="arx-accordion-header">Another</summary>
        <div class="arx-accordion-body">More content…</div>
    </details>
</div>

<!-- Borderless: add .arx-accordion-flush -->
<!-- Single-open + animated: add data-arx-accordion="single" -->
JS
// Auto-wired when data-arx-accordion is present. Or init manually:
$('.arx-accordion').accordion({
    single:   true,   // close others when one opens
    animate:  true,   // slide the body open/closed
    duration: 220
});
Tooltip

Pure-CSS tooltips via a data-arx-tooltip attribute — they appear on hover and keyboard focus. Position with .arx-tooltip-top (default), -bottom, -left, or -right.

Badge
HTML
<button class="arx-btn" data-arx-tooltip="Saves your changes">
    Save
</button>

<!-- Position: arx-tooltip-top (default) / bottom / left / right -->
<button class="arx-btn arx-tooltip-bottom" data-arx-tooltip="Below">
    Bottom
</button>
Popover

The richer companion to the tooltip — a click- or hover-triggered overlay with an optional title and rich body. Popovers are appended to <body> and positioned by JS, so they escape overflow:hidden ancestors and flip to the opposite side when there isn't room. Add data-arx-popover="…" with the body text.

HTML
<!-- Text body (default: click trigger, top placement) -->
<button class="arx-btn" data-arx-popover="Body text here"
        data-arx-popover-title="Title">Info</button>

<!-- Hover trigger, placed to the right -->
<button class="arx-btn" data-arx-popover="Quick hint"
        data-arx-trigger="hover" data-arx-placement="right">?</button>

<!-- Rich content cloned from a hidden element -->
<a data-arx-popover-target="#tpl" data-arx-popover-title="Details">Open</a>
<div id="tpl" hidden><strong>Any</strong> markup works.</div>
Attributes & API
data-arx-popover           Body text (escaped)
data-arx-popover-target    Selector of a hidden element to clone as the body
data-arx-popover-title     Optional header text
data-arx-placement         top (default) | bottom | left | right
data-arx-trigger           click (default) | hover

// Programmatic control
$('#el').popover('show');   // or 'hide', 'toggle', 'dispose'
ArxPopover.closeAll();
Form validation

Add data-arx-validate to a <form> and it validates from the native HTML constraints you already write — required, type="email", pattern, min/max, minlength/maxlength — driving the .arx-is-invalid border and an auto-injected .arx-invalid-feedback message. Fields validate on blur and on submit; once invalid they re-check as you type. Submit is blocked and the first invalid field is focused.

AttributeOnDescription
data-arx-validateformEnables validation for the form
data-arx-validate-triggerformblur (default) · input (eager) · submit
data-arx-validate-focusformSet off to skip focusing the first invalid field
data-arx-rulesfieldPipe-delimited custom rules, e.g. required|matches:#pw
data-arx-msg-<rule>fieldOverride the message for one rule (e.g. data-arx-msg-required)
data-arx-msgfieldSingle message for any failure on the field
data-arx-validate-skipfieldExclude the field from validation
HTML
<form data-arx-validate>
    <div class="arx-form-group">
        <input class="arx-input" type="email" required
               data-arx-msg-required="We need your email.">
    </div>
    <div class="arx-form-group">
        <input class="arx-input" type="password" id="pw"
               required minlength="8">
    </div>
    <div class="arx-form-group">
        <input class="arx-input" type="password"
               data-arx-rules="matches:#pw"
               data-arx-msg-matches="Passwords don't match.">
    </div>
    <button class="arx-btn arx-btn-primary">Create</button>
</form>
Custom rules & API
// Register a named rule — return true | false | a message
// | a Promise of any of those (async checks gate submit).
ArxValidate.addRule('username', function (value) {
    return fetch('/api/check?u=' + value)
        .then(r => r.json())
        .then(d => d.free || 'That username is taken.');
}, 'Choose a username.');

// Programmatic control
$('#signup').arxValidate();            // → true if every field passes
$('#signup').arxValidate('field', '#email');
$('#signup').arxValidate('reset');     // clear classes + messages

// Events fired on the form
$('#signup').on('arx:validate-success', () => save());
$('#signup').on('arx:validate-invalid', (e, p) => p.first.focus());
Progress

Set the width of .arx-progress inline to reflect the current value. Wrap in .arx-progress-labeled to show a percentage label alongside.

88%
HTML
<div class="arx-progress-wrap">
    <div class="arx-progress" style="width:60%"></div>
</div>

<!-- Variants: arx-progress-success/danger/warning/info -->
<!-- Animated: arx-progress-striped -->
<!-- Sizes on the wrap: arx-progress-sm, arx-progress-lg -->

<!-- With label -->
<div class="arx-progress-labeled">
    <div class="arx-progress-wrap">
        <div class="arx-progress arx-progress-success" style="width:88%"></div>
    </div>
    <span class="arx-progress-label">88%</span>
</div>
Spinner

Three variants: border circle, bouncing dots, and grow pulse. All are inline elements — nest inside a button or centre in a container.

HTML
<!-- Circle spinner -->
<div class="arx-spinner"></div>
<div class="arx-spinner arx-spinner-sm"></div>
<div class="arx-spinner arx-spinner-lg"></div>

<!-- Color variants -->
<div class="arx-spinner arx-spinner-success"></div>
<div class="arx-spinner arx-spinner-danger"></div>

<!-- Dots -->
<div class="arx-spinner-dots"><span></span><span></span><span></span></div>

<!-- Grow pulse -->
<div class="arx-spinner-grow"></div>

<!-- Inside a button -->
<button class="arx-btn arx-btn-primary arx-d-flex arx-align-center arx-gap-2">
    <div class="arx-spinner arx-spinner-sm"></div>
    Loading…
</button>
Placeholder

Skeleton loading states for content that hasn't loaded yet. Use .arx-placeholder-glow on a parent for a shimmer sweep instead of the default pulse.

HTML
<!-- Pulse (default) -->
<div class="arx-placeholder arx-placeholder-lg arx-w-75"></div>
<div class="arx-placeholder arx-w-100"></div>
<div class="arx-placeholder arx-placeholder-btn"></div>

<!-- Shimmer sweep -->
<div class="arx-placeholder-glow">
    <div class="arx-placeholder arx-w-100"></div>
    <div class="arx-placeholder arx-w-75"></div>
</div>

<!-- Sizes: arx-placeholder-xs/sm/lg/xl -->
<!-- Widths: arx-w-25/50/75 or inline width -->
<!-- Button shape: arx-placeholder-btn -->
<!-- Card wrapper: arx-skeleton -->
Toast

Transient, stacked notifications fired from JS. ArxToast creates a container per screen corner on demand — you don't write toast markup by hand. Toasts auto-dismiss after duration ms (default 4000; 0 stays until closed) and pause while hovered or focused.

JavaScript
ArxToast.show('Profile saved');
ArxToast.success('Saved', { title: 'Done', duration: 4000 });
ArxToast.danger('Upload failed', { duration: 0 });   // sticky

// Options: title, variant, duration (0 = sticky), position,
//          dismissible, icon (string/HTML or false), progress
// Variants: ArxToast.primary/success/danger/warning/info
// Positions: top-right (default), top-left, bottom-right,
//            bottom-left, top-center, bottom-center
ArxToast.clear();   // dismiss all
Declarative trigger
<button data-arx-toast="Copied to clipboard"
        data-arx-toast-variant="success"
        data-arx-toast-title="Done">Copy</button>
Spacing Utilities

Margin and padding utilities follow the pattern .arx-{property}{side}-{scale}. Scale runs 0–5.

PatternPropertySideScale → value
arx-m-{n}marginall0→0, 1→0.25rem, 2→0.5rem, 3→1rem, 4→1.5rem, 5→3rem
arx-p-{n}paddingall
arx-mt/mb/ml/mr-{n}margintop/bottom/left/right
arx-pt/pb-{n}paddingtop/bottom
arx-mx-{n} / arx-my-{n}marginx / y axis
arx-px-{n} / arx-py-{n}paddingx / y axis
arx-mx-automarginx-axisauto — centers block elements
Display & Flex Utilities
Classes
arx-d-none | arx-d-block | arx-d-inline | arx-d-flex
arx-d-inline-flex | arx-d-inline-block | arx-d-grid

arx-flex-row | arx-flex-col | arx-flex-wrap | arx-flex-nowrap
arx-flex-1   | arx-flex-auto | arx-flex-none

arx-gap-1 ... arx-gap-5

arx-justify-start | arx-justify-center | arx-justify-end
arx-justify-between | arx-justify-around

arx-align-start | arx-align-center | arx-align-end | arx-align-stretch
Text Utilities

arx-text-primary

arx-text-secondary

arx-text-muted

arx-text-success

arx-text-danger

arx-text-warning

arx-fw-bold

arx-fw-light

arx-text-uppercase

Classes
arx-text-left | arx-text-center | arx-text-right

arx-text-primary | arx-text-secondary | arx-text-muted
arx-text-success | arx-text-danger | arx-text-warning | arx-text-info

arx-text-uppercase | arx-text-lowercase | arx-text-nowrap | arx-text-truncate

arx-fw-light | arx-fw-normal | arx-fw-medium | arx-fw-bold
Sizing Utilities
Classes
arx-w-25 | arx-w-50 | arx-w-75 | arx-w-100 | arx-w-auto
arx-h-25 | arx-h-50 | arx-h-75 | arx-h-100 | arx-h-auto

arx-min-vh-100   /* min-height: 100vh */
arx-vh-100       /* height: 100vh */
Misc Utilities
Classes
/* Position */
arx-position-relative | arx-position-absolute | arx-position-fixed | arx-position-sticky

/* Overflow */
arx-overflow-hidden | arx-overflow-auto | arx-overflow-scroll

/* Visibility */
arx-visible | arx-invisible

/* Borders */
arx-border | arx-border-0 | arx-border-top | arx-border-bottom
arx-rounded | arx-rounded-sm | arx-rounded-lg | arx-rounded-pill | arx-rounded-0

/* Shadow */
arx-shadow-none | arx-shadow-sm | arx-shadow | arx-shadow-lg

/* Screen-reader only */
arx-sr-only

/* Divider */
<hr class="arx-divider">
Context Menu

A right-click menu. Put data-arx-context-menu="#id" on any element; right-clicking it opens the referenced .arx-context-menu at the cursor (flipped to stay on-screen). Closes on select, outside click, Esc, scroll or resize. Up/Down move, Enter activates. Fires arx:context-select with the item's data-value.

Right-click here
HTML
<div id="m" class="arx-context-menu">
  <button class="arx-context-menu-item" data-value="open">Open</button>
  <div class="arx-context-menu-divider"></div>
  <button class="arx-context-menu-item arx-danger">Delete</button>
</div>
<li data-arx-context-menu="#m"> … </li>

// $(el).on('arx:context-select', (e, value) => { … });
Counter

Animated count-up for stats. Add data-arx-counter="N" — it counts up when scrolled into view. Options: -from, -duration, -decimals, -prefix, -suffix (thousands separators are added automatically). Honours prefers-reduced-motion (jumps to the final value). Fires arx:countend.

$48.2kRevenue
1,284Orders
HTML
<span data-arx-counter="1284">0</span>
<span data-arx-counter="48.2" data-arx-counter-prefix="$"
      data-arx-counter-suffix="k" data-arx-counter-decimals="1">0</span>

// ArxCounter.start('#el')  ·  $(el).on('arx:countend', …)
Reading Progress

A thin scroll-progress bar pinned to the top of the viewport. Add a .arx-reading-progress element with data-arx-reading-progress — its width tracks whole-page scroll. Set the attribute to a selector (e.g. ="#article") to track progress through that element instead.

Used on the article example (scroll it to see the bar fill).

HTML
<div class="arx-reading-progress" data-arx-reading-progress></div>

<!-- or track a specific element -->
<div class="arx-reading-progress" data-arx-reading-progress="#article"></div>
Reveal on Scroll

Fade or slide elements in as they enter the viewport. Add data-arx-reveal — the value is the direction: fade (default), up, down, left, right, zoom. Stagger with data-arx-reveal-delay (ms), tune with -duration and -threshold, and set -once="false" to re-animate on every entry. Content stays visible if JS never loads, and reduced-motion users see it immediately (never hidden).

Slides up into view
Slides in from the right, delayed
Zooms in

Scroll the page so these enter the viewport to see them animate.

HTML
<div data-arx-reveal>fades in</div>
<div data-arx-reveal="up" data-arx-reveal-delay="120">…</div>
<div data-arx-reveal="zoom" data-arx-reveal-duration="800">…</div>

// Directions: fade | up | down | left | right | zoom
// ArxReveal.refresh()  — re-scan after injecting DOM
// $(el).on('arx:reveal', …)
Countdown

Counts down to a target date or time. Add data-arx-countdown with an ISO target (a bare date is read as local midnight). For the segmented display, nest [data-arx-countdown-part] boxes; otherwise the element's text becomes a compact 12d 03:04:05. Fires arx:countdown-end at zero; set data-arx-countdown-expired for the finished text.

00Days
00Hrs
00Min
00Sec
HTML
<!-- compact text -->
<span data-arx-countdown="2027-01-01T00:00"
      data-arx-countdown-expired="Live!"></span>

<!-- segmented -->
<div class="arx-countdown" data-arx-countdown="2027-01-01">
  <div class="arx-countdown-segment" data-arx-countdown-part="days">
    <span class="arx-countdown-value">00</span>
    <span class="arx-countdown-label">Days</span></div>
  <!-- hours, minutes, seconds … -->
</div>

// $('#el').countdown('start'|'stop') · arx:countdown-end
Command Palette

A ⌘K / Ctrl-K overlay that fuzzy-filters a list of actions. Mark the overlay data-arx-command; items are .arx-command-item with an .arx-command-label (and optional icon + .arx-command-hint). Arrow keys move, Enter activates (an item with an href navigates; otherwise fires arx:command-select), Esc closes.

  • Go to DocumentationG D
  • Toggle dark modeT
  • Open Playground
  • Browse examples
No results
HTML
<div class="arx-command" data-arx-command>
  <div class="arx-command-panel">
    <input class="arx-command-input">
    <ul class="arx-command-list">
      <li class="arx-command-item" data-arx-command-action="docs">
        <span class="arx-command-label">Documentation</span></li>
    </ul>
  </div>
</div>

// ⌘K / Ctrl-K · ArxCommand.open() · arx:command-select [action]
Calendar

A month event grid (distinct from the date picker). Add data-arx-calendar; events come from an inline JSON <script class="arx-calendar-events"> as [{ date, title, variant }]. Prev/next/today navigate; clicking a day fires arx:calendar-date and an event fires arx:calendar-event.

HTML
<div data-arx-calendar>
  <script type="application/json" class="arx-calendar-events">
    [{"date":"2026-06-12","title":"Release","variant":"success"}]
  </script>
</div>

// $('#el').calendar('next'|'prev'|'today')
// $(el).on('arx:calendar-date', (e, iso) => { … });
Keyboard & Accessibility

Every interactive component is operable from the keyboard and exposes ARIA state. Arx also honours prefers-reduced-motion (animations collapse) and ships a print stylesheet (chrome hidden, ink-friendly).

ComponentKeys
Dropdown open · move · Home/End · Esc close
Select/Enter open · type to filter · · Enter choose · Esc close
Date picker←↑↓→ move day · PgUp/PgDn month · Enter select · Esc close
OTP / PINtype to advance · Backspace back · move · paste fills all
Range slider ±step · Home/End · PgUp/PgDn ±10
Data tableEnter/Space on a column header to sort
AccordionEnter/Space toggle a section (native <details>)
Command palette/Ctrl+K open · type to filter · move · Enter run · Esc close
Tree move · / collapse/expand · Enter toggle
Context menuright-click opens · move · Enter activate · Esc close
Modal / Off-canvasEsc to close

Global conventions

  • Tab / Shift+Tab move focus; visible focus rings throughout.
  • Active scrollspy links and stepper steps set aria-current.
  • Copy buttons announce success to screen readers (aria-live).
  • Auto theme: ArxTheme.set('auto') follows the OS and tracks live changes.
Lucide Icons

Arx ships a local copy of Lucide (v1.17.0) — a clean, consistent open-source icon set with 1000+ SVG icons. It is bundled under the ISC License.

home user settings bell search heart star check-circle alert-triangle x-circle mail lock trash-2 edit plus arrow-right
Attribute / PropertyEffect
data-lucide="name"Selects the icon by its kebab-case name (e.g. arrow-right, check-circle)
style="width:Xpx;height:Xpx;"Controls icon size — defaults to 24 × 24 px
style="color:…"Stroke colour — icons inherit currentColor by default
stroke-widthPass via data-stroke-width="1.5" or override in CSS with stroke-width

Browse the full icon set at lucide.dev/icons. Lucide is © Lucide Icons and Contributors, released under the ISC License.

HTML — include the script
<script src="vendor/lucide/lucide.min.js"></script>
HTML — render icons
<!-- Place i tags wherever you need icons -->
<i data-lucide="heart"></i>
<i data-lucide="star"></i>
<i data-lucide="user"></i>

<!-- Call once after the DOM is ready -->
<script>
    lucide.createIcons();
</script>
HTML — icons in context
<!-- Inside a button -->
<button class="arx-btn arx-btn-primary arx-d-flex arx-align-center arx-gap-2">
    <i data-lucide="plus" style="width:16px;height:16px;"></i>
    New item
</button>

<!-- Colour follows CSS color -->
<i data-lucide="heart" style="width:20px;height:20px;color:var(--arx-danger);"></i>
<i data-lucide="check-circle" style="width:20px;height:20px;color:var(--arx-success);"></i>

<!-- Size via width/height -->
<i data-lucide="star" style="width:32px;height:32px;"></i>
Download

Grab the compiled public/css and public/js files below, or clone the full project from GitHub.

FileDescription
README.md Installation, theming, component reference, and CSS variables Download
css/arx.css Compiled CSS (light + dark themes, all components) Download