Skip to content
sebastian-softwarePublic

About

More capable HTML. Ordinary forms. Framework-independent form controls and core UI primitives.

Resources

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

html+

More capable HTML. Ordinary forms.

html+ provides framework-independent form controls and core UI primitives. Components handle interaction, value formats, and form participation. Your application supplies the data, business rules, and workflows. ADR-0007 records that boundary: an IBAN field checks format and check digits, while the application verifies the account and starts the payment.

Status: prototype. Element names and APIs may still change, and the h6- prefix is provisional. There is no npm package: npm i htmlplus installs an unrelated third-party package. The name htmlplus.dev is reserved and not live yet.

Try it: project site · form playground · styling gallery

Install today

No package is published yet, so build a tarball from a clone. Requires Node.js 22 or later and npm.

git clone https://github.com/sebastian-software/htmlplus.git
cd htmlplus
npm ci
npm pack

Then install htmlplus-0.1.0.tgz in your project. A Git dependency does not work, because the built files are ignored in Git. The name htmlplus in these examples is a placeholder until the package name is decided.

To explore the source instead, run npm run dev. The project site is at http://127.0.0.1:4173/, the playground at http://127.0.0.1:4173/demo/, and the gallery at http://127.0.0.1:4173/demo/styling.html. Refresh the page after source changes.

Use a control

Import an element once and use it in ordinary markup. Importing registers it, so there is nothing else to call.

import "htmlplus/iban";
import "htmlplus/tags";
import "htmlplus/switch";
<form>
  <label for="account">IBAN</label>
  <h6-iban id="account" name="iban" required></h6-iban>
  <label for="topics">Topics</label>
  <h6-tags id="topics" name="topics" value='["html", "forms"]'></h6-tags>
  <label for="updates">Product updates</label>
  <h6-switch id="updates" name="updates" value="email"></h6-switch>
  <button>Submit</button>
</form>

new FormData(form) and native submission receive the values, and labels, validation, reset, and disabled fieldsets work as with native fields. Forms and validation covers showing errors and using the elements with React.

Import What it does
htmlplus/<name>, such as htmlplus/date-picker Registers that element and exports its class
htmlplus Registers all twenty-four elements and exports their classes
htmlplus/react JSX types for React 19
htmlplus/native.css, themes.css, shadcn.css The optional stylesheets

Each entry is safe to import during server rendering, where it does nothing. Without a bundler, use the prebuilt modules in browser/, such as <script type="module" src="/htmlplus/browser/elements/iban.js"></script>. Keep that folder together with its chunks/, and keep THIRD_PARTY_NOTICES.txt with the distributed modules.

Each element has a built-in neutral base, so no stylesheet is required. Customize it with inherited --hp-* tokens, CSS parts, and ordinary CSS on wrapped native fields. Optional themes.css adds Light/Dark palettes, shadcn.css connects an existing shadcn theme, and native.css styles h6-field and <button class="hp-button"> without JavaScript. See the styling guide.

Elements

There are twenty-one form controls using ElementInternals, plus Accordion, Tabs, and Native Field, which submit nothing themselves. Fifteen elements use Zag's vanilla adapter internally; there is no runtime component framework. The element reference lists each element's attributes, properties, methods, and parts.

Element Behavior Reference
h6-field Shared base styling around a real light-DOM input, textarea, or select API
h6-switch On/off setting with a checkbox-like form value API
h6-checkbox Independent checked and mixed states; checkbox-like submission API
h6-toggle Pressed button with a checkbox-like form contribution API
h6-iban Grouped display, whitespace-free submission, ten national formats, and check digits API
h6-bic Uppercase submission and 8- or 11-character structure checks API
h6-otp Character slots over one native editor, autofill hints, and preserved leading zeros API
h6-password-input Native password editing and a keyboard-accessible visibility button API
h6-number-input Localized editing, canonical numeric submission, constraints, and stepping API
h6-duration Notation such as 1h 30m or 01:30, submitted as whole seconds API
h6-interval Two numeric editors with named boundaries and optional open ends API
h6-slider One bounded number by pointer or keyboard API
h6-range-slider Two stable thumbs with explicitly named form entries API
h6-rating Clearable whole score, hover preview, and an unanswered state API
h6-segmented Compact single selection using native radio inputs API
h6-radio-group One identifier with descriptions and disabled options API
h6-toggle-group Single or multiple selection with repeated form entries API
h6-select Single selection from supplied options in a top-layer listbox API
h6-combobox Searchable selection; query text never becomes a form entry API
h6-tags Add and remove tags that submit as repeated entries API
h6-date-picker Localized editor and calendar with strict date-only submission API
h6-date-range-picker Two editors, range selection, and two named ISO entries API
h6-accordion Disclosure sections; contained fields keep their own form values API
h6-tabs Linked panels; contained fields keep their own form values API

Actions stay real buttons: the native Button family is <button class="hp-button"> styled by native.css.

Browser support

html+ targets current stable Chromium, Firefox, and Safari. CI runs all three engines through Playwright. Exact minimum versions are unverified.

The elements rely on these platform features:

  • Custom elements, shadow DOM, and ElementInternals, on every form control.
  • ElementInternals.states and :state(), for live state on the controls that expose it.
  • The Popover API, on Select, Combobox, Date Picker, and Date Range Picker. There is no older-browser fallback.
  • CSS light-dark() and color-mix(), in surface shadows and derived colors.
  • document.execCommand("insertText"), as a fallback role only. It keeps native undo while IBAN and OTP format text. If the command is unavailable or fails, ordinary native editing, canonical values, and validation keep working. See formatted fields.

Physical devices, OTP autofill, password managers, manual assistive-technology combinations, and real browser history restoration are unverified. Automated axe checks and a Playwright WebKit pass don't establish them.

Size

Element sizes lists what each element costs on its own, from about 2 KB gzipped for h6-switch to about 50 KB for h6-date-range-picker. All twenty-four elements together are about 120 KB, and importing only the ones a page uses keeps the rest out of its bundle.

Current value contract

Text fields are IBAN, BIC, OTP, Duration, Number Input, and Password Input. Their shared rules are below, along with the contracts for Switch, BIC, and Tags, which have no separate guide. Other elements are described in the guides linked from the element table.

  • Text fields expose canonical .value, raw .displayValue, and .defaultValue. An IBAN displays DE89 3704 0044 0532 0130 00 while the form receives DE89370400440532013000. Enter submits the form like a native field. Enter that chooses a Combobox option or commits a tag does not submit.
  • Assignment is programmatic and emits no user events. null or undefined empties a string .value; other values use String(). Properties assigned before an element upgrades are applied when it connects.
  • Events: host input and change bubble and are composed.
  • Reset restores defaults and clears data-touched. Explicit custom errors remain until cleared with setCustomValidity(""). See forms and validation.
  • Switch exposes .checked, .defaultChecked, and .value (default on). An unchecked switch contributes no entry. It supports disabled, not readonly.
  • BIC normalizes case and whitespace and keeps the entered 8- or 11-character form, without adding or removing a branch identifier.
  • Tags expose .value and .defaultValue as string arrays; declarative defaults use a JSON array in the value attribute. Entries are trimmed and deduplicated with case-sensitive equality, in order. Enter, comma, or blur commits pending text. Backspace in an empty editor focuses the last remove button. Editing an existing tag in place is not implemented.
  • Format checks are local. They don't establish account ownership, BIC registration, or authentication-code validity.
  • IBAN coverage is AT, BE, CH, DE, ES, FR, GB, IE, IT, and NL. A well-formed IBAN from any other country fails validation with patternMismatch and blocks submission. The profiles are hand-authored format rules checked against Swift Registry release 103, not a bundled copy of the registry.

Additional value contracts

<h6-duration name="duration" value="1h 30m" min="60"></h6-duration>
<h6-interval name="budget" lower-name="budget_min" upper-name="budget_max"
  value="[200,800]" min="0" max="10000" step="50" required></h6-interval>
<h6-segmented name="delivery" value="standard" required>
  <option value="economy">Economy</option>
  <option value="standard">Standard</option>
  <option value="express">Express</option>
</h6-segmented>
  • Duration: .value is seconds as a string, and .displayValue is the editor text. Bare integers are seconds. HH:MM[:SS] means hours, minutes, and optional seconds; units accept whole h, m, and s in that order. Fractions, negatives, unsafe integers, and calendar units are rejected. min and max are in seconds; empty attributes are ignored. An optional empty field submits an empty string, like a native text input.
  • Interval: .value and .defaultValue are [number | null, number | null]; a JSON value attribute declares defaults. A host name enables submission; keys default to name.min and name.max unless lower-name and upper-name override them. lower-label and upper-label set the visible boundary labels (default From and To). Blank boundaries are omitted, and required requires both. Both inputs use min, max, and step (native default step: 1); ordering is validated without swapping. readonly allows inspection without editing. Restoration preserves the raw numeric editor strings.
  • Segmented: .value and .defaultValue are strings. Direct child options follow native defaults for values and labels; the first duplicate value wins. Unknown or disabled selections report "", are omitted, and return with their option. required needs an enabled selection. Options can be added, removed, or updated. Native radio arrow keys and Space select; a label click focuses the selected option. Use disabled for a noninteractive choice.

All three support ordinary form association, fieldset disabling, reset, setCustomValidity, checkValidity, and reportValidity. Programmatic assignments emit no user events.

Without JavaScript and server rendering

The custom form controls depend on JavaScript. There is no no-JavaScript fallback for them and no server-rendering integration: elements render no Declarative Shadow DOM, and the modules read document on import, so they can't be imported in a server runtime. A server can still emit the element tags and attributes for the browser to upgrade.

Native Field and the Button family keep native behavior without JavaScript, and native.css supplies their base appearance. Accordion and Tabs keep their sections in light DOM, so that content is present before upgrade.

Before upgrade, an element has no shadow root or layout, and child <option> elements can show as plain text. html+ ships no pre-upgrade CSS. Use :defined to reserve space and hide raw options, adjusting the selectors to the elements you use:

:is(h6-iban, h6-select, h6-combobox):not(:defined) {
  display: block;
  min-height: var(--hp-control-height, 2.5rem);
}
:is(h6-select, h6-combobox, h6-segmented, h6-radio-group, h6-toggle-group):not(:defined) > option {
  display: none;
}

This is ordinary CSS and the project's tests don't exercise it.

Documents

Using the elements

The project

Contributing and license

See CONTRIBUTING.md for setup, checks, adding an element, and deployment, and SECURITY.md to report a vulnerability. Repository conventions are in AGENTS.md.

html+ is licensed under the Apache License 2.0. Bundled dependencies are listed in the third-party notices.

About

More capable HTML. Ordinary forms. Framework-independent form controls and core UI primitives.

Resources

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages