Drop-in replacement for native text-type <input> elements with form participation,
validation, clear buttons, and slot-based customization.
Basic Usage
Code
<form class="form" id="example-form">
<label for="normal-input">Normal input</label>
<input name="normal-input" />
<m-input
label="m-input disabled"
name="m-input-example-disabled"
disabled
></m-input>
<m-input
label="m-input default value"
name="m-input-example-default-value"
value="Default value"
></m-input>
<m-input
label="m-input readonly"
name="m-input-example-readonly-value"
value="Readonly"
readonly
></m-input>
<m-input
label="m-input placeholder"
name="m-input-example-placeholder"
placeholder="Placeholder..."
></m-input>
<m-input
label="m-input clearable"
name="m-input-example-clearable"
clearable
></m-input>
<m-input
label="m-input size"
name="m-input-size" size="4"
></m-input>
<button type="submit">Submit</button>
<button type="reset">Reset</button>
</form>
<pre id="example-form-output"></pre>
<script>
document.querySelector('#example-form').addEventListener('submit', function(e) {
e.preventDefault();
const outputEl = document.querySelector("#example-form-output");
const formData = new FormData(e.target);
if(outputEl){
outputEl.textContent = JSON.stringify(Object.fromEntries(formData));
}
});
</script> Form Submission
Code
<form id="demo-form" class="form">
<m-input
label="Your name"
name="username"
placeholder="Type here…"
></m-input>
<button type="submit">Submit</button>
<div id="demo-output" class="box" data-variant="surface" data-span="2">
<strong>Form Data:</strong>
<pre id="demo-output-text" style="margin: 0.5rem 0 0 0;"></pre>
</div>
</form>
<script>
document.querySelector('#demo-form').addEventListener('submit', (e) => {
e.preventDefault();
const data = Object.fromEntries(new FormData(e.target));
document.querySelector('#demo-output-text').textContent =
JSON.stringify(data, null, 2);
});
</script> Layout And Labeling
Code
<div class="form">
<m-input
label="Email"
name="email"
placeholder="you@example.com"
></m-input>
<m-input
label="Vertical layout"
name="vertical"
self-orientation="vertical"
placeholder="Label appears above input"
></m-input>
</div> Clearable Input
Code
<form class="form" id="clearable-form">
<m-input
label="Default button"
name="default-clear"
value="Clear me"
clearable
></m-input>
<m-input
label="Custom button"
name="custom-clear"
value="Custom clear"
clearable
>
<button slot="clear" style="padding: 0 0.5rem;">✕</button>
</m-input>
<button type="reset" data-span="2">Reset</button>
</form> Slots
Code
<div class="form">
<m-input label="Before">
<svg slot="before" xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></svg>
</m-input>
<m-input label="After">
<svg slot="after" xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="14" height="14" x="8" y="8" rx="2" ry="2"/><path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2"/></svg>
</m-input>
<m-input label="Both" placeholder="Search...">
<div slot="before" class="stack" data-direction="row" data-gap="1">
<button data-background="surface-raised" data-padding="i-3" data-border>tag 1</button>
<button data-background="surface-raised" data-padding="i-3" data-border>tag 2</button>
<button data-background="surface-raised" data-padding="i-3" data-border>tag 3</button>
</div>
<svg slot="after" xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m21 21-4.34-4.34"/><circle cx="11" cy="11" r="8"/></svg>
</m-input>
</div> Validation
Code
<form class="form" id="example-required-form">
<label for="normal-required-input">Normal input</label>
<input name="normal-required-input" required />
<label for="normal-min-input">Normal min 3</label>
<input name="normal-min-input" minlength="3" />
<m-input
label="Required"
name="m-input-required"
required="true"
></m-input>
<m-input
label="Min length"
name="m-input-min-length"
minlength="3"
></m-input>
<m-input
label="Max length"
name="m-input-max-length"
maxlength="3"
></m-input>
<m-input
label="Pattern [0-9]"
name="m-input-pattern"
pattern="[0-9]"
></m-input>
<button type="submit">Submit</button>
<button type="reset">Reset</button>
</form>
<pre id="example-required-form-output"></pre>
<script>
document.querySelector('#example-required-form').addEventListener('submit', function(e) {
e.preventDefault();
const outputEl = document.querySelector("#example-required-form-output");
const formData = new FormData(e.target);
if(outputEl){
outputEl.textContent = JSON.stringify(Object.fromEntries(formData));
}
});
</script> Events
Code
<form class="form" id="events-form">
<m-input
id="email-input"
label="Email (required)"
name="email"
type="email"
required
clearable
value="test@example.com"
></m-input>
<div id="event-log" class="box" data-variant="surface" data-span="2">
<strong>Event Log:</strong>
<ul id="log-list" style="margin: 0; padding-left: 1.5rem;"></ul>
</div>
<button type="submit">Submit</button>
</form>
<script>
const emailInput = document.querySelector('#email-input');
const logList = document.querySelector('#log-list');
emailInput.addEventListener('m-invalid', (e) => {
const li = document.createElement('li');
li.textContent = `m-invalid: "${e.detail.validationMessage}"`;
li.style.color = 'var(--color-destructive-fill-mid)';
logList.appendChild(li);
});
emailInput.addEventListener('m-input-clear', (e) => {
const li = document.createElement('li');
li.textContent = `m-input-clear: Previous value was "${e.detail.value}"`;
li.style.color = 'var(--color-neutral-fill-mid)';
logList.appendChild(li);
});
emailInput.addEventListener('input', () => {
if (emailInput.validity.valid && emailInput.value) {
const li = document.createElement('li');
li.textContent = 'Valid email entered';
li.style.color = 'var(--color-success-fill-mid)';
logList.appendChild(li);
}
});
</script> Css Parts
Code
<style>
.styled-input::part(input) {
font-family: var(--ff-display);
}
.styled-input::part(input-wrapper) {
border-radius: 20px;
color: var(--color-primary-fill-mid);
}
.styled-input::part(label) {
color: var(--color-primary-fill-mid);
}
.styled-input::part(error) {
background: var(--color-neutral-fill-mid);
border-color: var(--color-neutral-stroke-mid);
}
.styled-input::part(clear-button) {
color: var(--color-primary-fill-mid);
}
.styled-input::part(clear-button):hover {
color: var(--color-destructive-fill-mid);
}
</style>
<div class="form">
<m-input
class="styled-input"
label="Styled input"
placeholder="Custom styled..."
clearable
></m-input>
<m-input
class="styled-input"
label="With validation"
placeholder="Type at least 3 chars..."
minlength="3"
clearable
></m-input>
</div> - type "text" | "search" | "tel" | "url" | "email" | "password" "text"
- minLength number | undefined —
- maxLength number | undefined —
- pattern string | undefined —
- placeholder string | undefined —
- autocomplete string | undefined —
- size number | undefined —
- clearable boolean false
- autofocus boolean false
- clearSlotHasContent — —
- selectionStart number | null —
- selectionEnd number | null —
- selectionDirection "forward" | "backward" | "none" | null —
- handleKeydown — —
- handleBlur — —
- handleInput — —
- adoptedStyleSheets array [baseStyleSheet]
- select void
- focus void
- blur void
- setSelectionRange void
- setRangeText void
- setRangeText void
- setRangeText void
- m-invalid CustomEvent
- Fires when validation fails. Detail: { validity: ValidityState, validationMessage: string, value: string }
- m-input-clear CustomEvent
- Fires when clear button is clicked (cancelable). Detail: { value: string }
- (default)
- Default slot for component content
- before
- Content before the input element (icons, chips, buttons)
- after
- Content after the input element
- clear
- Override the default clear button
- ::part(label)
- The label element
- ::part(input-wrapper)
- The wrapper containing the input and slots
- ::part(input)
- The native input element
- ::part(clear-button)
- The clear button
- ::part(clear-icon)
- The icon inside the clear button
- ::part(error)
- The error message container