Documentation
Complete API reference for every genX module, attribute, and configuration option.
Installation
CDN (Recommended)
Add the bootloader to your HTML. It auto-detects which modules your page needs and loads only those:
<script src="https://cdn.genx.software/v1/bootloader.js" defer></script>
The defer attribute ensures the script runs after the DOM is ready.
Self-Hosted
git clone https://github.com/adamzwasserman/genX.git
cd genX && npm run build
cp dist/* your-project/static/js/
<script src="/static/js/bootloader.js" defer></script>
npm (Coming Soon)
npm install @genx/core
Quick Start
Add genX attributes to any HTML element. No JavaScript required:
<!DOCTYPE html>
<html lang="en">
<head>
<script src="https://cdn.genx.software/v1/bootloader.js" defer></script>
</head>
<body>
<!-- Format a price -->
<span fx-format="currency">99.99</span>
<!-- Output: $99.99 -->
<!-- Add accessibility -->
<button ax-enhance="button" ax-label="Close dialog">×</button>
<!-- Reactive data binding -->
<input type="text" bx-model="name">
<p>Hello, <span bx-bind="name"></span>!</p>
<!-- Sortable table -->
<table tx-sortable>...</table>
<!-- Styled button -->
<button ux-enhance="button" ux-variant="primary">Click me</button>
</body>
</html>
How It Works
- Scan — The bootloader scans the DOM for genX attributes (
fx-*, bx-*, ux-*, etc.)
- Detect — Identifies which modules are needed based on attribute prefixes
- Load — Fetches only the required modules from CDN (parallel loading)
- Parse — Runs the parser pipeline (JSON > Colon > Verbose > Class) to extract configuration
- Init — Initializes each module with parsed config. Prefers
autoInit() (DATAOS pattern) to extract initial state from the DOM
- Watch — A centralized MutationObserver (via domx-bridge) watches for dynamically added elements
You only pay for what you use. If your page only has fx-* attributes, only fmtX is loaded.
Configuration
Optional. Set window.genxConfig before the bootloader script to customize behavior:
<script>
window.genxConfig = {
// Override module URLs
modulePaths: {
'fx': '/my-assets/fmtx.min.js',
'ux': '/my-assets/uix.min.js'
},
// CDN base URL (default: https://cdn.genx.software/v1)
cdn: 'https://cdn.genx.software/v1',
// Module-specific init options
modules: {
'fx': { locale: 'de-DE', currency: 'EUR' },
'lx': { minDisplayMs: 500 }
},
// Subresource Integrity hashes
sri: {
'fx': 'sha384-...'
},
// Enable/disable MutationObserver
observe: true,
// Log bootstrap performance timing
performance: { logging: true }
};
</script>
<script src="https://cdn.genx.software/v1/bootloader.js" defer></script>
Notation: Verbose (Default)
Each option is a separate HTML attribute with the module prefix. This is the most readable and the recommended style:
<span fx-format="currency" fx-currency="EUR" fx-decimals="2">1299.99</span>
<div ax-enhance="field" ax-error="Required" ax-help="Enter your email">
<input type="email">
</div>
<div dx-draggable="card" dx-data='{"id":123}' dx-snap="20" dx-ghost>
Drag me
</div>
Notation: Colon (Compact)
Values separated by colons follow a fixed cardinality order per module. Shorter to write, but less readable for complex configs:
<!-- fx order: format, currency, decimals, symbol -->
<span fx-format="currency:EUR:2">1299.99</span>
<!-- bx order: bind, debounce, throttle -->
<input bx-model="search:300">
Cardinality Orders by Module
| Prefix | Order |
fx | format, currency, decimals, symbol |
bx | bind, debounce, throttle |
ax | label, icon, value, role |
dx | type, id, style, group |
lx | strategy, duration, mode |
nx | type, label, href, target |
Notation: JSON
Pass a JSON object via a {prefix}-opts attribute. Highest priority — overrides all other notations:
<span fx-opts='{"format":"currency","currency":"EUR","decimals":2}'>1299.99</span>
<div dx-opts='{"draggable":"card","ghost":true,"snap":20}'>Drag me</div>
Notation: CSS Class
Encode config in CSS class names using the module's class prefix. Lowest priority — other notations override:
<!-- class prefix 'fmt' maps to fx module -->
<span class="fmt-currency-EUR-2">1299.99</span>
<!-- class prefix 'bind' maps to bx module -->
<input class="bind-search-300">
| Class Prefix | Module |
fmt-* | fmtX (fx) |
acc-* | accX (ax) |
bind-* | bindX (bx) |
drag-* | dragX (dx) |
load-* | loadX (lx) |
nav-* | navX (nx) |
table-* | tableX (tx) |
ui-* | uiX (ux) |
Parser priority: When multiple notations exist on the same element, they merge in order: JSON (highest) > Colon > Verbose > Class (lowest). Higher priority values override lower.
fmtX — Formatting
fx-*
Format numbers, currencies, dates, phone numbers, durations, file sizes, and more with declarative attributes.
Core Attributes
| Attribute | Type | Default | Description |
fx-format | string | — | Required. The format type (see table below) |
fx-type | string | auto | Input type conversion before formatting |
fx-value | string | — | Override source value (instead of textContent) |
fx-decimals | number | 2 | Decimal places |
fx-currency | string | USD | ISO 4217 currency code |
fx-phone-format | string | us | Phone format style |
fx-date-format | string | short | Date format style |
fx-time-format | string | short | Time format style |
fx-duration-format | string | short | Duration format style |
fx-binary | boolean | false | Use binary (1024) units for filesize |
Format Types
| Value | Output Example | Options |
currency | $1,299.99 | fx-currency, fx-decimals |
number | 1,234,567 | fx-decimals |
percent | 15.6% | fx-decimals (default: 0) |
accounting | ($1,234.56) | fx-currency |
scientific | 1.23e+3 | fx-decimals |
abbreviated | 1.5M | fx-decimals (default: 1) |
compact | 1.5 million | Uses Intl.NumberFormat |
fraction | 2 1/2 | Auto-detects or custom denominator |
filesize | 1.50 MB | fx-binary, fx-decimals |
duration | 1d 2h 3m | fx-duration-format |
date | 3/15/2024 | fx-date-format |
time | 2:30 PM | fx-time-format |
datetime | 3/15/24, 2:30 PM | — |
relative | 2 days ago | — |
phone | (555) 123-4567 | fx-phone-format |
ssn | ***-**-1234 | Masked by default |
creditcard | ****-****-****-1234 | Masked by default |
uppercase | HELLO WORLD | — |
lowercase | hello world | — |
capitalize | Hello World | — |
truncate | Long text th... | length (default: 50), suffix (default: "...") |
Date Format Styles
| Style | Output |
short (default) | 12/31/2024 |
medium | Dec 31, 2024 |
long | December 31, 2024 |
full | Wednesday, December 31, 2024 |
iso | 2024-12-31 |
custom | Uses pattern: YYYY, MM, DD, HH, mm, ss |
Phone Format Styles
| Style | Output |
us (default) | (555) 123-4567 |
us-dash | 555-123-4567 |
us-dot | 555.123.4567 |
intl | +1 555 123 4567 |
Duration Format Styles
| Style | Output (for 90061 seconds) |
short (default) | 1d 1h 1m 1s |
medium | 1 day 1 hr 1 min 1 sec |
long | 1 day, 1 hour, 1 minute, 1 second |
compact / clock | 1:01:01:01 (D:HH:MM:SS) |
Input Type Conversions
Use fx-type to convert raw values before formatting:
| Type | Conversion |
cents / pennies | Divide by 100 (integer cents to dollars) |
unix / timestamp | Seconds since epoch × 1000 |
milliseconds / ms | Milliseconds since epoch |
seconds / minutes / hours | Duration input conversion to seconds |
bytes / kb / mb / gb | File size input conversion to bytes |
decimal / fraction | Percent: value is already 0–1 |
Examples
<!-- Currency with locale override -->
<span fx-format="currency" fx-currency="EUR">1299.99</span>
<!-- Output: €1,299.99 -->
<!-- Cents stored as integers -->
<span fx-format="currency" fx-type="cents">12999</span>
<!-- Output: $129.99 -->
<!-- Unix timestamp to relative date -->
<span fx-format="relative" fx-type="unix">1710460800</span>
<!-- Output: 2 days ago -->
<!-- File size with binary units -->
<span fx-format="filesize" fx-binary>1073741824</span>
<!-- Output: 1.00 GiB -->
<!-- Duration from minutes -->
<span fx-format="duration" fx-type="minutes" fx-duration-format="long">90</span>
<!-- Output: 1 hour, 30 minutes -->
<!-- Abbreviated large numbers -->
<span fx-format="abbreviated" fx-decimals="1">1500000</span>
<!-- Output: 1.5M -->
<!-- Masked SSN -->
<span fx-format="ssn">123456789</span>
<!-- Output: ***-**-6789 -->
Value Source Priority
fx-raw attribute (cached after first format)
fx-value attribute
- Element's
textContent
- Element's
.value property (for INPUT/TEXTAREA)
accX — Accessibility
ax-*
Enhance accessibility with ARIA attributes, keyboard navigation, focus management, and screen reader support.
Enhancement Types
Set ax-enhance to one of these values to enable the corresponding behavior:
| Type | Purpose | Key Attributes |
srOnly | Screen reader-only text | ax-sr-text |
label | Auto ARIA labels (icon, abbreviation, currency, date) | ax-label |
live | Live region for dynamic content | aria-live set to polite or assertive |
field | Form field with error/help text, character count | ax-error, ax-help |
nav | Navigation with active link detection | ax-nav-label, ax-current |
button | Button accessibility (role, keyboard, loading state) | ax-label, ax-loading |
table | Table headers, caption, sortable columns | ax-caption, ax-sortable |
image | Alt text, decorative images, long descriptions | ax-decorative, ax-description |
modal | Dialog with focus trap, Escape to close | Auto label from heading |
skipLink | Skip-to-main-content link | Default target: #main |
landmark | Landmark roles (main, search, region) | ax-role, ax-landmark |
focus | Enhanced focus indicators & focus trapping | — |
keyboardNav | Arrow key navigation with multi-select | Supports Shift+Arrow, Ctrl+A |
tooltip | Accessible tooltips | ax-tooltip |
input | Auto-label inputs from placeholder/name | — |
error | Error announcements (role="alert") | — |
status | Status announcements (role="status") | — |
Attributes Reference
| Attribute | Default | Description |
ax-enhance | — | Required. Enhancement type (see above) |
ax-label | — | ARIA label text |
ax-sr-text | — | Screen reader-only text content |
ax-error | — | Error message for form fields |
ax-help | — | Help text for form fields |
ax-loading | false | Button loading state (aria-busy, aria-disabled) |
ax-caption | — | Table caption text |
ax-sortable | — | On <th> elements: make column sortable |
ax-decorative | false | Mark image as decorative (empty alt, aria-hidden) |
ax-description | — | Long description for images |
ax-role | region | Landmark role |
ax-tooltip | — | Tooltip text content |
ax-nav-label | Navigation | ARIA label for nav elements |
ax-current | — | Marks current page link |
Keyboard Navigation (keyboardNav)
| Key | Action |
| Arrow Down / Up | Move focus between items |
| Home / End | Jump to first / last item |
| Space | Toggle selection (multiselect) |
| Shift+Arrow | Range selection |
| Ctrl/Cmd+A | Select all items |
| Ctrl/Cmd+Arrow | Move focus without selecting |
Examples
<!-- Form field with validation feedback -->
<div ax-enhance="field" ax-error="Email is required" ax-help="We'll never share your email">
<input type="email" maxlength="100" placeholder="[email protected]">
</div>
<!-- Sets aria-required, aria-invalid, aria-describedby -->
<!-- Auto-adds character count for maxlength fields -->
<!-- Accessible icon button -->
<button ax-enhance="button" ax-label="Close dialog">
<i class="icon-close"></i>
</button>
<!-- Sets role="button", aria-label, Enter/Space handlers -->
<!-- Image with long description -->
<img src="chart.png" ax-enhance="image" ax-description="Bar chart showing Q1 sales at $2.4M, Q2 at $3.1M, Q3 at $2.8M">
<!-- Skip navigation link -->
<a href="#main" ax-enhance="skipLink">Skip to content</a>
<!-- Keyboard-navigable list -->
<ul ax-enhance="keyboardNav">
<li role="option" tabindex="0">Item 1</li>
<li role="option" tabindex="0">Item 2</li>
<li role="option" tabindex="0">Item 3</li>
</ul>
Validation API
Programmatic accessibility auditing:
const issues = window.AccessX.validate(document.body);
// Returns: [{ element, issue: "Image missing alt attribute", severity: "error" }]
bindX — Data Binding
bx-*
Reactive data binding using JavaScript Proxy. Supports two-way binding, computed properties, conditionals, and form validation.
Binding Attributes
| Attribute | Description | Example |
bx-model | Two-way binding (input ↔ data). Supports dot-notation paths and optional debounce. | bx-model="user.name"
bx-model="search:300" |
bx-bind | One-way binding (data → DOM). Updates textContent (or .value for inputs). | bx-bind="user.fullName" |
bx-computed | Computed expression with auto-dependency tracking. Requires bx-bind on same element. | bx-computed="price * qty" |
bx-if | Conditional display. Expression returning truthy/falsy controls visibility. | bx-if="items.length > 0" |
Input Type Handling
| Input Type | Read | Write |
checkbox | el.checked (boolean) | el.checked = bool |
radio | el.value (only if checked) | el.checked = (el.value === val) |
number / range | parseFloat(el.value) | el.value = String(val) |
text / textarea | el.value | el.value = String(val) |
select | el.value | el.value = String(val) |
Form Validation
| Attribute | Description |
bx-form | Wrap a <form> to enable validation and state tracking |
bx-validate | Validation rules (space-separated or JSON) |
bx-form-submit | Global function name called on valid submit |
bx-error-{rule} | Custom error message for a specific rule |
Validation Rules
| Rule | Description |
required | Non-empty value |
email | Valid email format |
url | Valid URL |
number | Valid number |
integer | Valid integer |
alpha | Letters only |
alphanumeric | Letters and digits only |
phone | Phone number (min 10 digits) |
min:N | Numeric minimum |
max:N | Numeric maximum |
minLength:N | Minimum string length |
maxLength:N | Maximum string length |
pattern:regex | Regex pattern match |
custom:fnName | Custom validator function from data |
Form State
When using bx-form, a formState object is automatically created in the reactive data:
data.formState = {
pristine: true, // Never modified
dirty: false, // User has changed fields
valid: true, // All validation passes
invalid: false, // Any field fails
errors: {}, // { fieldPath: ["Error message"] }
touched: Set() // Field names the user has interacted with
}
CSS Classes (auto-applied)
| Class | Applied To |
.bx-pristine / .bx-dirty | Form element |
.bx-valid / .bx-invalid | Form and individual fields |
.bx-error | Invalid fields |
Events
| Event | Detail | When |
bx-form-valid | { data: formData } | Form submitted and validation passes |
bx-form-invalid | { errors: {...} } | Form submitted and validation fails |
Examples
<!-- Two-way binding with debounce -->
<input type="text" bx-model="search:300" placeholder="Search...">
<p>Searching for: <span bx-bind="search"></span></p>
<!-- Computed property -->
<input type="number" bx-model="price">
<input type="number" bx-model="quantity">
<p>Total: <span bx-computed="price * quantity" bx-bind="total"></span></p>
<!-- Conditional display -->
<div bx-if="items.length > 0">You have items in your cart.</div>
<div bx-if="items.length === 0">Your cart is empty.</div>
<!-- Form with validation -->
<form bx-form bx-form-submit="handleSignup">
<input type="email" bx-model="email"
bx-validate="required email"
bx-error-required="Email is required"
bx-error-email="Enter a valid email">
<input type="password" bx-model="password"
bx-validate="required minLength:8">
<button type="submit" bx-if="formState.valid">Sign Up</button>
</form>
DATAOS Pattern
bindX extracts initial state directly from the DOM. Input values become the initial reactive data:
<!-- The DOM IS the initial state -->
<input type="text" bx-model="user.name" value="John">
<input type="number" bx-model="age" value="30">
<input type="checkbox" bx-model="newsletter" checked>
<!-- Automatically creates: { user: { name: "John" }, age: 30, newsletter: true } -->
dragX — Drag & Drop
dx-*
Drag-and-drop with spatial indexing (quad-tree), ghost images, snap-to-grid, axis constraints, and full keyboard accessibility.
Draggable Attributes
| Attribute | Type | Default | Description |
dx-draggable | string | — | Required. Type identifier for the draggable element |
dx-data | JSON | {} | Custom data payload (passed to drop handler) |
dx-handle | CSS selector | — | Restrict drag initiation to a handle element |
dx-ghost | boolean | false | Show ghost image during drag (canvas-based) |
dx-snap | number | — | Snap to grid (px). Empty or "true" = 10px |
dx-axis | string | — | horizontal or vertical constraint |
dx-revert | string | never | always, invalid, or never |
Drop Zone Attributes
| Attribute | Type | Default | Description |
dx-drop-zone | string | — | Required. Drop zone identifier |
dx-accepts | string | * | Comma-separated list of accepted draggable types. * = accept all |
dx-priority | number | 0 | Higher priority wins when zones overlap |
dx-sort | boolean | false | Enable sortable behavior within the zone |
Events
| Event | Fired On | Detail |
dx:dragstart | Draggable element | { element, type, data, x, y } |
dx:drop | Drop zone | { element, dropZone, type, data, x, y } |
dx:dragend | Draggable element | { element, dropZone, success, cancelled } |
CSS Classes (auto-applied)
| Class | Applied When |
dx-dragging | Element is being dragged |
dx-drag-over | Drop zone has a draggable hovering over it |
dx-keyboard-dragging | Element is being dragged via keyboard |
Keyboard Accessibility
| Key | Action |
| Arrow keys | Move dragged element |
| Tab | Cycle to next drop zone |
| Enter | Drop on current/focused zone |
| Escape | Cancel drag |
Examples
<!-- Draggable card with ghost and custom data -->
<div dx-draggable="task" dx-data='{"id":42,"status":"todo"}' dx-ghost>
Task #42: Fix login bug
</div>
<!-- Drop zone that only accepts "task" type -->
<div dx-drop-zone="in-progress" dx-accepts="task">
In Progress
</div>
<!-- Constrained drag with snap -->
<div dx-draggable="slider" dx-axis="horizontal" dx-snap="10">
<span dx-handle>|||</span> Slider Handle
</div>
<!-- Sortable list -->
<ul dx-drop-zone="items" dx-sort>
<li dx-draggable="item">Item 1</li>
<li dx-draggable="item">Item 2</li>
<li dx-draggable="item">Item 3</li>
</ul>
<!-- Listen for drops -->
<script>
document.addEventListener('dx:drop', e => {
console.log('Dropped:', e.detail.type, 'on:', e.detail.dropZone.id);
console.log('Data:', e.detail.data);
});
</script>
navX — Navigation
nx-*
Client-side navigation, breadcrumbs, tabs, scroll spy, sticky headers, mobile menus, dropdowns, and pagination.
Feature Types
| Attribute | Purpose |
nx-nav | Navigation with active link tracking |
nx-breadcrumb | Hierarchical breadcrumbs (auto, trail, or data-parent) |
nx-tabs | Accessible tab navigation (arrow keys, Home/End) |
nx-scroll-spy | Highlight nav links as sections scroll into view |
nx-sticky | Sticky header with stuck-state detection |
nx-mobile | Hamburger menu with focus trap |
nx-dropdown | Dropdown menus with keyboard navigation |
Navigation (nx-nav)
| Attribute | Default | Description |
nx-nav-activeClass | nx-active | CSS class for the active link |
nx-nav-exact | false | Require exact path match |
nx-nav-smoothScroll | true | Enable smooth scrolling for anchor links |
nx-confirm | — | Show confirmation dialog before navigating |
nx-shortcut | — | Keyboard shortcut (e.g., ctrl+h) |
Breadcrumbs (nx-breadcrumb)
| Attribute | Default | Description |
nx-breadcrumb | auto | Mode: hierarchical, trail, data-parent |
nx-breadcrumb-separator | > | HTML separator between items |
nx-breadcrumb-rootLabel | Home | Label for the root/home link |
nx-breadcrumb-schema | false | Include Schema.org structured data |
Sticky Headers (nx-sticky)
| Attribute | Default | Description |
nx-sticky-top | 0 | Top offset when stuck |
nx-sticky-zIndex | 1000 | Z-index when stuck |
nx-sticky-stickyClass | nx-stuck | CSS class when element is stuck |
Scroll Spy (nx-scroll-spy)
| Attribute | Default | Description |
nx-scroll-spy-threshold | 0.5 | IntersectionObserver threshold |
nx-scroll-spy-activeClass | nx-active | CSS class for active link |
Mobile Menu (nx-mobile)
| Attribute | Default | Description |
nx-mobile-preventScroll | true | Prevent body scroll when menu is open |
nx-mobile-closeOnClick | true | Close menu when a link is clicked |
nx-mobile-closeOnOutsideClick | true | Close menu on outside click |
Events
| Event | When |
nx:navigate | Link click in nx-nav |
nx:tab-change | Tab selected in nx-tabs |
nx:page-change | Page button clicked in pagination |
nx:content-loaded | Lazy content loaded in nx-dropdown |
Examples
<!-- Nav with active link detection -->
<nav nx-nav nx-nav-activeClass="current">
<a href="/">Home</a>
<a href="/products">Products</a>
<a href="/contact">Contact</a>
</nav>
<!-- Auto-generated breadcrumbs from URL path -->
<nav nx-breadcrumb="hierarchical" nx-breadcrumb-separator="/"></nav>
<!-- Sticky header with stuck detection -->
<header nx-sticky nx-sticky-stickyClass="is-stuck">
<h1>Page Title</h1>
</header>
<!-- Scroll spy sidebar -->
<nav nx-scroll-spy nx-scroll-spy-threshold="0.3">
<a href="#section1">Section 1</a>
<a href="#section2">Section 2</a>
<a href="#section3">Section 3</a>
</nav>
<!-- Mobile hamburger menu -->
<div nx-mobile>
<button data-mobile-trigger>Menu</button>
<nav data-mobile-menu>
<a href="/">Home</a>
<a href="/about">About</a>
</nav>
</div>
<!-- Dropdown with keyboard nav -->
<div nx-dropdown>
<button data-dropdown-trigger>Options</button>
<ul data-dropdown-menu>
<li><a href="#">Edit</a></li>
<li><a href="#">Delete</a></li>
</ul>
</div>
loadX — Loading States
lx-*
Loading spinners, skeleton screens, progress bars, and fade states. Auto-detects async operations (fetch, XHR, HTMX, forms).
Attributes
| Attribute | Type | Default | Description |
lx-strategy | string | — | spinner, skeleton, progress, or fade |
lx-loading | boolean | — | Mark element as having a loading state |
lx-spinner-type | string | circle | circle, dots, or bars |
lx-spinner-size | string | medium | small, medium, or large |
lx-spinner-color | CSS color | — | Spinner color |
lx-rows | number | auto | Number of skeleton rows (auto-detects from content) |
lx-animate | boolean | true | Animate skeleton shimmer |
lx-value | number | — | Progress bar current value |
lx-max | number | 100 | Progress bar maximum |
lx-progress-mode | string | determinate | determinate or indeterminate |
lx-duration | number | 300 | Fade duration in ms |
lx-message | string | Loading... | Loading message text (fade strategy) |
lx-urgent | boolean | false | Use aria-live="assertive" for announcements |
Auto-Detection
loadX can automatically show loading states for async operations:
| Source | Detection |
| Fetch API | Intercepts fetch() calls |
| XMLHttpRequest | Monitors XHR open/send |
| HTMX | Listens to htmx:beforeRequest / htmx:afterSwap |
| Forms | Monitors form submit events |
Configuration
window.genxConfig = {
modules: {
'lx': {
minDisplayMs: 300, // Minimum time to show loading indicator
autoDetect: {
fetch: true, // Monitor fetch API
xhr: true, // Monitor XMLHttpRequest
htmx: true, // Monitor HTMX events
forms: true // Monitor form submissions
},
preventCLS: true // Use ResizeObserver to prevent layout shift
}
}
};
Accessibility
- Creates ARIA live region (
#lx-live-region) for screen reader announcements
- Announces "Loading" on apply, "Loading complete" on remove
- Respects
prefers-reduced-motion (shows static text instead of animation)
- Progress bars include
role="progressbar", aria-valuenow, aria-valuemax
Examples
<!-- Spinner -->
<button lx-strategy="spinner" lx-spinner-type="dots" lx-spinner-size="small">
Submitting...
</button>
<!-- Skeleton loading -->
<div lx-strategy="skeleton" lx-rows="3">
Content loading...
</div>
<!-- Progress bar -->
<div lx-strategy="progress" lx-value="75" lx-max="100"></div>
<!-- Indeterminate progress -->
<div lx-strategy="progress" lx-progress-mode="indeterminate"></div>
<!-- Fade with message -->
<div lx-strategy="fade" lx-duration="500" lx-message="Please wait...">
Page content
</div>
tableX — Tables
tx-*
Table sorting (multi-column), client-side pagination, search filtering, and virtual scrolling for large datasets.
Attributes
| Attribute | Type | Default | Description |
tx-sortable | boolean | — | On <table>: enable sorting on all columns. On <th>: enable on that column only. |
tx-paginate | boolean | — | Enable client-side pagination |
tx-per-page | number | 10 | Rows per page |
tx-filterable | boolean | — | Enable search/filter input |
tx-filter-columns | string | all | Comma-separated column names to search |
tx-filter-debounce | number | 300 | Filter input debounce delay (ms) |
tx-virtual | string | auto | true, false, or auto (auto-enables for >1000 rows) |
tx-buffer | number | 10 | Buffer rows above/below viewport (virtual scroll) |
data-value | string | — | On <td>: override sort value (instead of textContent) |
Sorting Behavior
- Click a header: cycles through ascending → descending → clear
- Shift+Click: multi-column sort (adds/toggles column to sort stack)
- Keyboard: Space or Enter on focused header to sort
- Auto-detection: Column types detected from first 10 non-empty values (number, date, or string)
CSS Classes (auto-applied)
| Class | Applied To |
tx-sortable | Sortable headers (cursor: pointer) |
tx-sort-active | Currently sorted column header |
tx-sort-asc / tx-sort-desc | Sort direction indicator |
tx-pagination | Generated pagination container |
tx-filter-input | Generated search input |
Events
| Event | When |
tx:virtual-scroll | Viewport change during virtual scroll |
tx:performance | Performance metrics available |
Examples
<!-- Sortable + paginated + filterable table -->
<table tx-sortable tx-paginate tx-per-page="25" tx-filterable>
<thead>
<tr>
<th>Name</th>
<th>Date</th>
<th>Price</th>
</tr>
</thead>
<tbody>
<tr>
<td>Widget</td>
<td>2024-03-15</td>
<td data-value="29.99">$29.99</td>
</tr>
<!-- more rows -->
</tbody>
</table>
<!-- Sort only specific columns -->
<table>
<thead>
<tr>
<th tx-sortable>Name</th>
<th>Description</th>
<th tx-sortable>Price</th>
</tr>
</thead>
<!-- ... -->
</table>
<!-- Virtual scroll for large datasets -->
<table tx-sortable tx-virtual="true" tx-buffer="20">
<!-- 10,000+ rows -->
</table>
smartX — Auto-Format
sx-*
Automatically detect content type (currency, date, phone, email, URL, percentage, number) and apply formatting with a confidence score.
Attributes
| Attribute | Type | Default | Description |
fx-format="smart" | string | — | Trigger smart detection via fmtX |
fx-smart-threshold | number | 75 | Minimum confidence (0–100) to apply formatting |
fx-smart-types | string | all | Comma-separated list of allowed types |
Detection Types & Confidence
| Type | Patterns | Confidence Range |
email | [email protected] | 100 |
url | https://example.com | 100 |
percentage | 45.67% | 100 |
date | ISO, US, named months | 75–98 |
currency | $1234, 100 euros | 60–95 |
phone | International, US formats | 40–95 |
number | 1,234, 123.45 | 50–85 |
text | Fallback | 0 |
Configuration
<script>
// Configure after module loads
window.addEventListener('genx:ready', () => {
window.SmartX.configure({
threshold: 80, // Raise confidence threshold
logTarget: 'console' // Log low-confidence detections
});
});
</script>
Examples
<!-- Auto-detect and format -->
<span fx-format="smart">$1,234.56</span>
<!-- Detected: currency (confidence: 95), formatted -->
<span fx-format="smart">(555) 123-4567</span>
<!-- Detected: phone (confidence: 90), formatted -->
<span fx-format="smart">2024-03-15</span>
<!-- Detected: date (confidence: 98), formatted -->
<!-- Restrict to specific types -->
<span fx-format="smart" fx-smart-types="currency,number">42.50</span>
<!-- Higher threshold for strict matching -->
<span fx-format="smart" fx-smart-threshold="90">maybe a phone</span>
<!-- Below threshold: left unformatted -->
Data Attributes Set
After detection, smartX sets these attributes on the element for inspection:
data-smart-detected — detected type name
data-smart-confidence — confidence score
data-smart-applied / data-smart-fallback — whether formatting was applied
uiX — UI Components
ux-*
A full UI component library. Most components are CSS-driven with minimal JavaScript. Interactive components use declarative HTML triggers — no custom JavaScript required.
Component Types
| Category | Components |
| Atoms | button, badge, avatar, spinner, progress, skeleton, divider, tag, tooltip |
| Form | input, textarea, select, checkbox, radio, switch |
| Molecules | card, alert, modal, drawer, dropdown, accordion, tabs, popover, menu, breadcrumb, pagination |
| Organisms | nav, sidebar, table, stepper, toast |
Universal Attributes
| Attribute | Description |
ux-enhance="type" | Required. Component type to enhance |
ux-bg="color" | Background color (named, hex, or variant) |
ux-color="color" | Text color (auto-calculated from bg if omitted) |
ux-size="sm|lg" | Size variant (most components default to md) |
ux-variant="name" | Preset style variant |
Color System
All components accept color values in three formats:
- Named CSS colors:
tomato, navy, hotpink
- Hex values:
#6366f1, #fff
- Variant names:
primary (teal), secondary, success (green), danger (red), warning (amber), info (blue), neutral (slate)
When ux-bg is set without ux-color, text color is auto-calculated using WCAG luminance for contrast.
Button
| Attribute | Values | Description |
ux-variant | primary, secondary, outline, ghost, danger | Visual style |
ux-size | sm, lg | Button size |
ux-block | boolean | Full-width button |
ux-loading | boolean | Disabled with opacity |
ux-hover-bg | color | Custom hover background |
ux-active-bg | color | Custom active/pressed background |
<button ux-enhance="button" ux-variant="primary" ux-size="lg">Submit</button>
<button ux-enhance="button" ux-bg="hotpink" ux-hover-bg="deeppink">Custom</button>
<button ux-enhance="button" ux-variant="outline" ux-block>Full Width</button>
Badge & Avatar
<!-- Badge with variant -->
<span ux-enhance="badge" ux-variant="success">Active</span>
<span ux-enhance="badge" ux-variant="danger" ux-pill>3</span>
<span ux-enhance="badge" ux-bg="#6366f1" ux-color="white">Custom</span>
<!-- Avatar with image fallback to initials -->
<div ux-enhance="avatar" ux-size="xl" ux-bg="teal">JD</div>
<div ux-enhance="avatar" ux-size="lg">
<img src="photo.jpg" alt="Jane Doe">
</div>
Card
| Attribute | Description |
ux-title | Auto-generate a card header with this title |
ux-elevated | Remove border, add box shadow |
ux-hoverable | Lift effect on hover |
ux-header-bg / ux-header-color | Header styling |
<div ux-enhance="card" ux-title="Dashboard" ux-elevated ux-header-bg="primary" ux-header-color="white">
<p>Card body content.</p>
</div>
Modal
| Attribute | Default | Description |
ux-size | md (32rem) | sm, md, lg, xl, full |
ux-title | — | Auto-generate modal header |
ux-close-on-escape | true | Close on Escape key |
ux-close-on-backdrop | true | Close on backdrop click |
<!-- Open/close with declarative triggers -->
<button ux-opens="#confirm-modal">Delete Account</button>
<div ux-enhance="modal" ux-title="Confirm Delete" ux-size="sm" id="confirm-modal">
<p>This action cannot be undone.</p>
<button ux-closes ux-enhance="button" ux-variant="danger">Delete</button>
<button ux-closes ux-enhance="button" ux-variant="ghost">Cancel</button>
</div>
Tabs
| Attribute | Description |
ux-tabs | Comma-separated tab names (auto-generates tab buttons) |
ux-active-bg / ux-active-color | Active tab styling |
<!-- Auto-generated tabs -->
<div ux-enhance="tabs" ux-tabs="Overview, Details, Settings" id="my-tabs"
ux-active-bg="primary" ux-active-color="white">
<div>Overview content</div>
<div>Details content</div>
<div>Settings content</div>
</div>
<!-- External tab selection -->
<button ux-selects="#my-tabs" ux-tab="Settings">Go to Settings</button>
Accordion
| Attribute | Default | Description |
ux-single | false | Only one section open at a time |
ux-header-bg | — | Header background color |
<div ux-enhance="accordion" ux-single id="faq">
<div class="ux-accordion__item">
<button class="ux-accordion__header">What is genX?</button>
<div class="ux-accordion__content">
A declarative JavaScript library...
</div>
</div>
<div class="ux-accordion__item">
<button class="ux-accordion__header">How do I install it?</button>
<div class="ux-accordion__content">
Add a single script tag...
</div>
</div>
</div>
<!-- External toggle -->
<button ux-toggles="#faq" ux-index="0">Toggle First Section</button>
Drawer
<button ux-opens="#side-panel">Open Panel</button>
<div ux-enhance="drawer" ux-position="right" ux-size="lg" id="side-panel">
<h2>Settings</h2>
<p>Panel content...</p>
<button ux-closes>Close</button>
</div>
Form Inputs
<!-- Input with validation states -->
<input ux-enhance="input" ux-size="lg" placeholder="Email" ux-focus-color="primary">
<input ux-enhance="input" ux-error="true" placeholder="Invalid input">
<input ux-enhance="input" ux-success="true" placeholder="Valid input">
<!-- Switch toggle -->
<input type="checkbox" ux-enhance="switch" ux-label="Dark mode" ux-on-bg="success">
<!-- Checkbox with label -->
<input type="checkbox" ux-enhance="checkbox" ux-label="Accept terms">
Tooltip & Alert
<!-- Tooltip -->
<span ux-enhance="tooltip" ux-content="Helpful information!" ux-bg="dark">Hover me</span>
<!-- Dismissible alert -->
<div ux-enhance="alert" ux-variant="warning" ux-dismissible>
Your session will expire in 5 minutes.
</div>
Declarative Trigger Attributes
Control interactive components from anywhere in the page. No JavaScript required:
| Attribute | Action | Works With |
ux-opens="#id" | Open a modal or drawer | modal, drawer |
ux-closes | Close nearest modal/drawer/dropdown (or specify ux-closes="#id") | modal, drawer, dropdown |
ux-toggles="#id" | Toggle open/close state | dropdown, accordion (with ux-index) |
ux-selects="#id" | Select a tab (with ux-tab="name" or ux-index="0") | tabs |
Events
| Event | Components | Detail |
ux:open | modal, drawer, dropdown | — |
ux:close | modal, drawer, dropdown | — |
ux:change | tabs, accordion, switch | Tabs: { index, name, tab, panel } Accordion: { index, item, action } Switch: { checked } |
Toast API
Toasts are created programmatically:
// After genx:ready
window.UIX.toast('Item saved successfully', { type: 'success', duration: 3000 });
window.UIX.toast('Something went wrong', { type: 'danger', duration: 5000 });
Bootloader
The bootloader (bootloader.js) is the single entry point. It handles module discovery, loading, parsing, and initialization.
Module Detection
The bootloader looks for these entry attributes to determine which modules to load:
| Prefix | Module | Entry Attributes |
fx | fmtX | fx-format |
ax | accX | ax-enhance |
bx | bindX | bx-model, bx-bind, bx-if, bx-form |
dx | dragX | dx-draggable, dx-drop-zone |
lx | loadX | lx-strategy, lx-loading |
tx | tableX | tx-sortable |
nx | navX | nx-tabs, nx-dropdown, nx-breadcrumb, nx-mobile, nx-scroll-spy, nx-sticky, nx-nav |
sx | smartX | Loaded when fmtX encounters fx-format="smart" |
ux | uiX | ux-enhance |
Bootstrap Event
Listen for genx:ready to know when all modules are loaded:
window.addEventListener('genx:ready', (e) => {
console.log('Loaded modules:', e.detail.loaded); // ['fx', 'ux']
console.log('Elements parsed:', e.detail.elements.parsed);
console.log('Total time:', e.detail.timing.total + 'ms');
console.log('Phase breakdown:', e.detail.timing.phases);
});
Public API
// Check which modules are loaded
window.genx.loaded(); // ['fx', 'bx', 'ux']
window.genx.isLoaded('fx'); // true
// Get parsed config for an element (from WeakMap cache)
window.genx.getConfig(element); // { format: 'currency', currency: 'EUR' }
// Rescan after adding dynamic content
window.genx.rescan(); // Processes new elements
window.genx.rescan(myDiv); // Rescan specific subtree
Parser Pipeline
Configuration is extracted from DOM elements through 4 parser stages, merged in priority order:
- JSON (highest priority) —
{prefix}-opts='{"key":"value"}'
- Colon —
{prefix}-format="currency:EUR:2"
- Verbose — individual
{prefix}-key="value" attributes
- Class (lowest priority) — CSS class names like
fmt-currency-EUR
Each parser merges into the result of the previous one. Higher-priority values override lower.
MutationObserver
genX uses a single shared MutationObserver (via domx-bridge) for all modules. Each module subscribes with an attribute filter and only receives mutations relevant to its prefix.
Elements added dynamically (via JavaScript, HTMX, or any other mechanism) are automatically detected and processed.
Error Handling
genX uses a structured error hierarchy:
| Error Class | When |
GenXError | Base class for all errors. Has code, message, context, timestamp |
ParseError | Attribute/config parsing failures |
EnhancementError | Element enhancement failures |
ValidationError | Configuration validation failures |
Circuit Breaker
Modules that repeatedly fail are protected by a circuit breaker (default: 5 failures, 60s timeout):
- CLOSED — Normal operation, failures counted
- OPEN — After threshold failures, operations fail immediately for the timeout period
- HALF_OPEN — After timeout, allows one test operation. Success resets to CLOSED; failure returns to OPEN
Framework Compatibility
genX works with any framework because it operates on the DOM after rendering:
React
function ProductCard({ price }) {
return (
<div className="product">
<span fx-format="currency">{price}</span>
</div>
);
}
Vue
<template>
<div class="product">
<span fx-format="currency">{{ price }}</span>
</div>
</template>
Server-Side (Django, Rails, Laravel, PHP)
<!-- Django -->
<span fx-format="currency">{{ product.price }}</span>
<!-- Rails -->
<span fx-format="currency"><%= product.price %></span>
<!-- Laravel -->
<span fx-format="currency">{{ $product->price }}</span>
<!-- PHP -->
<span fx-format="currency"><?= $product['price'] ?></span>
HTMX Integration
genX and HTMX complement each other. HTMX loads content from the server, genX formats and enhances it. The MutationObserver automatically processes HTMX-swapped content:
<!-- Server returns raw values, genX formats them -->
<div hx-get="/api/products" hx-trigger="load">
<!-- Server responds with: <span fx-format="currency">29.99</span> -->
<!-- genX auto-formats to: $29.99 -->
</div>
<!-- Modal with server-loaded content -->
<button ux-opens="#user-modal" hx-get="/api/user/123" hx-target="#user-modal .ux-modal__body">
View User
</button>
<!-- Skeleton loading while HTMX fetches -->
<div hx-get="/api/items" hx-trigger="revealed" hx-indicator="#loading">
<div id="loading" ux-enhance="skeleton" ux-shape="rect"></div>
</div>
JavaScript API
Each module exposes a factory for programmatic use:
// Format a value without touching the DOM
window.FormatX.format('currency', 99.99, { currency: 'EUR' });
// Returns: "€99.99"
// Create reactive data programmatically
const { data } = window.bxXFactory.autoInit(document.body);
data.userName = 'Jane'; // Updates all bound elements
// Programmatic drag
window.DragX.startKeyboardDrag(element);
// Accessibility validation
const issues = window.AccessX.validate(document.body);
// Show a toast
window.UIX.toast('Saved!', { type: 'success' });
// Check table state
const state = window.tableX.getState(tableElement);
Browser Support
| Browser | Minimum Version |
| Chrome | 90+ |
| Firefox | 88+ |
| Safari | 14+ |
| Edge | 90+ |
Requirements: ES6+, Proxy, MutationObserver, IntersectionObserver, async/await
| Metric | Target |
| Bootloader scan (1000 elements) | <5ms |
| Element parsing (1000 elements) | <100ms |
| Total bootstrap | <105ms |
| Drag event processing | <0.5ms |
| Drop zone lookup (100 zones) | <1ms (quad-tree) |
| All operations | <16ms (60 FPS) |
Troubleshooting
Elements not formatting
- Check browser console for script loading errors
- Verify the
defer attribute is on the script tag
- Confirm attribute names use hyphens (
fx-format, not fx_format)
- Ensure values are valid (numbers for currency, ISO dates for dates)
Dynamic content not processed
- Elements must be appended to the DOM (not just created)
- Check for JavaScript errors blocking MutationObserver
- Call
window.genx.rescan() to manually trigger a rescan
Common console errors
| Error | Fix |
genX is not defined | Check script URL and network tab |
Invalid date | Use ISO format: 2024-03-15 |
NaN | Ensure numeric values are clean (no non-numeric characters) |
CircuitBreaker OPEN | A module had repeated failures. Check earlier errors and reload |
Module not loading
- Verify the entry attribute is present (e.g.,
fx-format not just fx-currency)
- Check the
genx:ready event detail for loaded modules
- If using
modulePaths, verify the URLs are correct