quell Light JS v1.0.0

quell-light.js is not a component library. It does not render anything. It does not manage state in the application sense. Its only mandate is behavioral stabilization: the small class of problems that CSS cannot solve safely on its own — runtime environment normalization, accessible attribute synchronization, modal focus isolation, and performant scroll tracking.

quell-light.js should feel like an extension of the browser’s own engine: invisible, unopinionated, and side-effect-free beyond its narrow contract

Overview & System Role

quell-light.js is the behavioral companion layer for the quell Light Architectural track.

It is intentionally not a component framework, rendering engine, state manager, virtual DOM abstraction, or UI runtime. Its responsibility is limited to the small set of browser behaviors that cannot be solved safely through CSS alone.

The file exists to provide:

  1. Runtime environment normalization
  2. Accessible disclosure state synchronization
  3. Modal focus containment and restoration
  4. Scroll-spy navigation tracking

The implementation is deliberately constrained by four architectural rules:

Constraint Description
Zero Dependencies No third-party libraries, utilities, polyfills, or frameworks
Sovereign Implementation Uses only native browser Is
Single Responsibility Every module solves exactly one browser behavior problem
CSS Parity JavaScript never becomes a second rendering system; CSS remains the visual source of truth

The script behaves as an extension of the browser rather than an application runtime.

Module Architecture Matrix

Module Name Main Function Core DOM Event Trapped Target Elements Affected Volume Context
Environment Normalize measureScrollbarWidth(), initEnvironmentNormalization(), debounce(), resize, keydown :root, <body> Volume I · Behavioral Foundations
Disclosures / UI Toggles initDisclosureToggles(), handleToggleClick(), setToggleState(), closeSiblingsInGroup() click [data-q-toggle], [data-q-active], [data-q-exclusive] Volume II · State Through Structure
Keyboard Focus Management initFocusManagement(), watchDialogActivation(), activateTrap(), deactivateTrap(), handleFocusTrapKeydown(), handleEscapeKeydown(), handleDismissClick(), closeDialog() “keydown, click, MutationObserver mutations <dialog>, [role=""dialog""], [role=""alertdialog""], [data-q-dismiss] Volume III · Advanced Interaction
Scroll Spy Tracking initScrollSpy(), handleSpyIntersections(), activateSpyLink() IntersectionObserver threshold crossings [data-q-spy], [data-q-spy-link] Volume III · Advanced Interaction

Internal Utility Layer

The Internal Utility Layer provides lightweight, dependency-free foundational helpers required by the high-level behavior modules. These utilities operate quietly behind the runtime environment, ensuring visibility-aware focus tracking and performant execution boundaries without leaking into the public API surface.

Utility Architecture Matrix

Utility Name Parameters Internal State Primary Consumer Architectural Purpose
getFocusableChildren root (Element) None Module 3: Keyboard Focus Management Constructs a visibility-aware snapshot of keyboard-reachable DOM nodes, filtering out hidden or inert elements to guarantee strict ARIA containment compliance.
debounce fn (Function), wait (Number) timer (Identifier) Module 1: Environment Normalization Restricts execution frequency of layout-heavy paint measurements during continuous, erratic browser window resize cycles.

Function: getFocusableChildren(root)

Contract

function getFocusableChildren(root)

Returns an ordered array of focusable descendants.

Included Selectors

a[href]
area[href]
button:not([disabled])
input:not([disabled]):not([type="hidden"])
select:not([disabled])
textarea:not([disabled])
details > summary:first-of-type
[tabindex]:not([tabindex="-1"])
audio[controls]
video[controls]
[contenteditable]:not([contenteditable="false"])

Filtering Rules

Excluded when:

  • hidden === true
  • Inside ancestor [hidden]
  • display: none
  • visibility: hidden

Rationale

Focus management must reflect actual keyboard reachability rather than DOM presence. The function creates a visibility-aware focus model suitable for accessibility enforcement.


Function: debounce(fn, wait)

Contract

function debounce(fn, wait)

Internal timer-based debounce utility.

State

let timer;

Usage

Only used for scrollbar-width recalculation during resize.

Rationale

Avoids repeated layout work during continuous viewport resizing.


Module 1 — Environment Normalization

API Snippet & Contract

State

let _lastViewportWidth = -1;

Functions

function measureScrollbarWidth()
function initEnvironmentNormalization()

Runtime Measurements

window.innerWidth
document.documentElement.clientWidth

CSS Variable Output

--q-scrollbar-width

Applied through:

document.documentElement.style.setProperty(
  '--q-scrollbar-width',
  `${scrollbarWidth}px`
);

Events Registered

Resize

window.addEventListener(
  'resize',
  debounce(measureScrollbarWidth, 150),
  { passive: true }
);

Keyboard Detection

window.addEventListener(
  'keydown',
  handleFirstTabKeypress
);

Keyboard Modality State

On first:

Tab

keypress:

document.body.setAttribute(
  'data-q-keyboard',
  'true'
);

Listener then removes itself.


Failsafe & Edge Cases Handled

Overlay Scrollbars

Math.max(0, delta)

Prevents negative widths.

Duplicate Measurements

Viewport width caching prevents unnecessary recalculation.

Passive Resize Handling

Ensures browser compositing is never blocked.

One-Time Keyboard Detection

Listener self-destructs after first successful detection.


Rationale

Environment normalization solves the browser-state problems CSS cannot independently measure.

The module exposes information to CSS rather than styling elements directly. JavaScript computes; CSS decides presentation.


Module 2 — Disclosures / UI Toggles

API Snippet & Contract

Functions

function initDisclosureToggles()
function handleToggleClick(event)
function setToggleState(trigger, targetEl, open)
function closeSiblingsInGroup(group, activeTrigger)

Delegated Event

document.body.addEventListener(
  'click',
  handleToggleClick
);

Trigger Attribute

data-q-toggle="target-id"

Target Resolution

document.getElementById(targetId)

Trigger State

aria-expanded="true"
aria-expanded="false"

Target State

Open:

data-q-active

Closed:

data-q-active removed

Exclusive Group Mechanics

Container:

data-q-exclusive

Lookup:

trigger.closest('[data-q-exclusive]')

Sibling Search:

group.querySelectorAll('[data-q-toggle]')

Behavior:

  • Active sibling remains open
  • Other expanded siblings close
  • Nested exclusive groups remain isolated

Failsafe & Edge Cases Handled

Missing Target ID

if (!targetId) return;

Missing DOM Target

console.warn(
  `[Quell] data-q-toggle target not found: #${targetId}`
);

Execution continues safely.

Nested SVG/Icon Clicks

Uses:

event.target.closest('[data-q-toggle]')

to resolve trigger ownership correctly.


Rationale

Disclosure state is centralized into a single delegated listener.

This provides:

  • Dynamic DOM compatibility
  • Constant listener overhead
  • Consistent ARIA synchronization
  • No per-component initialization

Module 3 — Keyboard Focus Management

API Snippet & Contract

Internal State

let _activeDialogEl = null;

Opener Registry

const _dialogOpenerMap = new WeakMap();

Functions

function initFocusManagement()
function watchDialogActivation()
function activateTrap(dialogEl)
function deactivateTrap(dialogEl)
function handleFocusTrapKeydown(event)
function handleEscapeKeydown(event)
function handleDismissClick(event)
function closeDialog(dialogEl)

Events Registered

Focus Trap

document.addEventListener(
  'keydown',
  handleFocusTrapKeydown
);

Escape Dismiss

document.addEventListener(
  'keydown',
  handleEscapeKeydown
);

Dismiss Trigger

document.body.addEventListener(
  'click',
  handleDismissClick
);

MutationObserver Contract

Observed Attribute:

data-q-active

Observer Configuration:

{
  subtree: true,
  attributes: true,
  attributeFilter: ['data-q-active']
}

Dialog Types Accepted:

<dialog>
role="dialog"
role="alertdialog"

Focus Trap Mechanics

Focus Snapshot

getFocusableChildren(_activeDialogEl)

Tab Wrapping

Forward:

last -> first

Backward:

first -> last

Empty Dialog Handling

If no focusable elements exist:

event.preventDefault()

Focus cannot escape.


Activation Strategy

Priority Order:

1

[autofocus]

2

First focusable descendant

3

Dialog container


Focus Restoration

Stored:

_dialogOpenerMap.set(
  dialogEl,
  currentFocus
);

Restored:

opener.focus({
  preventScroll: true
});

Dismiss Mechanics

Trigger:

data-q-dismiss

Ancestor Search:

dialog,
[role="dialog"],
[role="alertdialog"]

Native Dialog Integration

Open:

showModal()

Close:

close()

Both guarded for safe execution.


Failsafe & Edge Cases Handled

No Active Dialog

Immediate return.

Missing Dialog Ancestor

Warning issued.

Dialog Removed From DOM

WeakMap prevents memory leaks.

Already Open Native Dialog

showModal() exceptions safely ignored.

Focus Return Validation

document.contains(opener)

verified before focus restoration.


Rationale

This module enforces WCAG-compliant modal behavior while remaining independent from visual implementation.

Focus containment, opener tracking, and return-focus logic are implemented as browser-level behavioral guarantees rather than component-level assumptions.


Module 4 — Scroll Spy Tracking

API Snippet & Contract

State

let _spyRootMargin = '-10% 0px -10% 0px';
const _spyRatioCache = new Map();

Functions

function initScrollSpy()
function handleSpyIntersections(entries)
function activateSpyLink(activeId)

Observer Configuration

new IntersectionObserver(
  handleSpyIntersections,
  {
    root: null,
    rootMargin: _spyRootMargin,
    threshold: [0, 0.4]
  }
);

Observed Elements

Sections:

data-q-spy="section-id"

Navigation Links:

data-q-spy-link="section-id"

Ratio Cache

Stores:

Map<Element, number>

Purpose:

  • Avoid repeated DOM lookups
  • Maintain state between observer batches
  • Preserve ratios for unchanged sections

Activation Threshold

0.4

Minimum visibility required:

40%

Failsafe & Edge Cases Handled

No Spy Targets

Initialization exits immediately.

Multiple Visible Sections

Highest ratio wins.

No Valid Section

All active states cleared.

Dynamically Added Navigation

Links are queried fresh every activation cycle.


Rationale

Scroll tracking is delegated entirely to browser-native visibility observation.

No:

  • Scroll listeners
  • requestAnimationFrame polling
  • getBoundingClientRect loops

This produces scalable navigation awareness with minimal runtime overhead.


Specificity & State Control Mechanics

A defining architectural principle of Quell Light is that JavaScript never becomes a styling engine.

CSS State Channels

Environment

--q-scrollbar-width

Keyboard Navigation

data-q-keyboard="true"

Disclosure Open State

data-q-active

Disclosure Accessibility State

aria-expanded="true|false"

Scroll Spy Current State

data-q-current="true"

Scroll Spy Accessibility State

aria-current="location"

Dialog Open State

data-q-active

No module injects inline visibility styles, dimensions, positioning rules, colors, spacing values, transforms, animations, or layout directives.

JavaScript emits state.

CSS interprets state.


Public API Surface

A defining architectural principle of quell Light is that JavaScript never becomes a styling engine.

CSS State Channels

Environment

--q-scrollbar-width

Keyboard Navigation

data-q-keyboard="true"

Disclosure Open State

data-q-active

Disclosure Accessibility State

aria-expanded="true|false"

Scroll Spy Current State

data-q-current="true"

Scroll Spy Accessibility State

aria-current="location"

Dialog Open State

data-q-active

No module injects inline visibility styles, dimensions, positioning rules, colors, spacing values, transforms, animations, or layout directives.

JavaScript emits state.

CSS interprets state.


Initialization

quell.init()

Protected by:

let _initialized = false;

Duplicate calls:

console.warn(...)

and safely exit.


Dialog Control

quell.openDialog(elOrId)
quell.closeDialog(elOrId)

Accepted:

Element

or

String ID

Scroll Spy Configuration

quell.setSpyRootMargin(margin)

Updates:

_spyRootMargin

Teardown

quell.destroy()

Removes:

  • Toggle click listener
  • Dismiss click listener
  • Focus trap listener
  • Escape listener

Resets:

_initialized
_activeDialogEl
_lastViewportWidth
_spyRatioCache

Known v1 limitation:

  • Observer instances are not retained for disconnect()

Auto Initialization Contract

Disabled Through:

<script>
window.__quellNoAutoInit = true;
</script>

Auto-init Path:

DOMContentLoaded

Already-loaded DOM Path:

quell.init()

called synchronously.


Key Architectural Decisions

  • Native browser APIs are preferred over abstractions.
  • Event delegation minimizes listener count and supports dynamic DOM injection.
  • MutationObserver decouples focus management from disclosure mechanics.
  • WeakMap prevents dialog opener memory leaks.
  • IntersectionObserver eliminates scroll-event polling.
  • ARIA attributes serve as the accessibility source of truth.
  • CSS custom properties expose environment measurements without style injection.
  • State is represented through attributes rather than imperative styling.
  • Focus restoration follows ARIA Authoring Practices guidance.
  • Scroll-spy maintains a single-current-link model.
  • Modal containment enforces WCAG keyboard accessibility expectations.
  • Initialization is idempotent and protected from double bootstrapping.
  • Behavioral parity is maintained with the Quell CSS architecture rather than creating an independent runtime layer.
  • The framework remains dependency-free, sovereign, portable, and browser-native.

Quell Design Summary

quell Light treats JavaScript as a behavioral stabilizer, not a rendering authority. The browser remains the platform, CSS remains the layout engine, ARIA remains the accessibility contract, and JavaScript supplies only the runtime intelligence required to bridge the gaps that CSS cannot safely solve alone.


© 2026 Ortiz Design Studio. | quell system documentation.