Skip to content

Form — wd-form

Category: Form · Tag: wd-formBack to: Components overviewRelated: Forms module — where you build the form, its fields, validation, rules, and submit behaviour.


What it does

Embeds a form you built in the Forms module into a page, layout, or shared element. The component is a full schema-driven form engine: it renders the form's fields, runs validation and conditional rules (shouldDisplay / shouldEnable), shows a submit spinner, and either redirects or emits a submission on submit.

Build the form first. The fields, validators, submit action, and destination all come from the form's definition in the Forms module — this component just renders that form. Read the Forms guide for the field types, validation, rules, and submission handling.


Adding it & its settings

Add Component → Form → Form.

Basic Settings (all components): #id, Classes, Inline Style. Set an #id if you'll style with ::part().

Form Settings:

SettingTypeEffect
Formpicker (by reference)Choose which form (from the Forms module) to embed. Everything else — fields, validation, submit action, destination — comes from that form's definition.

Content & slots

The fields are generated from the form definition — you don't write them as child HTML. But the component has two slots, and one of them you're expected to fill:

SlotPurpose
default (unnamed)Your submit button (and any other trailing content). The component does not render a submit button — you supply it. It's placed after the last field, spanning the full width of the fields grid.
spinner (named)Optional custom content for the submit overlay shown while the form is submitting (defaults to "loading…").

The submit button (important)

The Forms module has no submit button of its own — the button is author-supplied slotted content. Because the button lives in the light DOM (it's your markup, not shadow DOM), two things follow:

  1. You wire it to submit yourself. A bare type="submit" won't submit the form — the button is slotted into the component's internal <form>, so it isn't that form's owner and no native submit fires. Instead, call the component's submit() method, e.g. from the button's click event (an Element Event → click in the builder):

    html
    <wd-form id="signup" component-settings='…'>
      <button type="submit">Sign up</button>
    </wd-form>

    submit() validates all fields — marking them touched so every error message appears — and then either redirects to the destination page or emits formSubmitted (see the Forms module). If the form is invalid it rejects and sends nothing.

    The spinner is yours to control. submit() does not show it — call toggleSpinner() around your submit. The recommended wiring, as a page/layout script, shows the spinner only once the form is actually valid (so an invalid click never flashes it):

    js
    const form = document.querySelector('#signup');
    const button = document.querySelector('#signup-submit');
    
    // wd-form emits formUpdated on every change/blur with the form's current validity.
    let isValid = false;
    form.addEventListener('formUpdated', (event) => { isValid = event.detail.isValid; });
    
    button.addEventListener('click', async () => {
      if (isValid) { await form.toggleSpinner(true); }
    
      try {
        await form.submit();          // re-validates; on failure it shows every error and rejects
      } catch (error) {
        await form.toggleSpinner(false);
      }
    });

    In a shared element (which has no script block) put the same handler body in the button's click element-event instead.

    For a redirect form, submit() navigates away and never settles — the spinner simply stays up through the navigation. For an emit form, whatever handles formSubmitted should turn the spinner off when its request finishes.

  2. You style it with normal page CSS — not ::part() or the --form-* variables, which only reach the shadow-DOM internals. Ordinary selectors (#signup button { … }) apply directly.

Controlling placement. The slot sits full-width after the last field, so the button stretches across the grid. To align or size it, wrap it in a container and lay that out (e.g. display: flex; justify-content: center):

html
<wd-form id="signup" component-settings='…'>
  <div class="form-actions">
    <button type="submit" id="signup-submit">Sign up</button>
  </div>
</wd-form>
css
#signup .form-actions { display: flex; justify-content: center; margin-block-start: 1.3rem; }

Styling

The form has a large, fully-themeable surface. Style it from a page/layout CSS block using the CSS variables below (grouped by area and by field type), and use ::part() to reach the form wrapper.

::part()

PartElement
form-<id>The form wrapper.
css
wd-form::part(form-signup) { max-width: 640px; margin-inline: auto; }

CSS variables

There are 244 variables. They share a consistent naming scheme: --form-* for the form itself, --form-field-control-* for the shared input control (used by text/email/number/etc.), --form-field-<type>-* for a specific field type, --form-legend-* for section headings, and --form-spinner-* for the submit overlay. Defaults shown in the second column.

Form spacing (8)

VariableDefault
--form-field-padding-bottom0px
--form-field-padding-inline-end0px
--form-field-padding-inline-start0px
--form-field-padding-top0px
--form-padding-bottom40px
--form-padding-left20px
--form-padding-right20px
--form-padding-top40px

Fields container (2)

VariableDefault
--form-fields-grid-x-gap16px
--form-fields-grid-y-gap24px

Field wrapper (layout) (6)

VariableDefault
--form-field-align-itemsflex-start
--form-field-displayflex
--form-field-flex-directioncolumn
--form-field-font-familysans-serif
--form-field-gap4px
--form-field-justify-contentflex-start

Field control — shared: inputs, borders, focus, valid/invalid, label, error (41)

VariableDefault
--form-field-control-bg-color#fff
--form-field-control-border-bottom-left-radius0.5rem
--form-field-control-border-bottom-right-radius0.5rem
--form-field-control-border-color1px solid #ced4da
--form-field-control-border-top-left-radius0.5rem
--form-field-control-border-top-right-radius0.5rem
--form-field-control-error-color#dc3545
--form-field-control-error-font-familysans-serif
--form-field-control-error-font-size12px
--form-field-control-error-font-weight400
--form-field-control-error-margin-top0px
--form-field-control-focus-bg-color#fff
--form-field-control-focus-border-color#86b7fe
--form-field-control-focus-label-color#5b89ce
--form-field-control-font-size14px
--form-field-control-height48px
--form-field-control-invalid-bg-colorrgba(255, 175, 200, 0.50)
--form-field-control-invalid-border-color#dc3545
--form-field-control-invalid-label-color#dc3545
--form-field-control-invalid-text-color#463333
--form-field-control-label-bg-colortransparent
--form-field-control-label-bottomauto
--form-field-control-label-color#7b8895
--form-field-control-label-font-familysans-serif
--form-field-control-label-font-size14px
--form-field-control-label-font-weight400
--form-field-control-label-leftauto
--form-field-control-label-order1
--form-field-control-label-padding0
--form-field-control-label-positionstatic
--form-field-control-label-rightauto
--form-field-control-label-topauto
--form-field-control-max-width100%
--form-field-control-order2
--form-field-control-padding0 12px
--form-field-control-text-color#495057
--form-field-control-valid-bg-color#fff
--form-field-control-valid-border-color#ced4da
--form-field-control-valid-label-color#7b8895
--form-field-control-valid-text-color#495057
--form-field-control-width100%

Checkbox field (27)

VariableDefault
--form-field-checkbox-checked-content-label-color#212529
--form-field-checkbox-checked-content-paragraph-color#6c757d
--form-field-checkbox-content-gap0.5rem
--form-field-checkbox-content-label-color#6c757d
--form-field-checkbox-content-label-font-familysans-serif
--form-field-checkbox-content-label-font-size1rem
--form-field-checkbox-content-label-font-weight500
--form-field-checkbox-content-order2
--form-field-checkbox-content-padding4px 0 0
--form-field-checkbox-content-paragraph-color#6c757d
--form-field-checkbox-content-paragraph-font-familysans-serif
--form-field-checkbox-content-paragraph-font-size0.875rem
--form-field-checkbox-content-paragraph-font-weight300
--form-field-checkbox-content-paragraph-line-height1.3
--form-field-checkbox-control-bg-color#fff
--form-field-checkbox-control-border-color#ced4da
--form-field-checkbox-control-border-radius0.25rem
--form-field-checkbox-control-checked-border-colorpurple
--form-field-checkbox-control-checkmark-bg-colorrgba(13, 109, 253, 0)
--form-field-checkbox-control-checkmark-border-radius0.125rem
--form-field-checkbox-control-checkmark-checked-bg-colorpurple
--form-field-checkbox-control-checkmark-checked-size19px
--form-field-checkbox-control-checkmark-size13px
--form-field-checkbox-control-size21px
--form-field-checkbox-gap12px
--form-field-checkbox-justify-contentflex-start
--form-field-checkbox-label-order1

Radio field (43)

VariableDefault
--form-field-radio-background#ccc
--form-field-radio-border-color#ccc
--form-field-radio-border-radius21px
--form-field-radio-checked-bg-color#007bff
--form-field-radio-checked-border-color#007bff
--form-field-radio-checked-content-label-color#212529
--form-field-radio-checked-content-paragraph-color#6c757d
--form-field-radio-checked-heading-color#212529
--form-field-radio-content-gap0.5rem
--form-field-radio-content-label-color#6c757d
--form-field-radio-content-label-font-familysans-serif
--form-field-radio-content-label-font-size1rem
--form-field-radio-content-label-font-weight500
--form-field-radio-content-order2
--form-field-radio-content-padding4px 0 0
--form-field-radio-content-paragraph-color#6c757d
--form-field-radio-content-paragraph-font-familysans-serif
--form-field-radio-content-paragraph-font-size0.875rem
--form-field-radio-content-paragraph-font-weight300
--form-field-radio-content-paragraph-line-height1.3
--form-field-radio-control-bg-color#ced4da
--form-field-radio-control-border-colortransparent
--form-field-radio-control-border-radius21px
--form-field-radio-control-checked-bg-colorpurple
--form-field-radio-control-checked-border-colortransparent
--form-field-radio-control-height21px
--form-field-radio-control-switcher-bg-color#fff
--form-field-radio-control-switcher-border-radius24px
--form-field-radio-control-switcher-height20px
--form-field-radio-control-switcher-width20px
--form-field-radio-control-width21px
--form-field-radio-description-color#6c757d
--form-field-radio-description-font-familysans-serif
--form-field-radio-description-font-size14px
--form-field-radio-description-font-weight400
--form-field-radio-gap12px
--form-field-radio-heading-color#6c757d
--form-field-radio-heading-font-familysans-serif
--form-field-radio-heading-font-size14px
--form-field-radio-heading-font-weight600
--form-field-radio-justify-contentflex-start
--form-field-radio-label-order1
--form-field-radio-size21px

Toggle field (29)

VariableDefault
--form-field-toggle-checked-content-label-color#212529
--form-field-toggle-checked-content-paragraph-color#6c757d
--form-field-toggle-content-gap0.5rem
--form-field-toggle-content-label-color#6c757d
--form-field-toggle-content-label-font-familysans-serif
--form-field-toggle-content-label-font-size1rem
--form-field-toggle-content-label-font-weight500
--form-field-toggle-content-order2
--form-field-toggle-content-padding4px 0 0
--form-field-toggle-content-paragraph-color#6c757d
--form-field-toggle-content-paragraph-font-familysans-serif
--form-field-toggle-content-paragraph-font-size0.875rem
--form-field-toggle-content-paragraph-font-weight300
--form-field-toggle-content-paragraph-line-height1.3
--form-field-toggle-control-bg-color#ced4da
--form-field-toggle-control-border-colortransparent
--form-field-toggle-control-border-radius24px
--form-field-toggle-control-checked-bg-colorpurple
--form-field-toggle-control-checked-border-colortransparent
--form-field-toggle-control-height24px
--form-field-toggle-control-switcher-bg-color#fff
--form-field-toggle-control-switcher-border-radius24px
--form-field-toggle-control-switcher-box-shadow0 1px 3px rgba(0, 0, 0, 0.6)
--form-field-toggle-control-switcher-height20px
--form-field-toggle-control-switcher-width20px
--form-field-toggle-control-width34px
--form-field-toggle-gap12px
--form-field-toggle-justify-contentflex-start
--form-field-toggle-label-order1

Typeahead field (9)

VariableDefault
--form-field-typeahead-control-dropdown-bg-color#fff
--form-field-typeahead-control-dropdown-border1px solid #ddd
--form-field-typeahead-control-dropdown-border-radius6px
--form-field-typeahead-control-dropdown-box-shadow0 6px 18px rgba(0, 0, 0, 0.15)
--form-field-typeahead-control-dropdown-item-color#212529
--form-field-typeahead-control-dropdown-item-font-size14px
--form-field-typeahead-control-dropdown-item-hover-bg-color#f2f2f2
--form-field-typeahead-control-dropdown-item-padding10px 12px
--form-field-typeahead-control-dropdown-max-height260px

File field (16)

VariableDefault
--form-field-file-control-padding8px 8px
--form-field-file-control-selector-bg-colorpurple
--form-field-file-control-selector-border-bottomnone
--form-field-file-control-selector-border-leftnone
--form-field-file-control-selector-border-radius0.375rem
--form-field-file-control-selector-border-rightnone
--form-field-file-control-selector-border-topnone
--form-field-file-control-selector-colorwhite
--form-field-file-control-selector-font-size14px
--form-field-file-control-selector-height32px
--form-field-file-control-selector-hover-bg-colorrgb(96, 0, 96)
--form-field-file-control-selector-margin-end8px
--form-field-file-control-selector-padding4px 12px
--form-field-file-control-selector-top50%
--form-field-file-control-selector-transformtranslateY(-50%)
--form-field-file-control-selector-width25%

Number field (12)

VariableDefault
--form-field-number-control-button-bg-color#e0e0e0
--form-field-number-control-button-bordernone
--form-field-number-control-button-border-bottom-left-radius0.5rem
--form-field-number-control-button-border-bottom-right-radius0.5rem
--form-field-number-control-button-border-top-left-radius0.5rem
--form-field-number-control-button-border-top-right-radius0.5rem
--form-field-number-control-button-color#333
--form-field-number-control-button-font-size1rem
--form-field-number-control-button-font-weight500
--form-field-number-control-button-height48px
--form-field-number-control-button-padding0
--form-field-number-control-button-width32px

Textarea field (2)

VariableDefault
--form-field-textarea-control-height96px
--form-field-textarea-control-padding8px 12px

Phone field (6)

VariableDefault
--form-field-phone-control-country-font-size0.875rem
--form-field-phone-control-country-width25%
--form-field-phone-control-separator-br-color#ccc
--form-field-phone-control-separator-height50%
--form-field-phone-control-separator-top50%
--form-field-phone-control-separator-width1px

Legend / section heading (26)

VariableDefault
--form-legend-align-itemscenter
--form-legend-bg-colortransparent
--form-legend-border-bottom1px solid #dee2e6
--form-legend-border-radiusnone
--form-legend-border-top1px solid #dee2e6
--form-legend-description-color#6c757d
--form-legend-description-font-familysans-serif
--form-legend-description-font-size14px
--form-legend-description-font-weight400
--form-legend-description-text-aligncenter
--form-legend-flex-directioncolumn
--form-legend-gap4px
--form-legend-justify-contentcenter
--form-legend-margin-bottom0
--form-legend-margin-left0
--form-legend-margin-right0
--form-legend-margin-top0
--form-legend-padding-bottom1rem
--form-legend-padding-left0px
--form-legend-padding-right0px
--form-legend-padding-top1.5rem
--form-legend-title-color#212529
--form-legend-title-font-familysans-serif
--form-legend-title-font-size18px
--form-legend-title-font-weight600
--form-legend-title-text-aligncenter

Submit spinner (17)

VariableDefault
--form-spinner-align-itemscenter
--form-spinner-bg-colorrgba(255, 255, 255, 0.8)
--form-spinner-displayflex
--form-spinner-filterblur(4px)
--form-spinner-height0
--form-spinner-justify-contentcenter
--form-spinner-left0
--form-spinner-padding-bottom0
--form-spinner-padding-left0
--form-spinner-padding-right0
--form-spinner-padding-top0
--form-spinner-positionabsolute
--form-spinner-top0
--form-spinner-visible-height100%
--form-spinner-visible-width100%
--form-spinner-width0
--form-spinner-z-index10

Methods

The form element gives you a small set of methods you can call from your own JavaScript — from an Element Event → click in the builder, or any page/layout script. (Inline onclick="…" attributes are stripped by the site's content-security policy, so use one of those two instead.) Grab the element by its #id first, then call a method on it:

js
const form = document.getElementById('signup');   // your wd-form's #id

All four are async (they return a Promise).

submit() — submit the form

Re-checks the conditional rules, validates every field, then runs the form's configured submit action.

  • Takes: nothing.
  • If everything is valid → it does whatever the form is set to do in the Forms module: either redirects the browser to the destination page, or fires the formSubmitted event (carrying the values) for you to handle.
  • If something is invalid → it marks the fields as touched (so their error messages show) and the Promise rejects with a helpful object you can inspect:
js
form.submit().catch(err => {
  // err.message  === "Form validation failed."
  // err.formFields === { email: { value: "", errors: ["Required"] }, ... }
});
  • It does not show the spinner — that's toggleSpinner(), below.
  • On a redirect form the Promise never settles: the browser navigates away as part of submitting, so don't put anything you depend on after await form.submit().

This is how your slotted submit button triggers the form — a bare type="submit" won't (see The submit button).

toggleSpinner(show) — show/hide the "submitting…" overlay

Handy when you handle the submission yourself (e.g. you listen for formSubmitted, do an async task, then finish) and want to show the busy state meanwhile.

  • Takes: a boolean — true shows the spinner, false hides it.
  • Returns: nothing.
js
form.toggleSpinner(true);    // busy…
await sendToMyApi();
form.toggleSpinner(false);   // done

setValue(fieldKey, value) — set a field's value

Sets a field programmatically, then re-validates that field and re-runs the conditional rules — exactly as if the visitor had typed it.

  • Takes: the field's key (its key from the Forms module) and the value.
  • Returns: nothing.
js
form.setValue('country', 'JO');

getFormValues() — read the current values

  • Takes: nothing.
  • Returns: a Promise that resolves to a plain object keyed by field key — e.g. { email: "a@b.com", country: "JO" } (section/heading fields are excluded).
js
const values = await form.getFormValues();

Notes

  • This page is about the component (rendering & styling). To build the form itself — fields, validators, conditional rules, submit action/redirect — use the Forms module.
  • The form also emits events for custom page scripts: formUpdated as values change, and (when it isn't redirecting) formSubmitted on a successful submit.