Components and Modules Catalog
This is a practical catalog, not exhaustive class-level Javadoc.
Primitive Components
- Text/content:
Header,Paragraph,Markdown,Div,Code,Blockquote - Forms:
Form,TextInput,TextArea,Select,Checkbox,RadioGroup,Button - Display:
Card,CardGrid,DataTable,Table,Alert,Badge,Tag,InfoBox,Spinner,ProgressBar,Modal - Media:
Image,Gallery,Video,Audio - Navigation:
Link,NavBar,SideNav,Breadcrumb - Forum:
ForumCategoryRenderer,ForumTopicRenderer,ForumCommentRenderer,ForumCategoryData,ForumTopicData,ForumCommentData,ForumTopicTitleLink,ForumTagParser,ForumTagResolverRegistry - Chat:
ChatTranscriptRenderer,ChatMessageData,ChatMessageComponent,DefaultChatMessageComponent,ChatUiConfig,ChatTransportMode - Static content helper:
StaticContentSiteBuilder,StaticContentSite,ContentSectionConfig,ContentRouteIndex,ContentListItemComponent,DefaultContentListItemComponent
Common primitive contract:
- Prefer
create(...)factories where available. - Use fluent mutators for static structure during page composition.
- Use
render(RenderContext)for context-aware output andrender()only for empty-context output. - Text setters escape plain text by default;
RawHtmlandwithUnsafeHtml(...)are trusted-input paths.
Form primitives:
Formowns method/action/HTMX post wiring and CSRF helper output.TextInput,TextArea,Select,Checkbox, andRadioGrouprender name/value controls for normal@RequestParamform handling.Buttonis a command primitive; attach HTMX attributes explicitly or through module/builders.
Navigation primitives:
Linkvalidates its typed href setters against its http/https/mailto/tel/relative policy.- NavBar, SideNav, Breadcrumb, Dropdown and AccountWidget destinations are application-controlled and attribute-encoded, without Link's scheme policy. See the method-specific URL boundaries.
Layout Components
PageRowColumnGridContainerSection
Layout contract:
Row.withChild(...)wraps non-column content in a default.col.Row.addColumn(...)appends a preconfiguredColumn.Row.copy()shallow-copies row attributes/text/children. EditableRow does not use it: supplied and registered rows remain separate.Column.withWidth(1..12),auto(), andfill()own grid sizing.Grid.withColumns(1..6)validates column bounds;Grid.withGap(...)retains caller spelling.
Module Components
ContentModuleFormModuleDataModuleGalleryModuleHeroModuleSimpleListModuleChatModuleEditableModule(wrapper/decorator)
Module contract:
- Modules build lazily and cache structure until an explicit supported rebuild/invalidation. SimpleList add/remove are configuration-only, not automatic invalidation.
- Consumers should not call module width helpers; use
Row/Columnlayout controls instead. ContentModule,RichContentModule, andSimpleListModuleimplement editing contracts for common content-editing flows.EditableModuledecorates another module with edit/delete controls; endpoint authorization remains the application controller's responsibility.
Selection Guidance
- Start with primitives for one-off UI elements.
- Use modules for reusable business sections.
- Wrap modules with
EditableModulewhen edit controls are needed. - Keep layout responsibilities in
Row/Column, not in module width settings.
Layout and module contract notes
- Row modifiers and Container/Section class helpers preserve base/custom classes. Grid updates
preserve whole custom tokens and accept column counts
1..6; gap spelling remains caller-owned. Column's sizing/class helpers retain their documented replacement semantics. StatsModule.withColumns(...)accepts1..6and emits CSS-backedstats-cols-*classes.ComparisonModulerequires each row to provide exactly one value per configured column.- Custom renderers treat
render(RenderContext)as canonical and forward context to children; zero-argument overrides delegate to an empty context.
Rendering and control details
Generated children (including card sections, table rows, select options, and radio controls)
are included in compiled templates. Custom renderers retain their render(RenderContext)
behavior. Code blocks escape both code and title text; modal body/footer content receives
the render context.
TextArea
For escaped static text and text slots beginning with LF or CR, direct and compiled
textarea output prepend one LF to compensate for native HTML parsing consuming an initial
LF. Browsers normalize CR and CRLF to LF in textarea values; this is not exact raw newline
preservation. Trusted HTML and children are unchanged, so this does not repair arbitrary markup.
Do not add a separate padding LF when supplying ordinary text.
Paragraph
Alignment helpers replace only the known whole align-left, align-center, align-right
and align-justify tokens in temporary output, preserving other tokens and their order.
Logical class aliases such as CLASS are recognized. Stored attributes/children remain unchanged;
text and inherited children retain normal escaping, slots and context dispatch. Its compiled
renderer stays live, including later alignment or text configuration.
Divider
Divider derives classes/styles without changing stored configuration. Horizontal output reserves
these exact lowercase inline properties and the whole outer divider-with-text token:
| Text state | Active generated properties | Suppressed properties/token |
|---|---|---|
null |
border-top, width, display, line-height |
position, text-align, margin, divider-with-text |
| Any non-null string, including empty | position, text-align, margin, divider-with-text |
border-top, width, display, line-height |
Active generated values override the same canonical caller property. Inactive names/token are
suppressed from temporary output even when caller-supplied, while stored attributes survive.
Vertical output generates only border-left and display, ignores text and performs no inactive
cleanup or ownership of the outer text token. Vertical height remains configurable;
withHeight on a horizontal divider is still a no-op.
Logical CLASS/STYLE aliases work, but CSS names and class tokens are case-sensitive for this
cleanup. Outside declarations/classes, case variants (WIDTH), height, custom properties,
border shorthands/longhands and construction/opposite-orientation tokens remain caller-owned.
Cleanup does not restore caller-replaced base classes or prevent every possible CSS conflict.
Generated escaped text precedes inherited children and owns divider-text on its span.
Templates retain the renderer live, including text/style configuration changes.
Migration: do not rely on inactive canonical inline names or the reserved outer token surviving in a horizontal divider's rendered output. Put independent styling on a surrounding component or use a non-reserved declaration. No historical render styling accumulates on the configured object. Finish configuration before sharing; custom children and contexts retain separate obligations.
SimpleListModule
Configure items before building. addItem and removeItem update only the model and current
editable descriptors; applyEdits explicitly rebuilds existing markup. Raw non-null item ids
are literal lookup identities; positional item-N descriptors for unassigned ids are separate
and may renumber. See item identity and lifecycle.
Modal
Modal.create() renders a .modal-backdrop containing a .modal-container dialog.
withModalId(...) accepts IDs starting with a letter followed by letters, digits, hyphens
or underscores. withTitle(...) supplies escaped visible text and the dialog's accessible
name through aria-labelledby. Missing or blank titles use aria-label="Dialog";
showCloseButton(false) with no title omits the header.
Load /css/framework.css and /js/framework.js once in the page shell (ShellBuilder does
this by default). On initial rendering, direct DOM insertion or HTMX insertion, focus moves
to a usable autofocus element, otherwise the first body control, any dialog control, or
the dialog container when there are no controls. Tab/Shift+Tab stay inside the top dialog.
Native disclosure summaries participate in tab order unless given a negative tabindex.
Closed details content is excluded, including nested disclosures; its first summary remains
reachable when visible. Enter/Space retain native disclosure activation.
Initial and keyboard focus reveal the chosen control through the dialog's internal scroll
containers without scrolling the background page.
Only the top dialog handles Escape, backdrop and .modal-close clicks. All three close
options default to enabled; closeOnEscape(false), closeOnBackdrop(false) and
showCloseButton(false) independently disable their corresponding paths. A disabled
Escape action does not close an underlying dialog. Clicking inside does not dismiss it.
Closing or removing a dialog, including HTMX/OOB replacement, restores its still-connected, usable launcher. Replacing an edit dialog retains its original launcher; closing a nested dialog returns to its launcher in the underlying dialog. If that launcher was removed, focus stays in the remaining top dialog when present. Applications must provide a close or completion path if they disable all dismissal options, and should supply a descriptive title rather than relying on the generic fallback. These behaviors are not a claim of complete application accessibility or application-owned authorization.
Alert dismissal
Alert.info(...), success(...), warning(...) and danger(...) select styling, not
an announcement policy. Alerts do not automatically receive a role or live-region attributes;
use existing attribute APIs when the application needs those semantics.
dismissible() configures one native type="button" close control, visibly marked × and
named Dismiss alert. Repeated calls do not duplicate it. Finish configuration before
compiling a template or sharing the component. Non-dismissible Alerts retain their output.
Load /css/framework.css and /js/framework.js once in the page shell (ShellBuilder does
this by default). Click/touch and native Enter/Space hide only the nearest usable dismissible
Alert with hidden. The Alert remains in the DOM; dismissal is not persisted across rendering
or navigation. Cancelled events, disabled/wrong-type controls, unavailable Alerts and controls
behind the top Modal do not dismiss an outer Alert. Direct and HTMX-inserted Alerts work without
reinitialization. The close control does not submit a containing form.
When the hidden Alert contained focus, an application-selected replacement is preserved.
Inside the top Modal, focus moves only to that dialog's container, without closing the dialog,
restoring its launcher or scrolling the page/backdrop. On ordinary pages, the framework tries
native focus on surviving, usable containing elements captured before focus callbacks. Only
when native focus fails may an absent tabindex be borrowed as -1. Existing caller values,
including empty or malformed ones, are never changed. Borrowed state remains while focus
needs it and is released on blur, failed focus or disconnection; later caller mutations,
including same-value writes, take ownership and are preserved. If that borrowed container
then opens a Modal, it can receive focus again when the dialog closes or is removed.
Unrelated focus is not redirected, and the framework does not choose an application next action.
Default dismissal layout reserves space for the control, wraps long messages and provides readable control/hover paint and a visible keyboard focus ring on all four severity backgrounds. Host theme/layout overrides, application focus flows and role/live-region semantics remain application responsibilities. These defaults are not whole-application accessibility certification.
Form labels and radio groups
Form.addField(label, control) associates a nonempty label with TextInput, TextArea,
Select, Checkbox or Button. It preserves an existing ID (including one set through
withAttribute) and assigns a unique, stable ID during field assembly when absent. For Checkbox,
withId controls the internal input; generic attributes still belong to the wrapper. A checkbox
field targets the internal input, not its wrapper. Configure IDs before addField: changing a
control ID afterward does not retarget the already assembled field label. Checkbox's own
withLabel relationship still follows its ID setters.
For RadioGroup, the field caption names the radiogroup through aria-labelledby; each
option keeps its individual label. Standalone groups need an application-supplied aria-label
or aria-labelledby. Option IDs are generated per instance: do not reconstruct them from
values. required() applies to later options too. Arbitrary/custom or multi-control components
receive only a visible caption; applications must compose their own control labels/names.
addField does not inspect rendered HTML or guess a target inside a custom component.
Tabs and accordions
Tabs and accordions generate unique fallback IDs. Supply module IDs for predictable selectors.
Tabs have one tab stop per owned tablist. With framework assets loaded, aria-orientation
follows the computed navigation layout on initialization, resize and dynamic insertion:
horizontal tabs use ArrowLeft/ArrowRight, while the default vertical layout through 768px
uses ArrowUp/ArrowDown. These arrows wrap and automatically activate the next owned tab;
Home/End select the first/last. Perpendicular arrows and modified keys retain native behavior.
Enter/Space use native button activation. Orientation synchronization preserves selection,
focus and nested state; activation keeps roving tabindex and panel hidden state consistent.
Server markup starts with horizontal orientation because the viewport is unknown during SSR.
Accordion regions are named by their own header buttons. Each .accordion-content panel
contains an .accordion-body wrapper for text or custom components. Default framework CSS
uses intrinsic expanded height without a fixed content cap, accommodates later content growth,
and does not clip child disclosures at the accordion's own item or panel boundaries.
Closed panels start with hidden, inert and aria-hidden="true". With framework assets loaded,
native header button activation (including Enter/Space) synchronizes these attributes with
aria-expanded and the active/expanded classes. If focus is inside a panel being closed,
it returns to that panel's header before descendants become unavailable. After the complete
transition, the framework reveals that header with nearest scrolling. Inside a Modal, reveal
is limited to dialog-internal scroll/clipping boundaries without scrolling the page or backdrop;
oversized headers use nearest alignment rather than guaranteed full visibility. Single-expand behavior
is scoped to the nearest owning .accordion; allowMultiple() permits multiple open items
within that owner. Closing an outer panel preserves nested expansion state for reopening.
Application styles or surrounding containers can still constrain height, clip disclosures or change contrast; applications must validate those custom layouts and palettes. These defaults are not a claim of whole-application accessibility conformance.
Dropdown and account disclosure navigation
Dropdown.build() and authenticated AccountWidget use named, non-submit buttons with
aria-controls/aria-expanded. Their closed panels are hidden and cannot receive keyboard focus.
Load framework JS/CSS once: click/touch and native Enter/Space toggle; ordinary Tab navigation
reaches links/custom items; outside clicks dismiss; Escape inside an open disclosure closes it
and returns focus to its trigger. A disclosure inside a modal handles its first Escape before
the outer dialog. These are disclosure navigation components, not ARIA menus; custom items keep
their native behavior. Choose alignment/placement that keeps open panels inside the host's visible
area; disclosure behavior does not reposition panels across clipping/scrolling ancestors.
Each built dropdown output has its own IDs; a configured account widget
keeps stable IDs across renders. Account rendering no longer clears inherited children and
forwards their request context. Finish configuration before sharing; retained custom child
delegates and contexts must independently be safe to share.
Progress and media
ProgressBar requires a positive maximum and clamps the rendered value to it. The .progress
root carries the progressbar role, value/min/max and accessible name. withLabel supplies both
visible fill text and the name; the default name is Progress. Use existing withAttribute
for a more descriptive aria-label or external aria-labelledby relationship.
Default height is 20px; withHeight sets the wrapper height. striped() and animated() use
progress-bar-striped/progress-bar-animated; animation implies stripes and is disabled for
reduced motion. CSS retains the legacy container/stripe/animation aliases. Default labeled
variants use white text on the darker blue default/primary fills and dark text on success,
warning, error and info fills. These foregrounds and gradients maintain at least 4.5:1 text
contrast across the default gradient and stripe paints. Application color/theme overrides
must recheck their own contrast. Audio/video withControls(false) removes controls.