Semantic HTML is the public API, CSS custom properties are the customization API, and one optional script adds interactive behavior. Everything on this page applies to version 1.2.0.
Copy smartwebui.css into your project and link it. Add smartwebui.js when you need interactive behaviors such as tabs, the custom select, dialogs, menus, and toasts. There is no installation, build step, compiler, or dependency.
Components are ordinary semantic HTML with class names. The library works with any server stack. PHP can emit the same markup as any other host — there is no Composer package, PHP extension, or compiler. An optional single-file renderer, smartwebui-surface.php, is included for agent surfaces (PHP 8.0+, no dependencies). Styling holds up before JavaScript loads.
Design choices & tradeoffs
smartwebUI is not a React replacement. It is a trusted UI layer for applications where HTML stays authoritative and agents compose within a catalog — not arbitrary markup.
HTML is the runtime; JSON is the agent wire format
Humans, server templates, and agents can all write semantic HTML with smart-* classes. Alternatively, agents can emit validated JSON surfaces that render to the same HTML through the DOM API — the wire format rejects HTML strings inside JSON, not HTML as a medium. There is no third API — the catalog is a subset of what you can hand-author.
When smartwebUI fits
PHP or server-rendered apps that want polished components without a JS framework
Admin tools, internal dashboards, and agent-built operator interfaces
Progressive enhancement: server first paint → adopt() → live patches
Teams that accept a 36-month browser window in exchange for modern APIs and less legacy weight
When to reach for something else
Rich client-side SPAs with complex shared state — React, Vue, or Svelte component ecosystems will feel more natural
Mandated support for legacy browsers or locked-down corporate WebViews — Bootstrap or heavily polyfilled stacks may be required
Teams that need maximum Stack Overflow density and hiring familiarity above agent contracts
Dependency-free means you own the bugs
There is no npm dependency tree to absorb fixes — every picker, upload flow, and patch path is maintained in this website. Release gates and contrast requirements are documented in QUALITY.md; accessibility scope in ACCESSIBILITY.md.
Comparison at a glance
smartwebUI
Bootstrap
React + UI kit
Public API
HTML + design tokens
CSS classes + JS plugins
Components + JSX
Agent contract
Catalog + surfaces
None
Ad hoc per app
Build step
None
None
Yes (bundler)
Browser window
Last 36 months
Broad legacy support
Depends on bundler targets
Best for
Agent UI, PHP-first, progressive enhancement
General marketing sites
Rich client applications
FAQ
Is this a framework?
No compile step. Optional JavaScript hydrates hand-written HTML. Agent JSON is an optional layer on top — the entire showcase works without it.
Can agents write HTML?
Yes. Agents can output semantic smart-* markup the same way humans do — that is the library’s public API. They can also compose through validated surface JSON, which the agent runtime renders to the same HTML via the DOM API. What surfaces reject is raw HTML strings inside JSON declarations; that boundary keeps generative UI fail-closed. Trusted application code may still use update(..., { html: true }) for its own markup.
Does it work without agents?
Yes. Link smartwebui.css and optionally smartwebui.js. Every component in the showcase is plain markup.
Why only modern browsers?
The library uses the Popover API, native <dialog>, cascade layers, color-mix(), light-dark(), and :has() without polyfills. See browser policy.
Can I use it inside React or Vue?
Possible — mount HTML islands and call hydrate() after render. That is not the primary integration story; expect to manage lifecycle yourself.
What changed in 1.0.2 for validation APIs?
SmartWebUI.validate(form, rules) is the form helper (core). Surface declarations use SmartWebUI.validateSurface(surface) (agent). The 1.0.1 validate(surface) alias was removed — see changelog.
Where is the security model documented?
SECURITY.md — agent threat model, CSP guidance, and e-commerce boundaries. Summarized under agent security.
Canonical inventory
HTML is the public API. The agent catalog is a subset of that HTML: every catalog type has a documented snippet, and every showcase family agents may emit maps to one catalog type. There is no third API.
Two lists both happen to total 38. They are not the same 38 items.
HTML families (38). The component reference numbers 38 families. The showcase shows every family except Payment: foundations 01–15, application patterns 16–36, and Carousel 38 (Payment is family 37, shown in the reference and agent demo). Combobox, File, Date & time, Search, Tags, and Confirm keep their own snippets even though they map onto Field or Dialog. Split actions stay in Button; advanced Field controls stay in Fields; circular progress stays in Progress; groups stay in Avatar; structured feeds and attachments stay in List; consent stays in Alert; Chart is one family with five marks; the lightbox viewer stays in Carousel.
Catalog types (34 + 4). The agent catalog has 34 component types plus 4 layout primitives (Stack, Cluster, Grid, Text). That set includes Icon, which is a documented foundation rather than a numbered HTML family, plus FormErrors, AppHeader, Footer, CommandPalette, Payment, Chart, and Carousel. It does not give Combobox or Confirm their own type names.
#
HTML family
Catalog type
—
Icons (foundation, not a numbered family)
Icon
01
Buttons (includes split actions)
Button
02
Fields
Fieldcontrol: input, textarea, select, range, otp, quantity, multiselect, numericrange, rating, color (mask is a property, not a control)
03–05
Choices, Switches, Segmented
Choice, Switch, SegmentedControl
06–15
Cards through Progress
Matching catalog names
16–20
Table, Pagination, Alerts, Form errors, Breadcrumbs
smartwebui-icons.svg is a curated, dependency-free SVG sprite built from Lucide plus 17 popular social-network brands. Its 516 stable names are indexed in smartwebui-icons.json. No npm, icon font, custom element, or build step is required for consumers.
--smart-icon-size accepts any valid CSS size: 17px, 2.4em, 10vw, or clamp(1rem, 4vw, 5rem). Set --smart-icon-stroke to tune line weight. The optional helper creates safe DOM nodes:
Omit label for decorative icons next to visible text; provide it for a meaningful standalone icon. Directional icons flip automatically in right-to-left layouts. External SVG sprites should be served from the same origin for the most reliable browser behavior. A different trusted path can be configured with SmartWebUI.setIconSource().
Use sprite icons—not font glyphs—for interface actions and status marks such as close, search, previous/next, add/remove, overflow, warnings, and ratings. Keep punctuation, mathematical notation, keyboard characters, and prose symbols as text. Icon-only controls still require a textual aria-label; their SVG remains aria-hidden="true".
Customizing
Override design tokens after the stylesheet. Every component reads the same variables, so one override updates the whole interface immediately.
Theme colors are written as light-dark() pairs resolved through color-scheme. Light and dark themes follow the operating system preference automatically; <html data-theme="dark"> forces a theme. Density (data-density="compact") and the Liquid material (data-material="liquid") are opt-in the same way.
Library CSS lives in cascade layers. Unlayered host CSS wins. See the token reference for the public variable list and the layer order.
Token reference
Public custom properties on :root. Override these after loading smartwebui.css. Names that start with --_ are private to a component and are not part of the API.
Host CSS should stay unlayered so it wins over every @layer smart.* rule, including Liquid (smart.materials). If you use layers, declare them after the library list: smart.reset, smart.tokens, smart.base, smart.layout, smart.components, smart.materials, smart.utilities.
When overriding --smart-accent or --smart-danger, also set --smart-on-accent and --smart-on-danger so contrast stays AA.
Utilities
Single-purpose classes in @layer smart.utilities — the last layer, so they win over components. Spacing steps reference the --smart-space-* scale, which means compact density and token overrides apply automatically.
Spacing
.smart-p-0….smart-p-8 — padding; .smart-px-* / .smart-py-* for inline / block axes; .smart-pt-* / .smart-pb-* / .smart-ps-* / .smart-pe-* for logical start/end sides. Steps 9–12 remain token-only for page-level spacing.
.smart-m-0….smart-m-8 — margin; .smart-mx-* / .smart-my-* for axes; .smart-mt-* / .smart-mb-* / .smart-ms-* / .smart-me-* for logical sides; .smart-m-auto / .smart-mx-auto / .smart-my-auto for centering
.smart-gap-0….smart-gap-8 — gap for .smart-stack, .smart-cluster, .smart-grid, or any flex/grid host
Layout
.smart-container with .smart-container--sm / --md / --lg — width cap + auto margins (40rem / 56rem / 76rem)
.smart-grid--2 / --3 / --4 — fixed column grids that collapse under 50rem; .smart-span-2, .smart-span-full for cell spans
.smart-span-desktop-2 / .smart-span-desktop-full apply spans only on desktop; .smart-sticky and .smart-sticky-top use the library sticky z-index.
.smart-z-raised, .smart-z-sticky, .smart-z-tooltip, .smart-z-toast, and .smart-z-banner expose the public z-index token scale without hard-coded numbers.
.smart-divider and .smart-divider--vertical create token-driven separators with consistent spacing.
.smart-ratio-1x1 / -4x3 / -16x9 / -21x9 — media boxes; children are stretched to cover. Override the crop with .smart-object-contain / -cover / -fill / -scale-down on the child (logos, screenshots).
Native <figure> / <figcaption> are styled in the base layer — caption tone, size, and spacing come free, no classes.
.smart-control-sm / .smart-control-lg — size any form control by overriding --smart-control-height locally: inputs, selects, date pickers, segmented controls, tabs and buttons all consume the same token, so one class aligns a whole row. Density still applies on top.
Form spacing tokens — override --smart-form-gap (row spacing in form.smart-stack, card bodies, and dialogs) and --smart-field-gap (label-to-control spacing) without touching markup.
.smart-reveal — fade-slide in on first scroll into view; stagger with --smart-reveal-delay. Content stays visible if JS never runs.
.smart-animate plus an effect class — opt-in .smart-enter-fade, --up, --down, --start, --end, or .smart-enter-scale entrances; matching .smart-leave-* exits; and one-shot .smart-attention-pulse, --shake, or --flash cues. Tune a single element with --smart-animation-duration, --smart-animation-delay, --smart-animation-ease, and --smart-animation-iterations. Start/end mirror in RTL and all effects stop under reduced motion.
Typography
Semantic scale: h1 through h6 use the token scale automatically; h2 is the display/section size, while h5 and h6 cover supporting and eyebrow headings.
.smart-truncate (one line), .smart-line-clamp-2/3/4 (multi-line), .smart-balance, .smart-mono, and font-weight helpers.
.smart-kbd styles shortcut hints such as ⌘ K.
Visibility
Prefer the native hidden attribute — the reset guards it against display overrides. SmartWebUI.hide(el) / SmartWebUI.show(el) toggle it, and agent nodes accept a universal hidden: true property.
.smart-hidden (display: none) when a class toggle is easier; .smart-invisible hides without removing layout
.smart-hidden-mobile hides under 50rem; .smart-visible-mobile shows only under 50rem
.smart-scroll-y / .smart-scroll-x — self-scrolling regions with thin scrollbars and contained overscroll, for message lists and long feeds.
.smart-pane-fill, .smart-pane-tall (min(60vh, 32rem)), .smart-pane-screen (100svh minus --smart-pane-offset) — pane heights for messenger-style layouts where a header or composer stays fixed.
Application layout helpers
Optional classes for common product shells — page main columns, auth forms, reading-width content, card grids, and log output. They compose with .smart-stack, .smart-cluster, and .smart-grid.
Class
Purpose
smart-page-main
Default public page main spacing
smart-shell-main
App shell main column spacing
smart-cluster--center
Horizontally center a flex cluster
smart-stack--tight
Reduced vertical stack gap
smart-auth-layout
Narrow centered auth form column (max 44rem)
smart-auth-logo
Login/register logo sizing
smart-reading-card
Prose-width card container
smart-tile-card
Icon + title link tile (with smart-card--interactive)
smart-card__feature-icon
Large icon badge for tiles and pickers
smart-grid--cards
Equal-height card grid
smart-grid--tiles
Tile grid min width (combine with --cards)
smart-grid--pickers
Choice/picker card grid
smart-card--picker
Centered picker tile styling
smart-log-panel
Scrollable monospace log output
Print
.smart-no-print hides any element on paper; popovers, toasts, dialogs, and app-shell controls are hidden automatically in @media print.
.smart-print-document flattens a card into a print-ready document — shadows removed, black on white — for invoices, quotes, and receipts.
JavaScript API
The optional script exposes a frozen SmartWebUI global. Every method works on ordinary DOM elements, so it composes with any server-rendered or client-rendered page.
One global, two validation roles.SmartWebUI.validate(form, rules) validates form fields (core). SmartWebUI.validateSurface(surface) validates agent JSON (requires smartwebui-agent.js). Do not pass surface declarations to validate(). With the agent script loaded, the same SmartWebUI object extends the core API — one script tag pair for simplicity.
SmartWebUI.update(target, content, options)
Replaces the content of an element with a View Transition when the browser supports one, or an immediate accessible update otherwise. Strings are inserted as plain text by default. Pass { html: true } only for trusted application markup, and { replace: true, html: true } to replace the element itself. Pass a Node to insert structured DOM without parsing HTML.
Agent-produced content must go through render() and patch() — not HTML strings in update().
Shows a dismissible notification in the polite live region. tone is one of success, info, warning, or danger; duration: 0 keeps it until dismissed. placement accepts top-start, top-center, top-end, bottom-start, bottom-center, or bottom-end.
Apply light/dark/auto themes, comfortable/compact density, or the solid/liquid material. Choices persist to localStorage when available and dispatch a matching smart: event.
SmartWebUI.setBusy(button, busy)
Sets aria-busy on a button and disables it while a save or other in-flight action runs. Call setBusy(button, false) to restore the previous disabled state.
SmartWebUI.validate(form, rules, options)
Runs declarative rules against a form and wires the results into the Field pattern: aria-invalid on failing controls, linked .smart-error messages, .smart-field--invalid on wrappers, and an optional FormErrors summary ({ summary: "#form-errors" }). The summary is built with DOM text nodes — labels and messages are never interpolated into HTML. Rules per field name: required, email, minLength, maxLength, pattern, match, min, max, custom(value, form), plus label and message overrides. Returns and dispatches { valid, issues } via smart:validate.
SmartWebUI.setLocale(strings)
Overrides component chrome strings — placeholders, aria labels, picker buttons, upload and validation messages — for any subset of keys (SmartWebUI.t(key) reads one back). Dates and currency always format through Intl; RTL works via dir="rtl" as always. Dispatches smart:locale.
SmartWebUI.upload(input, url, options)
Uploads the file in a dropzone-enabled file input with a live progress bar, returning a promise for the server response. Options: method, fieldName, fields (extra form values), headers. Progress and completion arrive as smart:upload-progress, smart:upload-done, and smart:upload-error events on the input.
SmartWebUI.countdown(target, seconds, options)
Renders an m:ss countdown into any element, ticking once per second, and returns a controller with stop(), start(), and reset(seconds). Options: format, warnBelow (adds .smart-countdown--warn), onTick, onExpire. Declaratively, data-smart-countdown="180" (with optional data-countdown-warn) starts on hydration — the session-timeout pattern. Events: smart:countdown, smart:countdown-expire.
copy() · confirm() · formatters
copy(text) writes plain text to the clipboard and returns it. confirm(options) opens an accessible native dialog and resolves to a boolean. formatDate(), formatNumber(), formatCurrency(), and formatRelativeTime() provide dependency-free Intl-based display formatting with optional locale settings.
debounce() · throttle() · shortcuts
debounce(callback, delay) and throttle(callback, interval) handle search and scroll work. SmartWebUI.shortcuts.register("mod+k", handler, options) registers a normalized keyboard shortcut and returns an unregister function; mod maps to Command on macOS and Control elsewhere.
observeBreakpoint(query, callback) exposes a cleanup-friendly matchMedia observer. observeLoadMore(sentinel, callback, options) wraps IntersectionObserver for infinite lists. observeConnectivity(callback) reports navigator.onLine immediately and on browser online/offline events. Each returns a cleanup function.
focusFirst() · trapFocus()
focusFirst(target, options) moves focus to the first usable descendant. trapFocus(target, options) exposes the library's focus-loop behavior for custom host surfaces and returns a cleanup function that can restore prior focus. Native dialogs remain preferred when modal semantics apply.
trackDirty() · queryState
trackDirty(form, options) compares live form data with a resettable baseline, toggles data-smart-dirty, dispatches smart:dirty, and optionally guards beforeunload. queryState.read/write/apply/bind synchronizes named filter and table controls with URLSearchParams through replace-state by default.
virtualize() · scrollToTop()
virtualize(target, items, renderItem, options) windows fixed-height rows with configurable itemHeight and overscan, returning update, refresh, scroll, and destroy controls. scrollToTop() honors reduced motion; data-smart-scroll-top turns a .smart-scroll-top button into an automatically revealed affordance.
SmartWebUI.hydrate(root)
Initializes popovers, custom selects, comboboxes, multi-selects, date and time pickers, dual ranges, ratings, masks, color outputs, command palettes, app headers, attachment removal, table selection and expandable rows, app shells, steppers, search, file/dropzone, quantity, password strength, OTP, countdowns, tabs, segmented controls, ranges, tooltips, and charts inside any element. Call it after inserting smartwebUI markup dynamically so new components behave like static ones.
TypeScript types for the core and agent globals live in smartwebui.d.ts.
Markup behaviors
Common interactions need no JavaScript of your own — add a data attribute.
data-smart-open="#dialog-id" opens that <dialog> modally and returns focus on close.
data-smart-close closes the closest dialog from any button inside it.
data-smart-collapse="#region-id" toggles the hidden attribute on any region from a remote trigger, with aria-expanded / aria-controls managed for you.
data-smart-scrollspy on a table-of-contents nav highlights the link whose section is in view via aria-current="location" — this page's sidebar uses it.
data-smart-toast with optional data-toast-title, data-toast-message, and data-toast-placement fires a toast on click.
data-smart-breadcrumbs-collapse folds deep breadcrumb middles; data-smart-scroll-top provides an automatically revealed return-to-top button; data-smart-connectivity reflects browser online/offline state.
data-smart-theme, data-smart-density, and data-smart-material apply preferences on click.
Events
Components announce changes through bubbling custom events, so integration stays decoupled from markup.
smart:tab from tab activation, smart:select from the custom select, smart:combobox from filterable fields.
smart:date and smart:time from custom date and time pickers; range pickers include start and end.
smart:tags from tag fields, smart:multiselect from multi-select dropdowns, smart:range from dual ranges, and smart:rating from ratings.
smart:attachment-remove from removable file rows; smart:copy after a successful copy action.
smart:sort from table sort buttons, smart:table-select and smart:table-action from bulk selection, smart:step from steppers, smart:chip-remove from removable chips.
smart:confirm after a confirm dialog is accepted, smart:toast-action from toast action buttons, smart:accordion from disclosure toggles, and smart:breadcrumbs when a collapsed path expands.
smart:connectivity, smart:dirty, smart:query-state, and smart:virtualize expose host-level state changes.
smart:update after update() replaces a region.
smart:quantity from stepper buttons, smart:strength while typing scored passwords, smart:otp from OTP digit rows ({ value, complete, length }), smart:validate after validate().
smart:file when a pick or drop is accepted or rejected; smart:upload-progress, smart:upload-done, smart:upload-error around upload().
smart:board and smart:move from Kanban card drops ({ cardId, fromColumnId, toColumnId, fromIndex, toIndex }); smart:send from conversation composers ({ value, textarea }); RichText fields also emit smart:change with { value, control } (agent-bound controls use the surface smart:change shape instead).
smart:locale after setLocale() overrides chrome strings.
smart:theme, smart:density, smart:material when preferences change.
smart:action and smart:change from agent-rendered controls (below).
smart:surface and smart:patch after agent rendering and patching; smart:adopt after server-rendered markup is registered; smart:agent-ready when the agent runtime loads.
smart:compat with the missing feature list when data-smart-compat detects an unsupported browser; smart:stripe-ready and smart:payment-ready from the payment companion.
Agent surfaces
The optional agent runtime (smartwebui-agent.js, loaded after smartwebui.js) lets generative systems compose interfaces from a declarative JSON contract. Agents can also write hand-authored smart-* HTML directly — surfaces are the fail-closed path when JSON is the wire format.
The renderer builds elements through the DOM API and never accepts HTML strings, JavaScript, classes, or inline styles inside a surface declaration. Each node is validated against its catalog property map — unknown keys fail closed. href and src accept only http(s), mailto:, relative paths, and in-page hashes. Surface data is otherwise structurally validated: plain objects only, unique IDs, and depth, node-count, and text-length limits. Buttons emit smart:action events; the runtime never executes agent-provided code.
Stable IDs support live updates with four patch operations — data.patch, node.upsert, node.remove, and node.append — and focus is preserved across patches when possible. node.append (or SmartWebUI.append(target, { parent, node | nodes })) grows a container as items arrive, the streaming shape for SSE and WebSocket token flows.
SmartWebUI.validateSurface(surface) checks a declaration before rendering, returning { valid, errors } plus structured issues: [{ path, message }] that agents can self-correct from.
SmartWebUI.snapshot(target) reads a live surface back: { capturedAt, nodes: { [id]: { type, value?, checked?, selected?, expanded?, step?, open?, hidden? } } } — field values, date ranges, switch states, tab selection, accordion panels, stepper progress, table selections, dialog state, all keyed by stable id.
SmartWebUI.loadCatalog() fetches smartwebui.catalog.json, the machine-readable contract of components, properties, composition rules, and accessibility expectations.
SmartWebUI.history(target) records every patch applied to a surface with its precise inverse — undo(), redo(), clear() — so agents and users can roll back interface changes safely.
Server-side rendering:smartwebui-surface.php (a dependency-free PHP class) emits the same semantic HTML with stable IDs and an embedded declaration via SmartWebUISurface::container($surface). In the browser, SmartWebUI.adopt("#target") registers that markup without a rebuild — dispatching smart:adopt — after which patch(), snapshot(), and history() behave exactly as on client-rendered surfaces.
/llms.txt summarizes the whole library — conventions, utilities, catalog, events — in the format coding agents look for.
Dialogs and drawers accept open: true to open modally once the surface mounts; toasts are announced through the standard toast region.
Server-first workflow
The recommended path for PHP and other server stacks:
Agent or application produces a validated surface JSON object.
Server renders first paint with SmartWebUISurface::container($surface, ["id" => "report"]) — same HTML, stable data-smart-id attributes, embedded declaration.
Browser loads smartwebui.js + smartwebui-agent.js, then SmartWebUI.adopt("#report") registers the DOM without a client rebuild.
Live updates use patch(), append(), snapshot(), and history() on the adopted surface.
See the agent demo for a live server-rendered surface with adopt, streaming append, and undo/redo. A conformance test suite for ports in other languages is planned.
Streaming & concurrency
node.append / SmartWebUI.append() is for growing lists and feeds as tokens arrive — not replacing whole trees mid-edit.
Concurrent patches to the same node are last-write-wins unless the host serializes an operation queue.
Focus is preserved across data.patch when possible; streaming append does not move focus — announce critical updates with your own live region if needed.
Use history() to roll back mistaken agent patches during development and operator workflows.
Agent security
The agent runtime validates every surface before render and refuses HTML strings in JSON, scripts, classes, and unsafe URLs. Actions are events — your application executes them; the library never runs agent code. Agent-generated markup outside the surface protocol is the same HTML API humans use — the host decides whether to trust or sanitize it.
Threat
Mitigation
XSS via agent HTML
Surfaces use typed nodes only — no HTML strings in JSON
Open redirect
href / src allowlist
Payment spoofing
Payment amounts are display-only; charge server-side
Malicious patch
Validate before render; host may reject operations
Misleading copy
Agent text is not verified — treat like user-generated content
Full threat model, CSP guidance, and reporting: SECURITY.md in the repository root.
E-commerce scope
In scope: checkout UI slot (Payment node), token-aware Stripe Elements via smartwebui-stripe.js, Address and Express Checkout element types, print styles for invoices and receipts.
Out of scope: cart logic, tax calculation, inventory, webhooks, PCI scope — the host application owns commerce rules and secrets.
“E-commerce ready” means checkout UI hooks and agent-safe payment intent — not a Shopify alternative.
Component reference
Thirty-eight HTML families for product screens. HTML is the public API. The agent catalog is a different list of 34 component types plus 4 layout primitives (also 38 entries, including Icon, which is not a numbered family). Variations stay with their parent family instead of inflating the inventory. See the inventory, the foundation showcase, and application patterns.
Initiate an immediate user action with clear hierarchy and tactile feedback.
Classes
.smart-button, modifiers --primary, --quiet, --link, --danger, --small, --large, --icon, --full; group with .smart-button-group; attach a menu with .smart-split-button
Data attributes
data-smart-toast, data-toast-title, data-toast-message on triggers; use aria-pressed for toggle groups; aria-busy="true" for a saving spinner
Events
Native click; optional toast via data-smart-toast; agent buttons emit smart:action
Agent catalog
Button — label, variant (including link), size, fullWidth, pressed, action, disabled, busy; add splitItems[] and menuLabel for the attached-menu variation
Accessibility
Visible label or aria-label on icon buttons; one primary action per decision area
data-smart-select, data-value on root and options; data-smart-anchor on popover; data-smart-password + data-smart-reveal; data-smart-copy; data-smart-strength creates a password meter; data-smart-counter pairs a textarea with its native maxlength
Field uses one control family: "input", "textarea", "select", "combobox", "search", "file", "range", "date", "time", "tags", "daterange", "otp", "quantity", "multiselect", "numericrange", "rating", or "color". Optional mask is a property on text inputs, not a control type. Inputs accept text prefix/suffix or catalog-backed prefixIcon/suffixIcon; textarea counters use maxLength + counter.
Accessibility
Programmatic label; custom select exposes combobox + listbox semantics; link errors with aria-describedby
Client-side states come from native constraint validation: :user-invalid / :user-valid style touched controls (danger border, success border), required fields get an automatic * on the label, and .smart-error--live reveals its message only while the field is invalid. Server-side errors stay class-driven: .smart-field--invalid plus a visible .smart-error — what the agent emits for Field.error.
Switch between peer content views without leaving the current context.
Classes
.smart-tabs, .smart-tab-list, .smart-tab, .smart-tab-panel; vertical: .smart-tabs--vertical + aria-orientation="vertical" — side list for settings layouts, Up/Down keys move between tabs, collapses to horizontal under 50rem
Events
smart:tab{ tab }; arrow, Home, and End keyboard navigation
Agent catalog
Tabs — label, selected, items[] with value, label, content
Accessibility
Full ARIA tablist / tab / tabpanel wiring with aria-controls and aria-selected
data-smart-table on the wrap; data-smart-sort="columnKey" on sort buttons; data-smart-selected on <tr>; data-smart-table-all on select-all; data-smart-table-action on bulk actions
Accessible file picker with live filename feedback.
Classes
.smart-file, .smart-file__control, .smart-file__label, .smart-file__name; post-upload display uses the List-family .smart-attachment-list and .smart-attachment
Data attributes
data-placeholder on .smart-file__name; data-smart-dropzone on .smart-file enables drag-and-drop; data-max-size (bytes) and accept validate picks with inline .smart-file__error feedback
Events
Native change on file input; smart:file on accept/reject; SmartWebUI.upload(input, url, { method, fieldName, fields, headers }) uploads with a progress bar and smart:upload-progress / -done / -error events
Agent catalog
Field with control: "file", accept, buttonLabel, dropzone: true
Use the toolbar modifier for filters, search, view controls, and a page-level action. The toolbar remains in one compact row from tablet widths upward and wraps naturally on smaller screens.
data-smart-shell; data-collapsed for the icon rail; data-nav-open for the mobile drawer; data-smart-shell-menu, data-smart-shell-collapse, data-smart-shell-close
Agent catalog
AppShell — brand, recursive items[] groups with open; leaf items accept icon; collapsed activates the icon rail; accepts child content nodes
Accessibility
Sidebar uses nav; current page link has aria-current="page"; menu and collapse expose aria-expanded
data-smart-shell hydrates an AppShell; AppHeader reads aria-current="page" on the active link. Mobile collapse is automatic below the configured breakpoint.
Events
smart:change on navigation interaction; AppShell retains its own smart:shell lifecycle. No dedicated header/footer event — navigation is link-based.
Agent catalog
AppHeader — required brand + items[]; optional brandHref, navigationLabel, actions[], sticky. Footer — optional text, links[], label. See smartwebui.catalog.json for the validated property map.
Accessibility
The header is a <header> landmark with a labelled <nav>; the footer is a <footer> landmark. Active links carry aria-current="page". Mobile collapse preserves the navigation label and focus order.
data-smart-command on the <dialog>; data-smart-command-shortcut (for example mod+k); data-smart-command-search on the filter input; data-smart-command-item with role="option" on each action; optional data-smart-command-group for labeled sections.
Events
smart:action{ action, id } when an item is chosen; the dialog uses native close. Arrow keys move between results, Enter activates, Escape closes.
Agent catalog
CommandPalette — required label + items[] (label, action, optional group, shortcut); optional placeholder, open, shortcut. See smartwebui.catalog.json for the validated property map.
Accessibility
The palette is a native <dialog> with role="dialog". The listbox uses role="listbox" with role="option" items. Active selection follows aria-activedescendant. The search input is auto-focused on open; focus returns to the trigger on close.
data-smart-chart, data-mark, optional data-values + data-labels, or a JSON <script type="application/json"> payload. Series tones are accent, success, warning, danger, secondary only.
Events
smart:chart-select{ index, x, values, summary } from click or arrow keys; hover and keyboard focus show a value tooltip in the same language as .smart-tooltip
Agent catalog
Chart — required label; mark; optional series[] with tone + points[{x,y}] or a single-series values + labels shorthand. Sparkline composes as a Card child. At most 8 series and 48 finite points. No SVG, hex, or path data from JSON.
Accessibility
Figure + caption, decorative SVG, visually hidden data table, keyboard selection on cartesian marks. Progress ring stays the one-number circular indicator.
data-smart-payment on the container; the mount slot id is derived from the node id
Events
smart:payment-ready when the host-mounted element finishes loading; aria-busy on the slot while it loads
Agent catalog
Payment — label, amount + currency (display only), description. Never keys, secrets, or card data.
Stripe
Load the optional smartwebui-stripe.js companion and call SmartWebUIStripe.mount(slot, { publishableKey, clientSecret }). Stripe.js lazy-loads from js.stripe.com, and the Elements appearance is derived from your --smart-* tokens — re-synced automatically on theme, density, and material changes.
Markup
<!-- What an agent surface renders for { type: "Payment", id: "checkout", label: "Pro plan", amount: 24, currency: "USD" } -->
<div class="smart-payment" data-smart-payment>
<div class="smart-payment__summary">
<span class="smart-payment__label">Pro plan</span>
<span class="smart-payment__amount">$24.00</span>
</div>
<!-- The mount slot id is derived from the node id (here "checkout-element") -->
<div class="smart-payment__element" id="checkout-element"></div>
</div>
<script type="module">
// Host code — keys and clientSecret never appear in agent JSON.
// Select the renderer-derived slot id; do not hardcode a different selector.
await SmartWebUIStripe.mount("#checkout-element", {
publishableKey: "pk_live_…",
clientSecret: paymentIntentClientSecretFromYourServer,
});
// Address and Express Checkout (Apple Pay / Google Pay) mount the same way:
await SmartWebUIStripe.mount("#address-element", {
publishableKey, clientSecret,
elementType: "address",
addressMode: "shipping",
});
await SmartWebUIStripe.mount("#express-element", {
publishableKey, clientSecret,
elementType: "expressCheckout",
});
</script>
38 Carousel & lightbox
Momentum media browsing: a snap-scrolling slide strip, a gallery grid, and a fullscreen zoomable viewer — Apple-style page dots included.
data-smart-carousel on the section with optional data-loop, data-autoplay, data-interval; data-smart-carousel-track, -prev, -next, -dot, -toggle, -live; lightbox data-smart-lightbox, -open, -stage, -image, -counter, -caption, -zoom, -prev, -next; slides and triggers carry data-index
Events
smart:slide{ index } when the active slide changes — swipe, dots, arrows, keyboard, autoplay, or viewer navigation. The viewer itself is a native <dialog> (native close)
Agent catalog
Carousel — required label + items[] (src, alt, caption, optional nested content nodes); variant (carousel, gallery), ratio (1x1, 4x3, 16x9, 21x9), selected, autoplay, interval, loop, lightbox. At most 24 items; image URLs follow the surface src policy.
Accessibility
aria-roledescription="carousel" with slides announcing “N of M”; Arrow, Home and End keys on the track; iOS-style dots are labelled buttons with aria-current; slide changes are announced politely; the viewer is a native modal dialog with visible close, labelled zoom toggle, and zoom reset on navigation. Autoplay pauses on hover, focus, and offscreen — and never starts under prefers-reduced-motion.
Four composable layout nodes for agent surfaces. They also map to utility classes in hand-written HTML.
Stack
Vertical arrangement with consistent spacing. CSS: .smart-stack. Property: gap (small, medium, large). Forms use --smart-form-gap automatically when marked form.smart-stack.
Responsive peer grid. CSS: .smart-grid. Property: minimum (compact, standard, wide). Grids containing .smart-field widen column gap and default to an 18rem minimum.
Text
Trusted text with semantic emphasis. Property: text, as (p, strong, small, h2, h3), tone.
See also application layout helpers in Utilities — page shells, auth columns, card grids, and log panels built from the same stack/cluster/grid primitives.
Global behaviors
Shared data attributes and library events used across multiple components.
Call SmartWebUI.hydrate(root) after inserting markup dynamically. Machine-readable agent contracts live in smartwebui.catalog.json.
Materials
Solid surfaces are the default. Liquid is an opt-in global material using the same markup: set <html data-material="liquid"> or call SmartWebUI.setMaterial("liquid"). Tune it with the --smart-liquid-* tokens; lower --smart-liquid-opacity values create greater transparency.
Liquid controls track a precise pointer through CSS variables, with no component initialization. Connected controls share one optical container, while content surfaces use restrained edge light, tint, and depth rather than opaque Solid fills. The effect disables itself for reduced motion, touch-only input, and accessibility preferences, and smartwebUI increases opacity when the browser requests reduced transparency or increased contrast.
Browser policy
smartwebUI targets browser releases from the preceding 36 months — covering color-mix(), cascade layers, the Popover API, @starting-style, and light-dark(). No legacy bundles or polyfills are shipped. Where a newer animation feature is missing, components remain fully functional with simpler motion.
Who this is for
Teams that choose maintainability and modern platform APIs over supporting browsers outside the window. Internal tools, public SaaS on evergreen browsers, and agent-built dashboards fit well.
Who should not adopt the full stack
Environments with frozen corporate browsers or old embedded WebViews
Kiosks or devices that cannot receive security updates
Products with contractual legacy-browser SLAs — use tokens and hand-authored HTML without Popover-dependent components if you need a partial fit
What unsupported clients see
With data-smart-compat, a dismissible banner asks users to update. Core content remains readable where CSS parses; interactive components that depend on Popover or dialog may degrade. The default banner uses inline styles so it appears even when the main stylesheet cannot load.
Bidirectional layouts are supported throughout: spacing, borders, and radii use logical properties, so setting dir="rtl" mirrors the interface. A handful of physical-direction exceptions (switch knob, combobox chevron, calendar nav glyphs) are corrected with html[dir="rtl"] overrides.
Compatibility banner
Add data-smart-compat to your <html> element and smartwebUI feature-detects the baseline at boot — Popover API, <dialog>, color-mix(), light-dark(), :has(), logical properties. On a browser that misses any of them, a dismissible warning banner appears telling the end user the app deliberately uses the latest standards and asking them to update. Provide your own markup with <div class="smart-compat-banner" data-smart-compat-banner hidden> (optionally a data-smart-compat-dismiss button), or let the library inject a default — inline-styled so it survives even browsers too old for the layered stylesheet. The result also lands on <html data-smart-compat="supported|unsupported"> and a smart:compat event with the missing feature list.
Quality & accessibility
smartwebUI targets WCAG 2.2 AA at the component level. Keyboard operation, visible focus, reduced-motion support, and AA contrast in the default themes are engineering requirements, verified per release and documented in QUALITY.md and ACCESSIBILITY.md.
Complete pages still require an application-level audit: heading order, landmarks, error identification, zoom and reflow, custom themes, dir="rtl", and dynamic content. Date grids follow the locale week start. See the changelog for release history.
QUALITY.md documents contrast targets and release acceptance gates. ACCESSIBILITY.md separates component responsibilities from host-app obligations.
Agent-specific security boundaries: agent security and SECURITY.md.
The marketing site loads optional first-party analytics from analytics.elonovo.com only when the browser does not send Do Not Track or Global Privacy Control. The agent demo page does not load analytics.