# Technical Notes

## Technical Challenges

A form input element rendered **inside the code component’s Shadow DOM** will **not** be serialized by the outer HTML `<form>` on submit. Forms only submit light-DOM controls or form-associated custom elements (FACE).&#x20;

Ways to make it work:

### Slot the real input (best, native submit)&#x20;

```html
<!-- In the component's shadow: -->
<slot name="ctrl"></slot>

<!-- In your form (light DOM): -->
<form>
  <code-island> <!-- your component host -->
    <input type="checkbox" slot="ctrl" name="agree" value="1">
  </code-island>
  <button type="submit">Send</button>
</form>
```

The checkbox stays in light DOM → included in submission.

### Mirror to a hidden input (when the component must render its own UI)

```html
<form id="f">
  <code-island id="agree-toggle"></code-island>
  <input type="hidden" name="agree" id="agree-hidden" value="0">
</form>

<script>
// listen for a composed event from the component and mirror the value
document.getElementById('agree-toggle')!.addEventListener('agree-change', (e:any) => {
  document.getElementById('agree-hidden')!.setAttribute('value', e.detail.checked ? '1' : '0');
});
</script>
```

Inside the React component (run in the shadow root), dispatch a composed event on change:

```ts
checkbox.addEventListener('input', () => {
  const host = (checkbox.getRootNode() as ShadowRoot).host as HTMLElement;
  host.dispatchEvent(new CustomEvent('agree-change', { detail:{ checked: checkbox.checked }, bubbles:true, composed:true }));
});
```

### Intercept submit and append programmatically (no hidden input)

```js
const form = document.querySelector('form')!;
form.addEventListener('formdata', (ev) => {
  const host = document.getElementById('agree-toggle')!;
  const checked = (host.shadowRoot!.querySelector('input[type=checkbox]') as HTMLInputElement).checked;
  ev.formData.set('agree', checked ? '1' : '0');
});
```

Pick 1 if you can control markup; 2 if you want native posts with minimal JS; 3 if you already handle submissions via JS.

## Form-Associated Custom Elements (FACE)&#x20;

Form-associated custom elements (FACE) are custom elements that participate in HTML forms like native inputs.

{% hint style="danger" %}
Chromium supported- not just supported in Safari or Firefox.&#x20;
{% endhint %}

Key points

* Opt-in: `static formAssociated = true;` then `this.internals = this.attachInternals()`.
* Association: the element is owned by the nearest `<form>` or by `form="<form id>"` even if placed outside. Access via `this.internals.form`.
* Submission: give the element a `name` attribute and call `this.internals.setFormValue(value)` whenever its value changes; the value is included in form `FormData`.
* Labels: `this.internals.labels` returns associated `<label>`s.
* Validation: use `this.internals.setValidity(...)`, `checkValidity()`, `reportValidity()`, `willValidate`, `validationMessage`.
* Lifecycle hooks: `formAssociated(form)`, `formDisabledCallback(disabled)`, `formResetCallback()`, `formStateRestoreCallback(state,mode)`.

Minimal example (TypeScript)

```ts
class XToggle extends HTMLElement {
  static formAssociated = true;
  private internals: ElementInternals;
  private checked = false;

  constructor() {
    super();
    this.attachShadow({ mode: 'open' }).innerHTML = `
      <button part="btn" type="button" aria-pressed="false">Off</button>
      <style>:host{display:inline-block}</style>`;
    this.internals = this.attachInternals();
  }

  connectedCallback() {
    const btn = this.shadowRoot!.querySelector('button')!;
    btn.addEventListener('click', () => {
      this.checked = !this.checked;
      btn.setAttribute('aria-pressed', String(this.checked));
      // include in form data under this element's name
      this.internals.setFormValue(this.checked ? '1' : '');
      // optional validation example
      const ok = !this.hasAttribute('required') || this.checked;
      this.internals.setValidity(ok ? {} : { valueMissing: true },
                                 ok ? '' : 'Required');
    });
  }

  // Form hooks (optional)
  formResetCallback() {
    this.checked = false;
    this.internals.setFormValue('');
    this.internals.setValidity({});
  }
}
customElements.define('x-toggle', XToggle);
```

Usage (native submit)

```html
<form>
  <x-toggle name="agree" required></x-toggle>
  <button>Submit</button>
</form>
```

Notes

* Must have a `name` to be serialized.
* Works with the `formdata` and normal form submission flows.
* Broad support in Chromium-based browsers and Safari; Firefox does not support FACE as of now.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://sygnal.gitbook.io/sygnal-webflow-components/cc/sygnal-forms/technical-notes.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
