Welcome to Vanilla Breeze
This bell pulls live notifications from /go/notify/messages — the same contract documented at /docs/concepts/service-contracts/. Static articles like this one are the no-JS / no-backend fallback.
This bell pulls live notifications from /go/notify/messages — the same contract documented at /docs/concepts/service-contracts/. Static articles like this one are the no-JS / no-backend fallback.
Extend native HTML elements with custom behavior via customized built-in elements. Inherits all native semantics, accessibility, and form participation for free.
The is attribute creates customized built-in elements — native HTML elements enhanced with custom JavaScript behavior. Unlike autonomous custom elements (e.g., <my-component>), customized built-ins inherit all native behavior: semantics, accessibility, form participation, and default styles.
The syntax is <element is="custom-name">, where the custom element class extends the native element's interface (e.g., HTMLButtonElement, HTMLInputElement).
Add the is attribute to a native element to activate the custom behavior registered for that name.
<!-- Use a customized built-in button --><button is="confirm-button">Delete Account</button> <!-- The element is still a <button> with all native behavior: - Form participation - Keyboard activation (Enter/Space) - Default button styling - Screen reader announces "button" But with custom behavior added by the class -->
The custom element class must extend the specific element interface, and the define() call must include the extends option.
<script>class ConfirmButton extends HTMLButtonElement { connectedCallback() { this.addEventListener('click', (e) => { if (!confirm(`Are you sure you want to: ${this.textContent}?`)) { e.preventDefault(); e.stopPropagation(); } }); }} // Third argument { extends } tells the registry which element is being extendedcustomElements.define('confirm-button', ConfirmButton, { extends: 'button' });</script>
A text input that automatically formats phone numbers as the user types. It inherits all native input behavior — form submission, validation, labels, autofill.
<script>class AutoFormatInput extends HTMLInputElement { connectedCallback() { this.addEventListener('input', () => { // Auto-format as phone number: (123) 456-7890 const digits = this.value.replace(/\D/g, '').slice(0, 10); if (digits.length >= 6) { this.value = `(${digits.slice(0,3)}) ${digits.slice(3,6)}-${digits.slice(6)}`; } else if (digits.length >= 3) { this.value = `(${digits.slice(0,3)}) ${digits.slice(3)}`; } }); }} customElements.define('auto-format-input', AutoFormatInput, { extends: 'input' });</script> <label for="phone">Phone</label><input is="auto-format-input" type="tel" id="phone" name="phone" />
There are two types of custom elements. The is attribute is for the customized built-in type.
<!-- Customized built-in: extends <button>, inherits everything --><button is="fancy-button">Click Me</button> <!-- Autonomous custom element: starts from scratch --><fancy-button>Click Me</fancy-button>
| Feature | Customized Built-in (is) | Autonomous (<my-el>) |
|---|---|---|
| Syntax | <button is="x"> | <my-button> |
| Extends | Specific element interface | HTMLElement |
| Native semantics | Inherited | Must be added manually |
| Form participation | Inherited | Requires ElementInternals |
| Default styles | Inherited | None |
| Safari support | No | Yes |
| Browser support | Chrome, Firefox, Edge | All modern browsers |
<button is="fancy-button"> is announced as "button" by screen readers, participates in tab order, and responds to Enter and Space — with zero extra work.<fancy-button> would need role="button", tabindex="0", and keyboard event handlers to achieve the same accessibility.is approach inherits native validation, labels, and error messages automatically.is attribute. This is the most significant limitation. Safari's WebKit team has explicitly declined to implement it, citing architectural concerns. This makes customized built-ins unreliable for production use without a polyfill.is attributes and manually upgrading elements.HTMLButtonElement cannot be used on an <input>.is attributes works in Chrome and Firefox before JavaScript loads (the native element renders normally). In Safari, the is attribute is simply ignored.<my-element> syntax for universal compatibility.tabindex — manage focus order for custom interactive elementsdisabled — inherited by customized built-in form controls