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">&times;</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

  1. Scan — The bootloader scans the DOM for genX attributes (fx-*, bx-*, ux-*, etc.)
  2. Detect — Identifies which modules are needed based on attribute prefixes
  3. Load — Fetches only the required modules from CDN (parallel loading)
  4. Parse — Runs the parser pipeline (JSON > Colon > Verbose > Class) to extract configuration
  5. Init — Initializes each module with parsed config. Prefers autoInit() (DATAOS pattern) to extract initial state from the DOM
  6. 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

PrefixOrder
fxformat, currency, decimals, symbol
bxbind, debounce, throttle
axlabel, icon, value, role
dxtype, id, style, group
lxstrategy, duration, mode
nxtype, 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 PrefixModule
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

AttributeTypeDefaultDescription
fx-formatstringRequired. The format type (see table below)
fx-typestringautoInput type conversion before formatting
fx-valuestringOverride source value (instead of textContent)
fx-decimalsnumber2Decimal places
fx-currencystringUSDISO 4217 currency code
fx-phone-formatstringusPhone format style
fx-date-formatstringshortDate format style
fx-time-formatstringshortTime format style
fx-duration-formatstringshortDuration format style
fx-binarybooleanfalseUse binary (1024) units for filesize

Format Types

ValueOutput ExampleOptions
currency$1,299.99fx-currency, fx-decimals
number1,234,567fx-decimals
percent15.6%fx-decimals (default: 0)
accounting($1,234.56)fx-currency
scientific1.23e+3fx-decimals
abbreviated1.5Mfx-decimals (default: 1)
compact1.5 millionUses Intl.NumberFormat
fraction2 1/2Auto-detects or custom denominator
filesize1.50 MBfx-binary, fx-decimals
duration1d 2h 3mfx-duration-format
date3/15/2024fx-date-format
time2:30 PMfx-time-format
datetime3/15/24, 2:30 PM
relative2 days ago
phone(555) 123-4567fx-phone-format
ssn***-**-1234Masked by default
creditcard****-****-****-1234Masked by default
uppercaseHELLO WORLD
lowercasehello world
capitalizeHello World
truncateLong text th...length (default: 50), suffix (default: "...")

Date Format Styles

StyleOutput
short (default)12/31/2024
mediumDec 31, 2024
longDecember 31, 2024
fullWednesday, December 31, 2024
iso2024-12-31
customUses pattern: YYYY, MM, DD, HH, mm, ss

Phone Format Styles

StyleOutput
us (default)(555) 123-4567
us-dash555-123-4567
us-dot555.123.4567
intl+1 555 123 4567

Duration Format Styles

StyleOutput (for 90061 seconds)
short (default)1d 1h 1m 1s
medium1 day 1 hr 1 min 1 sec
long1 day, 1 hour, 1 minute, 1 second
compact / clock1:01:01:01 (D:HH:MM:SS)

Input Type Conversions

Use fx-type to convert raw values before formatting:

TypeConversion
cents / penniesDivide by 100 (integer cents to dollars)
unix / timestampSeconds since epoch × 1000
milliseconds / msMilliseconds since epoch
seconds / minutes / hoursDuration input conversion to seconds
bytes / kb / mb / gbFile size input conversion to bytes
decimal / fractionPercent: 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

  1. fx-raw attribute (cached after first format)
  2. fx-value attribute
  3. Element's textContent
  4. 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:

TypePurposeKey Attributes
srOnlyScreen reader-only textax-sr-text
labelAuto ARIA labels (icon, abbreviation, currency, date)ax-label
liveLive region for dynamic contentaria-live set to polite or assertive
fieldForm field with error/help text, character countax-error, ax-help
navNavigation with active link detectionax-nav-label, ax-current
buttonButton accessibility (role, keyboard, loading state)ax-label, ax-loading
tableTable headers, caption, sortable columnsax-caption, ax-sortable
imageAlt text, decorative images, long descriptionsax-decorative, ax-description
modalDialog with focus trap, Escape to closeAuto label from heading
skipLinkSkip-to-main-content linkDefault target: #main
landmarkLandmark roles (main, search, region)ax-role, ax-landmark
focusEnhanced focus indicators & focus trapping
keyboardNavArrow key navigation with multi-selectSupports Shift+Arrow, Ctrl+A
tooltipAccessible tooltipsax-tooltip
inputAuto-label inputs from placeholder/name
errorError announcements (role="alert")
statusStatus announcements (role="status")

Attributes Reference

AttributeDefaultDescription
ax-enhanceRequired. Enhancement type (see above)
ax-labelARIA label text
ax-sr-textScreen reader-only text content
ax-errorError message for form fields
ax-helpHelp text for form fields
ax-loadingfalseButton loading state (aria-busy, aria-disabled)
ax-captionTable caption text
ax-sortableOn <th> elements: make column sortable
ax-decorativefalseMark image as decorative (empty alt, aria-hidden)
ax-descriptionLong description for images
ax-roleregionLandmark role
ax-tooltipTooltip text content
ax-nav-labelNavigationARIA label for nav elements
ax-currentMarks current page link

Keyboard Navigation (keyboardNav)

KeyAction
Arrow Down / UpMove focus between items
Home / EndJump to first / last item
SpaceToggle selection (multiselect)
Shift+ArrowRange selection
Ctrl/Cmd+ASelect all items
Ctrl/Cmd+ArrowMove 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

AttributeDescriptionExample
bx-modelTwo-way binding (input ↔ data). Supports dot-notation paths and optional debounce.bx-model="user.name"
bx-model="search:300"
bx-bindOne-way binding (data → DOM). Updates textContent (or .value for inputs).bx-bind="user.fullName"
bx-computedComputed expression with auto-dependency tracking. Requires bx-bind on same element.bx-computed="price * qty"
bx-ifConditional display. Expression returning truthy/falsy controls visibility.bx-if="items.length > 0"

Input Type Handling

Input TypeReadWrite
checkboxel.checked (boolean)el.checked = bool
radioel.value (only if checked)el.checked = (el.value === val)
number / rangeparseFloat(el.value)el.value = String(val)
text / textareael.valueel.value = String(val)
selectel.valueel.value = String(val)

Form Validation

AttributeDescription
bx-formWrap a <form> to enable validation and state tracking
bx-validateValidation rules (space-separated or JSON)
bx-form-submitGlobal function name called on valid submit
bx-error-{rule}Custom error message for a specific rule

Validation Rules

RuleDescription
requiredNon-empty value
emailValid email format
urlValid URL
numberValid number
integerValid integer
alphaLetters only
alphanumericLetters and digits only
phonePhone number (min 10 digits)
min:NNumeric minimum
max:NNumeric maximum
minLength:NMinimum string length
maxLength:NMaximum string length
pattern:regexRegex pattern match
custom:fnNameCustom 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)

ClassApplied To
.bx-pristine / .bx-dirtyForm element
.bx-valid / .bx-invalidForm and individual fields
.bx-errorInvalid fields

Events

EventDetailWhen
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

AttributeTypeDefaultDescription
dx-draggablestringRequired. Type identifier for the draggable element
dx-dataJSON{}Custom data payload (passed to drop handler)
dx-handleCSS selectorRestrict drag initiation to a handle element
dx-ghostbooleanfalseShow ghost image during drag (canvas-based)
dx-snapnumberSnap to grid (px). Empty or "true" = 10px
dx-axisstringhorizontal or vertical constraint
dx-revertstringneveralways, invalid, or never

Drop Zone Attributes

AttributeTypeDefaultDescription
dx-drop-zonestringRequired. Drop zone identifier
dx-acceptsstring*Comma-separated list of accepted draggable types. * = accept all
dx-prioritynumber0Higher priority wins when zones overlap
dx-sortbooleanfalseEnable sortable behavior within the zone

Events

EventFired OnDetail
dx:dragstartDraggable element{ element, type, data, x, y }
dx:dropDrop zone{ element, dropZone, type, data, x, y }
dx:dragendDraggable element{ element, dropZone, success, cancelled }

CSS Classes (auto-applied)

ClassApplied When
dx-draggingElement is being dragged
dx-drag-overDrop zone has a draggable hovering over it
dx-keyboard-draggingElement is being dragged via keyboard

Keyboard Accessibility

KeyAction
Arrow keysMove dragged element
TabCycle to next drop zone
EnterDrop on current/focused zone
EscapeCancel 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>
nx-*

Client-side navigation, breadcrumbs, tabs, scroll spy, sticky headers, mobile menus, dropdowns, and pagination.

Feature Types

AttributePurpose
nx-navNavigation with active link tracking
nx-breadcrumbHierarchical breadcrumbs (auto, trail, or data-parent)
nx-tabsAccessible tab navigation (arrow keys, Home/End)
nx-scroll-spyHighlight nav links as sections scroll into view
nx-stickySticky header with stuck-state detection
nx-mobileHamburger menu with focus trap
nx-dropdownDropdown menus with keyboard navigation

Navigation (nx-nav)

AttributeDefaultDescription
nx-nav-activeClassnx-activeCSS class for the active link
nx-nav-exactfalseRequire exact path match
nx-nav-smoothScrolltrueEnable smooth scrolling for anchor links
nx-confirmShow confirmation dialog before navigating
nx-shortcutKeyboard shortcut (e.g., ctrl+h)

Breadcrumbs (nx-breadcrumb)

AttributeDefaultDescription
nx-breadcrumbautoMode: hierarchical, trail, data-parent
nx-breadcrumb-separator>HTML separator between items
nx-breadcrumb-rootLabelHomeLabel for the root/home link
nx-breadcrumb-schemafalseInclude Schema.org structured data

Sticky Headers (nx-sticky)

AttributeDefaultDescription
nx-sticky-top0Top offset when stuck
nx-sticky-zIndex1000Z-index when stuck
nx-sticky-stickyClassnx-stuckCSS class when element is stuck

Scroll Spy (nx-scroll-spy)

AttributeDefaultDescription
nx-scroll-spy-threshold0.5IntersectionObserver threshold
nx-scroll-spy-activeClassnx-activeCSS class for active link

Mobile Menu (nx-mobile)

AttributeDefaultDescription
nx-mobile-preventScrolltruePrevent body scroll when menu is open
nx-mobile-closeOnClicktrueClose menu when a link is clicked
nx-mobile-closeOnOutsideClicktrueClose menu on outside click

Events

EventWhen
nx:navigateLink click in nx-nav
nx:tab-changeTab selected in nx-tabs
nx:page-changePage button clicked in pagination
nx:content-loadedLazy 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

AttributeTypeDefaultDescription
lx-strategystringspinner, skeleton, progress, or fade
lx-loadingbooleanMark element as having a loading state
lx-spinner-typestringcirclecircle, dots, or bars
lx-spinner-sizestringmediumsmall, medium, or large
lx-spinner-colorCSS colorSpinner color
lx-rowsnumberautoNumber of skeleton rows (auto-detects from content)
lx-animatebooleantrueAnimate skeleton shimmer
lx-valuenumberProgress bar current value
lx-maxnumber100Progress bar maximum
lx-progress-modestringdeterminatedeterminate or indeterminate
lx-durationnumber300Fade duration in ms
lx-messagestringLoading...Loading message text (fade strategy)
lx-urgentbooleanfalseUse aria-live="assertive" for announcements

Auto-Detection

loadX can automatically show loading states for async operations:

SourceDetection
Fetch APIIntercepts fetch() calls
XMLHttpRequestMonitors XHR open/send
HTMXListens to htmx:beforeRequest / htmx:afterSwap
FormsMonitors 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

AttributeTypeDefaultDescription
tx-sortablebooleanOn <table>: enable sorting on all columns. On <th>: enable on that column only.
tx-paginatebooleanEnable client-side pagination
tx-per-pagenumber10Rows per page
tx-filterablebooleanEnable search/filter input
tx-filter-columnsstringallComma-separated column names to search
tx-filter-debouncenumber300Filter input debounce delay (ms)
tx-virtualstringautotrue, false, or auto (auto-enables for >1000 rows)
tx-buffernumber10Buffer rows above/below viewport (virtual scroll)
data-valuestringOn <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)

ClassApplied To
tx-sortableSortable headers (cursor: pointer)
tx-sort-activeCurrently sorted column header
tx-sort-asc / tx-sort-descSort direction indicator
tx-paginationGenerated pagination container
tx-filter-inputGenerated search input

Events

EventWhen
tx:virtual-scrollViewport change during virtual scroll
tx:performancePerformance 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

AttributeTypeDefaultDescription
fx-format="smart"stringTrigger smart detection via fmtX
fx-smart-thresholdnumber75Minimum confidence (0–100) to apply formatting
fx-smart-typesstringallComma-separated list of allowed types

Detection Types & Confidence

TypePatternsConfidence Range
email[email protected]100
urlhttps://example.com100
percentage45.67%100
dateISO, US, named months75–98
currency$1234, 100 euros60–95
phoneInternational, US formats40–95
number1,234, 123.4550–85
textFallback0

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

CategoryComponents
Atomsbutton, badge, avatar, spinner, progress, skeleton, divider, tag, tooltip
Forminput, textarea, select, checkbox, radio, switch
Moleculescard, alert, modal, drawer, dropdown, accordion, tabs, popover, menu, breadcrumb, pagination
Organismsnav, sidebar, table, stepper, toast

Universal Attributes

AttributeDescription
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

AttributeValuesDescription
ux-variantprimary, secondary, outline, ghost, dangerVisual style
ux-sizesm, lgButton size
ux-blockbooleanFull-width button
ux-loadingbooleanDisabled with opacity
ux-hover-bgcolorCustom hover background
ux-active-bgcolorCustom 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

AttributeDescription
ux-titleAuto-generate a card header with this title
ux-elevatedRemove border, add box shadow
ux-hoverableLift effect on hover
ux-header-bg / ux-header-colorHeader 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

AttributeDefaultDescription
ux-sizemd (32rem)sm, md, lg, xl, full
ux-titleAuto-generate modal header
ux-close-on-escapetrueClose on Escape key
ux-close-on-backdroptrueClose 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

AttributeDescription
ux-tabsComma-separated tab names (auto-generates tab buttons)
ux-active-bg / ux-active-colorActive 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

AttributeDefaultDescription
ux-singlefalseOnly one section open at a time
ux-header-bgHeader 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:

AttributeActionWorks With
ux-opens="#id"Open a modal or drawermodal, drawer
ux-closesClose nearest modal/drawer/dropdown (or specify ux-closes="#id")modal, drawer, dropdown
ux-toggles="#id"Toggle open/close statedropdown, accordion (with ux-index)
ux-selects="#id"Select a tab (with ux-tab="name" or ux-index="0")tabs

Events

EventComponentsDetail
ux:openmodal, drawer, dropdown
ux:closemodal, drawer, dropdown
ux:changetabs, accordion, switchTabs: { 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:

PrefixModuleEntry Attributes
fxfmtXfx-format
axaccXax-enhance
bxbindXbx-model, bx-bind, bx-if, bx-form
dxdragXdx-draggable, dx-drop-zone
lxloadXlx-strategy, lx-loading
txtableXtx-sortable
nxnavXnx-tabs, nx-dropdown, nx-breadcrumb, nx-mobile, nx-scroll-spy, nx-sticky, nx-nav
sxsmartXLoaded when fmtX encounters fx-format="smart"
uxuiXux-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:

  1. JSON (highest priority) — {prefix}-opts='{"key":"value"}'
  2. Colon{prefix}-format="currency:EUR:2"
  3. Verbose — individual {prefix}-key="value" attributes
  4. 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 ClassWhen
GenXErrorBase class for all errors. Has code, message, context, timestamp
ParseErrorAttribute/config parsing failures
EnhancementErrorElement enhancement failures
ValidationErrorConfiguration 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

BrowserMinimum Version
Chrome90+
Firefox88+
Safari14+
Edge90+

Requirements: ES6+, Proxy, MutationObserver, IntersectionObserver, async/await

Performance

MetricTarget
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

  1. Check browser console for script loading errors
  2. Verify the defer attribute is on the script tag
  3. Confirm attribute names use hyphens (fx-format, not fx_format)
  4. Ensure values are valid (numbers for currency, ISO dates for dates)

Dynamic content not processed

  1. Elements must be appended to the DOM (not just created)
  2. Check for JavaScript errors blocking MutationObserver
  3. Call window.genx.rescan() to manually trigger a rescan

Common console errors

ErrorFix
genX is not definedCheck script URL and network tab
Invalid dateUse ISO format: 2024-03-15
NaNEnsure numeric values are clean (no non-numeric characters)
CircuitBreaker OPENA module had repeated failures. Check earlier errors and reload

Module not loading

  1. Verify the entry attribute is present (e.g., fx-format not just fx-currency)
  2. Check the genx:ready event detail for loaded modules
  3. If using modulePaths, verify the URLs are correct