npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@htmlbricks/hb-form

v0.76.5

Published

JSON `schema`-driven form engine: each entry’s `type` maps to an `hb-input-*` web component or layout `row`. Handles grouping, conditional visibility, validation messages, disabled state, and dispatches rich `update` payloads (field values + `_valid`) plu

Readme

hb-form

Category: forms · Tags: forms · Package: @htmlbricks/hb-form

hb-form is a JSON schema-driven form engine for HTML Bricks: each schema row describes one logical field (or a layout row). The host maps type to the correct hb-input-* custom element, wires validation state, conditional visibility from dependencies, and exposes a submit / update event contract for backends, SPAs, or low-code flows.

You integrate by (1) loading this component plus its input dependencies, (2) passing a schema (JSON string from HTML, or an object when using a JS framework), and (3) listening for update, submit, submitinvalid, and optionally getValues.


What it does

  • Renders a Bulma-styled form (columns / field / label) inside the shadow root.
  • Parses schema when it arrives as a string (typical HTML attribute), merges live values, and avoids redundant resets when the same schema string is re-applied.
  • Passes each control a schemaentry attribute: JSON serialization of the schema entry merged with the current value (allValues[id] ?? entry.value).
  • Forwards show_validation ("yes" / "no") to every nested input so validation UI can be shown or suppressed consistently.
  • For hb-input-file and hb-input-coords, forwards i18nlang to the child.
  • Aggregates per-field validity in valids; the whole form is considered invalid until visible fields have reported validity (see Validation and _valid).
  • Supports dependencies: fields (and nested columns) can show or hide based on other fields’ values.
  • submitted="yes" triggers onSubmit() on a microtask after render (programmatic submit from the host).
  • Several text-like inputs call onclickEnter to run the same submit handler as the primary button.
  • Dispatches update on value/validity changes with a 300 ms debounce (only update is debounced; other events fire immediately).

Runtime prerequisites

hb-form dynamically registers child packages via addComponent (see component.wc.svelte). Your page or bundle must actually load the corresponding hb-input-* scripts/styles so those custom elements are defined.

Declared dependency graph (from extra/docs.ts metadata) includes at least:

  • hb-input-text, hb-input-area, hb-input-number, hb-input-email
  • hb-input-select, hb-input-radio, hb-input-checkbox
  • hb-input-date, hb-input-datetime, hb-input-color, hb-input-range
  • hb-input-file, hb-input-coords, hb-input-array-objects, hb-input-array-tags

Nested packages (e.g. hb-input-array-objectshb-form, hb-table, dialogs, paginate, etc.) apply when you use those field types—plan bundle size accordingly.


Custom element

| Tag | Notes | | --------- | ------------------------------------------ | | hb-form | Shadow root; forwards Bulma + local SCSS. |


Attributes and properties (snake_case, string-friendly)

HTML and setAttribute only carry strings. Booleans and numbers must follow your host conventions (this codebase generally uses yes / no for booleans on attributes where documented). TypeScript Component (see types/webcomponent.type.d.ts) describes the logical shape.

| Name | Required | Type / values (logical) | Role | | -------------------- | -------- | ------------------------------------------ | ---- | | schema | yes | FormSchema (array) or JSON string | Field definitions. Recommended: pass schema as a JSON string from HTML or setAttribute: the parse $effect runs JSON.parse, resets valids / allValues / visibility, and calls setVisibility. Identical consecutive strings are skipped to avoid churn. If you bind a live object without going through that string path, setVisibility may never run in the reference implementation—controls can stay invisible until you adjust the integration. | | id | no | string | Passed through to update detail as _id (see events). Default "". | | style | no | string | Present on Component typings; not consumed by current script logic (commented in source)—safe for host/CSS use if your toolchain forwards it. | | values | no | FormValues | Declared on Component for typing / tooling; not read in component.wc.svelte—values come from schema defaults, user input, and allValues. | | isInvalid | no | boolean | Declared on Component typings; not read in component.wc.svelte (internal state is the derived is_invalid variable). Treat as reserved / wrapper metadata unless your build maps it. | | submitted | no | "yes" | "no" | null | "yes" queues onSubmit() on a microtask. Inside submit, the implementation sets show_validation = "yes" and submitted = "no" before validating. | | getvals | no | "yes" | "no" | null | Typed API for getValues. The source defines getVals() but does not call it from a reactive effect; toggling this attribute alone may not emit getValues until your bundle wires it. Prefer update / submit for snapshots unless you confirm behavior in your build. | | show_validation | no | "yes" | "no" (default "no") | Forwarded to all child inputs. Forced to "yes" when submit runs. | | hide_submit | no | boolean (coerced) | If truthy (true, "yes", "true"), the entire submit area (including slots) is not rendered. | | buttons_outlined | no | "yes" | "no" (default "no") | When "yes" (or coerced "true" / boolean true from JS), the default submit <button> gets Bulma is-outlined in addition to is-primary. Does not affect fully custom submit_button slot markup. | | i18nlang | no | string (e.g. en, it) | Passed to hb-input-file and hb-input-coords. |


schemaFormSchema and each entry

FormSchema is FormSchemaEntry[]. Shared fields (FormSchemaEntryShared) and the full entry type are defined in types/webcomponent.type.d.ts.

Common fields (every entry, including inside row columns)

| Field | Type | Purpose | | ------------------- | ---------------------------- | ------- | | id | string | Stable key for DOM for=, internal maps, and event payloads. Required for non-row entries you expect to submit. | | type | string | Discriminator mapped in registeredComponents (see table below). On standalone schemaentry payloads to inputs, type may be implied by the host tag. | | label | string | Rendered above the control unless labelIsHandledByComponent applies (checkbox). | | value | unknown | Default / initial value when nothing is in allValues yet. | | dependencies | FormSchemaDependency[] | Conditional visibility (see below). | | readonly | boolean | Carried in schemaentry for inputs that support it. | | disabled | boolean | Same. | | required | boolean | Shown as * on label; enforced by child inputs + validation. | | validationRegex | string | Carried to inputs that honor regex validation. | | validationTip | string | Tip text for invalid state in children. | | placeholder | string | Passed through schemaentry. | | params | Record<string, unknown> | Type-specific options (e.g. min/max for number, options for select/radio, nested schema for arrayobjects). |

FormSchemaDependency

{ id: string; values?: (string | number | boolean)[] }
  • id: id of the controlling field.
  • values: optional whitelist. If omitted, the dependent is eligible when the controller has any non-empty value (not undefined / null / ""). If present, the controller’s current value must satisfy dep.values.includes(depVal) (strict equality with string | number | boolean).

Dependency resolution uses valueForDependency: live allValues[depId] first, otherwise the value on the schema entry for that id.


Schema type → nested custom element

Mapping is defined in registeredComponents in component.wc.svelte. Unknown type values cause setVisibility / getControls to throw at runtime—there is no silent skip.

| Schema type | Child element | Notes | | --------------- | ----------------------------- | ----- | | row | (none — layout only) | Renders Bulma columns is-multiline. Nested entries live in params.columns. registeredComponents.row uses options.row: true and component: undefined. | | text | hb-input-text | Enter key submits. | | textarea | hb-input-area | Enter key submits (handler on component). | | number | hb-input-number | Enter key submits. | | email | hb-input-email | Enter key submits. | | select | hb-input-select | | | radio | hb-input-radio | | | checkbox | hb-input-checkbox | labelIsHandledByComponent: true — outer hb-form label is not rendered (the checkbox handles labeling). Column cells get a slightly taller min-height for alignment. | | date | hb-input-date | Enter key submits. | | datetime | hb-input-datetime | Enter key submits. | | color | hb-input-color | | | file | hb-input-file | Receives i18nlang. | | range | hb-input-range | | | coords | hb-input-coords | Receives i18nlang. | | arrayobjects | hb-input-array-objects | Nested schema under params.schema. | | arraytags | hb-input-array-tags | Optional schema field array_style: "pills" or "area" (string). When omitted, hb-form passes array_style="area" on the host (textarea-like chips). Set array_style: "pills" for the classic tag row + add control. |


Layout: row and columns

  • Top-level (or nested) entry with type: "row" must provide params.columns as another array of FormSchemaEntry objects.
  • The row’s own id participates in visibility like any other entry.
  • Column cells are column is-flex is-align-items-center; checkboxes get inline min-height: 3.25rem on the column for vertical alignment.

Conditional visibility

  1. On string schema parse, setVisibility walks the tree: for each entry, visibility[id] = visibility[id] || !dependencies?.length. So fields with dependencies start hidden (false) unless already true; fields without dependencies start visible.
  2. dependencyMap groups entries by every dependency id they reference (via groupMultipleBy).
  3. When a control fires onsetVal with { id, value, valid }, setValByMessage updates allValues, valids, and for the changed id runs handleVisibility on dependents—cascading show / hide.
  4. values (derived object used for submit) includes only entries where visibility[id] is true, using allValues[id] ?? entry.value.

Hidden branch values are therefore omitted from submit / getValues payloads (matching legacy “visible only” shape).


Validation and _valid

  • Each child emits onsetVal; the form stores valids[fieldId].
  • is_invalid is true when there are no valids entries yet, or when any visible field has valids[id] === false.
  • onSubmit always sets show_validation = "yes" so users see errors after an attempt.
  • If invalid: dispatches submitinvalid with {} and does not dispatch submit.
  • If valid: dispatches submit with { _valid: true, ...values } where values is the flat map of visible field ids → current values (same shape as FormSubmitLikeDetail in typings: _valid + FormValues).

Events (types/webcomponent.type.d.ts)

All events are native CustomEvent on the host element (bubbles / composed follow your Svelte CE compile settings; detail shapes below match the implementation).

| Event | detail type (logical) | When | | ---------------- | ------------------------- | ---- | | update | { _valid: boolean; _id: string } & FormValues | Value or validity changed; debounced 300 ms and coalesced. _id is the form’s id prop (string). | | submit | FormSubmitLikeDetail = { _valid: boolean } & FormValues | Successful validation after submit (button or submitted="yes"). _valid is true when dispatched. | | submitinvalid | Record<string, never> ({}) | Submit attempted while the form is invalid. | | getValues | FormSubmitLikeDetail | Same merge as submit when getVals() runs with getvals === "yes" (see attribute notes). |

Correction vs older docs: submit and getValues details are a flat object { _valid, ...fieldValues }, not a nested { values: { ... } } object. update is also flat field keys plus _valid and _id, not a nested values property.


Slots (::part host children)

Declared in extra/docs.ts / rendered near the bottom of component.wc.svelte.

| Slot | Description | | ----------------- | ----------- | | submit_button | Replaces the entire default submit control (default content is the primary Bulma button + inner label slot). Still wrapped in the same clickable span that calls onSubmit(). | | submit_label | Default text inside the primary button (Submit). Ignored if submit_button fully replaces the button. | | other_buttons | Extra controls rendered after the submit slot, still inside button_container. |

If hide_submit is true, no submit region (and no slots) is rendered.


CSS parts

| Part | Description | | -------------------- | ----------- | | button_container | Flex wrapper around submit + other_buttons. Element id button_container. | | main_button | Default <button type="button" class="button is-primary"> when using the stock submit_button slot. |

Style the host with ::part(button_container) / ::part(main_button) from the light DOM (where supported).


CSS custom properties

From styleSetup / Bulma integration (extra/docs.ts):

| Variable | Meaning | | ---------------------- | ------- | | --bulma-column-gap | Horizontal padding / column gap on :host; tuned so Bulma .columns negative margins fit. |

Additional --bulma-* variables come from forwarded Bulma modules in styles/bulma.scss (form, grid, button, etc.). See Bulma CSS variables.


Internationalization

i18nlang is forwarded to file and coords inputs. Supported languages listed in extra/docs.ts include English and Italian; extend via package metadata if more locales are registered upstream.


Integration patterns

1. Declarative HTML (minimal)

Pass schema as a single JSON string (escape quotes for HTML).

<hb-form
  id="signup"
  schema="[{&quot;type&quot;:&quot;text&quot;,&quot;id&quot;:&quot;name&quot;,&quot;label&quot;:&quot;Name&quot;,&quot;required&quot;:true}]"
  show_validation="no"
  submitted="no"
  i18nlang="en"
></hb-form>

2. Vanilla JS — programmatic submit and listening

const form = document.querySelector("hb-form");

form.addEventListener("update", (e) => {
  const { _valid, _id, ...rest } = e.detail;
  console.log("valid?", _valid, "form id:", _id, "values:", rest);
});

form.addEventListener("submit", (e) => {
  if (e.detail._valid) {
    console.log("posted", e.detail);
  }
});

form.addEventListener("submitinvalid", () => {
  console.warn("fix validation errors");
});

// Trigger the same path as clicking submit (after schema is parsed):
form.setAttribute("submitted", "yes");

3. Building schema in JavaScript

const schema = [
  {
    id: "profile",
    type: "row",
    params: {
      columns: [
        { id: "firstName", type: "text", label: "First name", required: true },
        { id: "lastName", type: "text", label: "Last name", required: true },
      ],
    },
  },
  {
    id: "age",
    type: "number",
    label: "Age",
    required: true,
    params: { min: 0, max: 120 },
    validationTip: "Enter a realistic age.",
  },
];

form.setAttribute("schema", JSON.stringify(schema));

4. Conditional field (dependencies)

const schema = [
  { id: "code", type: "text", label: "Access code", required: true },
  {
    id: "secret",
    type: "text",
    label: "Secret phrase",
    dependencies: [{ id: "code", values: ["VIP"] }],
  },
];

The secret field stays hidden until code’s live value is exactly "VIP" (or the number/boolean you list in values).


Troubleshooting

| Symptom | Likely cause | | ------- | ------------- | | Console error unknown component type | type string not in the mapping table—fix the schema or extend the registry in source. | | Submit always invalid at first | valids empty until children emit onsetVal—ensure inputs mount and fire validation for visible fields. | | Schema changes ignored | String compare: identical schema string to the previous one is intentionally skipped; mutate or append a cache-busting suffix if you truly need a no-op reparse. | | Dependents never appear | Controller value empty or not in values whitelist; dependency controller may still be hidden. |


TypeScript

Authoring types: types/webcomponent.type.d.tsComponent (including schema as string | FormSchema), Events, FormSchemaEntry, FormSubmitLikeDetail, FormValues, IComponentName, etc.


See also

  • extra/docs.tsstyleSetup, slots, Storybook args, dependency list, and packaged examples (schema1, conditional schemas, file + array-objects samples).
  • component.wc.svelteregisteredComponents, visibility, submit pipeline, and slot markup.