API
Input
Text inputs of every type, with optional icons.
Installation
pnpm dlx zazz-ui add inputnpx zazz-ui add inputyarn dlx zazz-ui add inputbunx zazz-ui add inputAlso adds fields, which it depends on. Run zazz-ui init first if the project has no zazz/ folder yet.
Examples
Default
We'll never sell or share your email address.
Please enter a valid email address.
<div data-ui="field">
<label data-field-slot="label" for="f-name">Name</label>
<input
data-ui="input"
id="f-name"
name="name"
type="text"
inputmode="text"
placeholder="Ada Lovelace"
autocomplete="name"
autocapitalize="words"
enterkeyhint="next"
/>
</div>
<div data-ui="field">
<label data-field-slot="label" for="f-email">Email</label>
<input
data-ui="input"
id="f-email"
name="email"
type="email"
inputmode="email"
required
placeholder="you@example.com"
autocomplete="email"
autocapitalize="off"
spellcheck="false"
enterkeyhint="next"
aria-describedby="f-email-hint"
/>
<div data-field-slot="description">
<span data-field-slot="hint" id="f-email-hint"
>We'll never sell or share your email address.</span
>
<span data-field-slot="error" role="alert">Please enter a valid email address.</span>
</div>
</div>
<div data-ui="field">
<label data-field-slot="label" for="f-phone">Phone</label>
<input
data-ui="input"
id="f-phone"
name="phone"
type="tel"
inputmode="tel"
placeholder="+1 (555) 012-3456"
autocomplete="tel"
enterkeyhint="next"
/>
</div>
<div data-ui="field">
<label data-field-slot="label" for="f-date">Datetime</label>
<input
data-ui="input"
id="f-date"
name="date"
type="datetime-local"
inputmode="none"
autocomplete="off"
/>
</div>/**
* input.css — Text input ([data-ui~="input"])
*
* @layer variables, components
* @requires _layers.css, _variables.css, _properties.css (--_*-resolved, --_focus-shadow), fields.css, _reset.css
* @uses ::-webkit-calendar-picker-indicator — date/time picker chrome (WebKit)
* @uses mask — calendar icon (not background-image, for color: currentColor)
* @tokens --ui-input-* (@layer variables; surface colors and height read the
* --ui-field-* family directly)
*/
@layer variables {
:root {
--ui-input-w: 100%;
--ui-input-min-w: 16ch;
--ui-input-pl: var(--ui-field-px);
--ui-input-pr: var(--ui-field-px);
--ui-input-font-size: var(--ui-field-font-size);
--ui-input-leading: var(--ui-field-leading);
--ui-input-border-width: var(--ui-field-border-width);
--ui-input-border-style: var(--ui-field-border-style);
--ui-input-border-color: var(--ui-field-border-color);
--ui-input-rounded: var(--ui-field-rounded);
--ui-input-calendar-picker-color: currentColor;
--ui-input-calendar-picker-icon-size: calc(var(--spacing) * 4);
--ui-input-calendar-picker-size: calc(var(--spacing) * 6);
--ui-input-calendar-picker-radius: var(--ui-field-inset-child-radius);
--ui-input-calendar-picker-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 256 256'%3E%3Crect width='256' height='256' fill='none'/%3E%3Crect x='40' y='40' width='176' height='176' rx='8' fill='none' stroke='currentColor' stroke-linecap='round' stroke-linejoin='round' stroke-width='16'/%3E%3Cline x1='176' y1='24' x2='176' y2='56' fill='none' stroke='currentColor' stroke-linecap='round' stroke-linejoin='round' stroke-width='16'/%3E%3Cline x1='80' y1='24' x2='80' y2='56' fill='none' stroke='currentColor' stroke-linecap='round' stroke-linejoin='round' stroke-width='16'/%3E%3Cline x1='40' y1='88' x2='216' y2='88' fill='none' stroke='currentColor' stroke-linecap='round' stroke-linejoin='round' stroke-width='16'/%3E%3C/svg%3E");
}
}
@layer ui.components {
/* ===========================================================================
TEXT INPUTS — data-ui="input"
- One identity for every text-like type (text, search, email, number, date, …);
the type only changes the picker/keyboard, not the box.
- Shares the --ui-field-* tokens defined in fields.css so it aligns with the select,
the textarea, and [data-ui~="button"].
=========================================================================== */
:where([data-ui~="input"]) {
/* dual-mode metrics go through the registered typed pair */
--_w-len: var(--_w-resolved, var(--ui-input-w));
--_w-num: var(--_w-resolved, var(--ui-input-w));
inline-size: calc(var(--_w-len) + var(--_w-num) * var(--spacing));
--_min-w-len: var(--_min-w-resolved, var(--ui-input-min-w));
--_min-w-num: var(--_min-w-resolved, var(--ui-input-min-w));
min-inline-size: calc(var(--_min-w-len) + var(--_min-w-num) * var(--spacing));
--_h-len: var(--_h-resolved, var(--ui-field-h));
--_h-num: var(--_h-resolved, var(--ui-field-h));
block-size: calc(var(--_h-len) + var(--_h-num) * var(--spacing));
--_pl-len: var(--_pl-resolved, var(--ui-input-pl));
--_pl-num: var(--_pl-resolved, var(--ui-input-pl));
padding-inline-start: calc(var(--_pl-len) + var(--_pl-num) * var(--spacing));
--_pr-len: var(--_pr-resolved, var(--ui-input-pr));
--_pr-num: var(--_pr-resolved, var(--ui-input-pr));
padding-inline-end: calc(var(--_pr-len) + var(--_pr-num) * var(--spacing));
font-size: var(--_font-size-resolved, var(--ui-input-font-size));
line-height: var(--_leading-resolved, var(--ui-input-leading));
color: var(--_text-resolved, var(--ui-field-text));
background-color: var(--_bg-resolved, var(--ui-field-bg));
border-width: var(--_border-width-resolved, var(--ui-input-border-width));
border-style: var(--_border-style-resolved, var(--ui-input-border-style));
border-color: var(--_border-color-resolved, var(--ui-input-border-color));
border-radius: var(--_rounded-resolved, var(--ui-input-rounded));
/* The ring is published to --_focus-shadow so a --shadow utility keeps
it; the transparent outline twin keeps focus visible in forced-colors */
--_ring-offset-width: 0px;
--_ring-width: 0px;
--_focus-in: max(0, -1 * sign(var(--_focus-ring-w, 1px)));
--_ring-fill: color-mix(in oklch, var(--ui-field-ring-color) var(--ring-opacity), transparent);
--_focus-shadow:
0 0 0 calc(var(--_ring-offset-width) * (1 - var(--_focus-in))) var(--ring-offset-shadow-color),
0 0 0 calc((var(--_ring-offset-width) + var(--_ring-width)) * (1 - var(--_focus-in)))
var(--_ring-fill, var(--ring-shadow-color)),
inset 0 0 0 calc(var(--_ring-offset-width) * var(--_focus-in)) var(--ring-offset-shadow-color),
inset 0 0 0 calc((var(--_ring-offset-width) + var(--_ring-width)) * var(--_focus-in))
var(--_ring-fill, var(--ring-shadow-color));
box-shadow: var(--_focus-shadow, 0 0 #0000), var(--_shadow-resolved, 0 0 #0000);
outline: abs(var(--_focus-ring-w, var(--outline-width))) var(--outline-style) transparent;
outline-offset: calc(
var(--outline-offset) - var(--_focus-in) *
(2 * var(--outline-offset) + abs(var(--_focus-ring-w, var(--outline-width))))
);
transition: var(--default-transition);
}
@media (hover: hover) {
:where([data-ui~="input"]):hover {
border-color: var(--_border-color-resolved, var(--ui-field-border-color--hover));
background-color: var(--_bg-resolved, var(--ui-field-bg--hover));
}
}
:where([data-ui~="input"]):focus-within {
background-color: var(--_bg-resolved, var(--ui-field-bg--focus-within));
border-color: var(--_border-color-resolved, var(--ui-field-border-color--focus-within));
--_ring-offset-width: var(--ring-offset-width);
--_ring-width: abs(var(--_focus-ring-w, var(--ring-width)));
outline-color: transparent;
}
:where([data-ui~="input"])[type="date"],
:where([data-ui~="input"])[type="time"],
:where([data-ui~="input"])[type="datetime-local"] {
--ui-input-pr: calc(var(--spacing) * 1.5);
}
/* Native date/time controls keep their picker indicator tinted to the text */
:where([data-ui~="input"])::-webkit-calendar-picker-indicator {
cursor: pointer;
-webkit-appearance: none;
margin: 0;
padding: 0;
inline-size: var(--ui-input-calendar-picker-size);
block-size: var(--ui-input-calendar-picker-size);
border-radius: var(--ui-input-calendar-picker-radius);
transition:
opacity var(--default-transition-duration) var(--default-transition-timing-function),
display var(--default-transition-duration) var(--default-transition-timing-function)
allow-discrete;
mask: var(--ui-input-calendar-picker-mask) no-repeat center /
var(--ui-input-calendar-picker-icon-size);
background-size: var(--ui-input-calendar-picker-icon-size);
background-color: var(--ui-input-calendar-picker-color);
background-repeat: no-repeat;
background-position: center;
}
:where([data-ui~="input"])::-webkit-calendar-picker-indicator:is(:hover) {
opacity: 0.5;
}
}
/* Disabled and committed-invalid states; an invalid control remaps the
--ui-field-* family on itself. */
@layer ui.components {
:where([data-ui~="input"]):disabled {
opacity: 0.5;
cursor: not-allowed;
}
:where([data-ui~="input"]):user-invalid {
--ui-field-bg: color-mix(in oklch, var(--color-destructive) 5%, var(--color-input));
--ui-field-bg--hover: color-mix(in oklch, var(--color-destructive) 10%, var(--color-input));
--ui-field-border-color: var(--color-destructive);
--ui-field-border-color--hover: var(--color-destructive);
--ui-field-border-color--focus-within: var(--color-destructive);
--ui-field-ring-color: var(--color-destructive);
border-color: var(--color-destructive);
}
}Icon leading
<label data-ui="input-group">
<input data-ui="input" type="text" name="query" placeholder="Placeholder" />
<span data-input-group-slot="addon">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
aria-hidden="true"
>
<line x1="4" y1="6" x2="20" y2="6" />
<line x1="4" y1="12" x2="20" y2="12" />
<line x1="4" y1="18" x2="20" y2="18" />
</svg>
</span>
</label>/**
* input.css — Text input ([data-ui~="input"])
*
* @layer variables, components
* @requires _layers.css, _variables.css, _properties.css (--_*-resolved, --_focus-shadow), fields.css, _reset.css
* @uses ::-webkit-calendar-picker-indicator — date/time picker chrome (WebKit)
* @uses mask — calendar icon (not background-image, for color: currentColor)
* @tokens --ui-input-* (@layer variables; surface colors and height read the
* --ui-field-* family directly)
*/
@layer variables {
:root {
--ui-input-w: 100%;
--ui-input-min-w: 16ch;
--ui-input-pl: var(--ui-field-px);
--ui-input-pr: var(--ui-field-px);
--ui-input-font-size: var(--ui-field-font-size);
--ui-input-leading: var(--ui-field-leading);
--ui-input-border-width: var(--ui-field-border-width);
--ui-input-border-style: var(--ui-field-border-style);
--ui-input-border-color: var(--ui-field-border-color);
--ui-input-rounded: var(--ui-field-rounded);
--ui-input-calendar-picker-color: currentColor;
--ui-input-calendar-picker-icon-size: calc(var(--spacing) * 4);
--ui-input-calendar-picker-size: calc(var(--spacing) * 6);
--ui-input-calendar-picker-radius: var(--ui-field-inset-child-radius);
--ui-input-calendar-picker-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 256 256'%3E%3Crect width='256' height='256' fill='none'/%3E%3Crect x='40' y='40' width='176' height='176' rx='8' fill='none' stroke='currentColor' stroke-linecap='round' stroke-linejoin='round' stroke-width='16'/%3E%3Cline x1='176' y1='24' x2='176' y2='56' fill='none' stroke='currentColor' stroke-linecap='round' stroke-linejoin='round' stroke-width='16'/%3E%3Cline x1='80' y1='24' x2='80' y2='56' fill='none' stroke='currentColor' stroke-linecap='round' stroke-linejoin='round' stroke-width='16'/%3E%3Cline x1='40' y1='88' x2='216' y2='88' fill='none' stroke='currentColor' stroke-linecap='round' stroke-linejoin='round' stroke-width='16'/%3E%3C/svg%3E");
}
}
@layer ui.components {
/* ===========================================================================
TEXT INPUTS — data-ui="input"
- One identity for every text-like type (text, search, email, number, date, …);
the type only changes the picker/keyboard, not the box.
- Shares the --ui-field-* tokens defined in fields.css so it aligns with the select,
the textarea, and [data-ui~="button"].
=========================================================================== */
:where([data-ui~="input"]) {
/* dual-mode metrics go through the registered typed pair */
--_w-len: var(--_w-resolved, var(--ui-input-w));
--_w-num: var(--_w-resolved, var(--ui-input-w));
inline-size: calc(var(--_w-len) + var(--_w-num) * var(--spacing));
--_min-w-len: var(--_min-w-resolved, var(--ui-input-min-w));
--_min-w-num: var(--_min-w-resolved, var(--ui-input-min-w));
min-inline-size: calc(var(--_min-w-len) + var(--_min-w-num) * var(--spacing));
--_h-len: var(--_h-resolved, var(--ui-field-h));
--_h-num: var(--_h-resolved, var(--ui-field-h));
block-size: calc(var(--_h-len) + var(--_h-num) * var(--spacing));
--_pl-len: var(--_pl-resolved, var(--ui-input-pl));
--_pl-num: var(--_pl-resolved, var(--ui-input-pl));
padding-inline-start: calc(var(--_pl-len) + var(--_pl-num) * var(--spacing));
--_pr-len: var(--_pr-resolved, var(--ui-input-pr));
--_pr-num: var(--_pr-resolved, var(--ui-input-pr));
padding-inline-end: calc(var(--_pr-len) + var(--_pr-num) * var(--spacing));
font-size: var(--_font-size-resolved, var(--ui-input-font-size));
line-height: var(--_leading-resolved, var(--ui-input-leading));
color: var(--_text-resolved, var(--ui-field-text));
background-color: var(--_bg-resolved, var(--ui-field-bg));
border-width: var(--_border-width-resolved, var(--ui-input-border-width));
border-style: var(--_border-style-resolved, var(--ui-input-border-style));
border-color: var(--_border-color-resolved, var(--ui-input-border-color));
border-radius: var(--_rounded-resolved, var(--ui-input-rounded));
/* The ring is published to --_focus-shadow so a --shadow utility keeps
it; the transparent outline twin keeps focus visible in forced-colors */
--_ring-offset-width: 0px;
--_ring-width: 0px;
--_focus-in: max(0, -1 * sign(var(--_focus-ring-w, 1px)));
--_ring-fill: color-mix(in oklch, var(--ui-field-ring-color) var(--ring-opacity), transparent);
--_focus-shadow:
0 0 0 calc(var(--_ring-offset-width) * (1 - var(--_focus-in))) var(--ring-offset-shadow-color),
0 0 0 calc((var(--_ring-offset-width) + var(--_ring-width)) * (1 - var(--_focus-in)))
var(--_ring-fill, var(--ring-shadow-color)),
inset 0 0 0 calc(var(--_ring-offset-width) * var(--_focus-in)) var(--ring-offset-shadow-color),
inset 0 0 0 calc((var(--_ring-offset-width) + var(--_ring-width)) * var(--_focus-in))
var(--_ring-fill, var(--ring-shadow-color));
box-shadow: var(--_focus-shadow, 0 0 #0000), var(--_shadow-resolved, 0 0 #0000);
outline: abs(var(--_focus-ring-w, var(--outline-width))) var(--outline-style) transparent;
outline-offset: calc(
var(--outline-offset) - var(--_focus-in) *
(2 * var(--outline-offset) + abs(var(--_focus-ring-w, var(--outline-width))))
);
transition: var(--default-transition);
}
@media (hover: hover) {
:where([data-ui~="input"]):hover {
border-color: var(--_border-color-resolved, var(--ui-field-border-color--hover));
background-color: var(--_bg-resolved, var(--ui-field-bg--hover));
}
}
:where([data-ui~="input"]):focus-within {
background-color: var(--_bg-resolved, var(--ui-field-bg--focus-within));
border-color: var(--_border-color-resolved, var(--ui-field-border-color--focus-within));
--_ring-offset-width: var(--ring-offset-width);
--_ring-width: abs(var(--_focus-ring-w, var(--ring-width)));
outline-color: transparent;
}
:where([data-ui~="input"])[type="date"],
:where([data-ui~="input"])[type="time"],
:where([data-ui~="input"])[type="datetime-local"] {
--ui-input-pr: calc(var(--spacing) * 1.5);
}
/* Native date/time controls keep their picker indicator tinted to the text */
:where([data-ui~="input"])::-webkit-calendar-picker-indicator {
cursor: pointer;
-webkit-appearance: none;
margin: 0;
padding: 0;
inline-size: var(--ui-input-calendar-picker-size);
block-size: var(--ui-input-calendar-picker-size);
border-radius: var(--ui-input-calendar-picker-radius);
transition:
opacity var(--default-transition-duration) var(--default-transition-timing-function),
display var(--default-transition-duration) var(--default-transition-timing-function)
allow-discrete;
mask: var(--ui-input-calendar-picker-mask) no-repeat center /
var(--ui-input-calendar-picker-icon-size);
background-size: var(--ui-input-calendar-picker-icon-size);
background-color: var(--ui-input-calendar-picker-color);
background-repeat: no-repeat;
background-position: center;
}
:where([data-ui~="input"])::-webkit-calendar-picker-indicator:is(:hover) {
opacity: 0.5;
}
}
/* Disabled and committed-invalid states; an invalid control remaps the
--ui-field-* family on itself. */
@layer ui.components {
:where([data-ui~="input"]):disabled {
opacity: 0.5;
cursor: not-allowed;
}
:where([data-ui~="input"]):user-invalid {
--ui-field-bg: color-mix(in oklch, var(--color-destructive) 5%, var(--color-input));
--ui-field-bg--hover: color-mix(in oklch, var(--color-destructive) 10%, var(--color-input));
--ui-field-border-color: var(--color-destructive);
--ui-field-border-color--hover: var(--color-destructive);
--ui-field-border-color--focus-within: var(--color-destructive);
--ui-field-ring-color: var(--color-destructive);
border-color: var(--color-destructive);
}
}Icon trailing
<label data-ui="input-group">
<input data-ui="input" type="search" name="q" placeholder="Search…" aria-label="Search" />
<span data-input-group-slot="addon" data-input-group-align="inline-end">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
aria-hidden="true"
>
<circle cx="11" cy="11" r="8" />
<line x1="21" y1="21" x2="16.65" y2="16.65" />
</svg>
</span>
</label>/**
* input.css — Text input ([data-ui~="input"])
*
* @layer variables, components
* @requires _layers.css, _variables.css, _properties.css (--_*-resolved, --_focus-shadow), fields.css, _reset.css
* @uses ::-webkit-calendar-picker-indicator — date/time picker chrome (WebKit)
* @uses mask — calendar icon (not background-image, for color: currentColor)
* @tokens --ui-input-* (@layer variables; surface colors and height read the
* --ui-field-* family directly)
*/
@layer variables {
:root {
--ui-input-w: 100%;
--ui-input-min-w: 16ch;
--ui-input-pl: var(--ui-field-px);
--ui-input-pr: var(--ui-field-px);
--ui-input-font-size: var(--ui-field-font-size);
--ui-input-leading: var(--ui-field-leading);
--ui-input-border-width: var(--ui-field-border-width);
--ui-input-border-style: var(--ui-field-border-style);
--ui-input-border-color: var(--ui-field-border-color);
--ui-input-rounded: var(--ui-field-rounded);
--ui-input-calendar-picker-color: currentColor;
--ui-input-calendar-picker-icon-size: calc(var(--spacing) * 4);
--ui-input-calendar-picker-size: calc(var(--spacing) * 6);
--ui-input-calendar-picker-radius: var(--ui-field-inset-child-radius);
--ui-input-calendar-picker-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 256 256'%3E%3Crect width='256' height='256' fill='none'/%3E%3Crect x='40' y='40' width='176' height='176' rx='8' fill='none' stroke='currentColor' stroke-linecap='round' stroke-linejoin='round' stroke-width='16'/%3E%3Cline x1='176' y1='24' x2='176' y2='56' fill='none' stroke='currentColor' stroke-linecap='round' stroke-linejoin='round' stroke-width='16'/%3E%3Cline x1='80' y1='24' x2='80' y2='56' fill='none' stroke='currentColor' stroke-linecap='round' stroke-linejoin='round' stroke-width='16'/%3E%3Cline x1='40' y1='88' x2='216' y2='88' fill='none' stroke='currentColor' stroke-linecap='round' stroke-linejoin='round' stroke-width='16'/%3E%3C/svg%3E");
}
}
@layer ui.components {
/* ===========================================================================
TEXT INPUTS — data-ui="input"
- One identity for every text-like type (text, search, email, number, date, …);
the type only changes the picker/keyboard, not the box.
- Shares the --ui-field-* tokens defined in fields.css so it aligns with the select,
the textarea, and [data-ui~="button"].
=========================================================================== */
:where([data-ui~="input"]) {
/* dual-mode metrics go through the registered typed pair */
--_w-len: var(--_w-resolved, var(--ui-input-w));
--_w-num: var(--_w-resolved, var(--ui-input-w));
inline-size: calc(var(--_w-len) + var(--_w-num) * var(--spacing));
--_min-w-len: var(--_min-w-resolved, var(--ui-input-min-w));
--_min-w-num: var(--_min-w-resolved, var(--ui-input-min-w));
min-inline-size: calc(var(--_min-w-len) + var(--_min-w-num) * var(--spacing));
--_h-len: var(--_h-resolved, var(--ui-field-h));
--_h-num: var(--_h-resolved, var(--ui-field-h));
block-size: calc(var(--_h-len) + var(--_h-num) * var(--spacing));
--_pl-len: var(--_pl-resolved, var(--ui-input-pl));
--_pl-num: var(--_pl-resolved, var(--ui-input-pl));
padding-inline-start: calc(var(--_pl-len) + var(--_pl-num) * var(--spacing));
--_pr-len: var(--_pr-resolved, var(--ui-input-pr));
--_pr-num: var(--_pr-resolved, var(--ui-input-pr));
padding-inline-end: calc(var(--_pr-len) + var(--_pr-num) * var(--spacing));
font-size: var(--_font-size-resolved, var(--ui-input-font-size));
line-height: var(--_leading-resolved, var(--ui-input-leading));
color: var(--_text-resolved, var(--ui-field-text));
background-color: var(--_bg-resolved, var(--ui-field-bg));
border-width: var(--_border-width-resolved, var(--ui-input-border-width));
border-style: var(--_border-style-resolved, var(--ui-input-border-style));
border-color: var(--_border-color-resolved, var(--ui-input-border-color));
border-radius: var(--_rounded-resolved, var(--ui-input-rounded));
/* The ring is published to --_focus-shadow so a --shadow utility keeps
it; the transparent outline twin keeps focus visible in forced-colors */
--_ring-offset-width: 0px;
--_ring-width: 0px;
--_focus-in: max(0, -1 * sign(var(--_focus-ring-w, 1px)));
--_ring-fill: color-mix(in oklch, var(--ui-field-ring-color) var(--ring-opacity), transparent);
--_focus-shadow:
0 0 0 calc(var(--_ring-offset-width) * (1 - var(--_focus-in))) var(--ring-offset-shadow-color),
0 0 0 calc((var(--_ring-offset-width) + var(--_ring-width)) * (1 - var(--_focus-in)))
var(--_ring-fill, var(--ring-shadow-color)),
inset 0 0 0 calc(var(--_ring-offset-width) * var(--_focus-in)) var(--ring-offset-shadow-color),
inset 0 0 0 calc((var(--_ring-offset-width) + var(--_ring-width)) * var(--_focus-in))
var(--_ring-fill, var(--ring-shadow-color));
box-shadow: var(--_focus-shadow, 0 0 #0000), var(--_shadow-resolved, 0 0 #0000);
outline: abs(var(--_focus-ring-w, var(--outline-width))) var(--outline-style) transparent;
outline-offset: calc(
var(--outline-offset) - var(--_focus-in) *
(2 * var(--outline-offset) + abs(var(--_focus-ring-w, var(--outline-width))))
);
transition: var(--default-transition);
}
@media (hover: hover) {
:where([data-ui~="input"]):hover {
border-color: var(--_border-color-resolved, var(--ui-field-border-color--hover));
background-color: var(--_bg-resolved, var(--ui-field-bg--hover));
}
}
:where([data-ui~="input"]):focus-within {
background-color: var(--_bg-resolved, var(--ui-field-bg--focus-within));
border-color: var(--_border-color-resolved, var(--ui-field-border-color--focus-within));
--_ring-offset-width: var(--ring-offset-width);
--_ring-width: abs(var(--_focus-ring-w, var(--ring-width)));
outline-color: transparent;
}
:where([data-ui~="input"])[type="date"],
:where([data-ui~="input"])[type="time"],
:where([data-ui~="input"])[type="datetime-local"] {
--ui-input-pr: calc(var(--spacing) * 1.5);
}
/* Native date/time controls keep their picker indicator tinted to the text */
:where([data-ui~="input"])::-webkit-calendar-picker-indicator {
cursor: pointer;
-webkit-appearance: none;
margin: 0;
padding: 0;
inline-size: var(--ui-input-calendar-picker-size);
block-size: var(--ui-input-calendar-picker-size);
border-radius: var(--ui-input-calendar-picker-radius);
transition:
opacity var(--default-transition-duration) var(--default-transition-timing-function),
display var(--default-transition-duration) var(--default-transition-timing-function)
allow-discrete;
mask: var(--ui-input-calendar-picker-mask) no-repeat center /
var(--ui-input-calendar-picker-icon-size);
background-size: var(--ui-input-calendar-picker-icon-size);
background-color: var(--ui-input-calendar-picker-color);
background-repeat: no-repeat;
background-position: center;
}
:where([data-ui~="input"])::-webkit-calendar-picker-indicator:is(:hover) {
opacity: 0.5;
}
}
/* Disabled and committed-invalid states; an invalid control remaps the
--ui-field-* family on itself. */
@layer ui.components {
:where([data-ui~="input"]):disabled {
opacity: 0.5;
cursor: not-allowed;
}
:where([data-ui~="input"]):user-invalid {
--ui-field-bg: color-mix(in oklch, var(--color-destructive) 5%, var(--color-input));
--ui-field-bg--hover: color-mix(in oklch, var(--color-destructive) 10%, var(--color-input));
--ui-field-border-color: var(--color-destructive);
--ui-field-border-color--hover: var(--color-destructive);
--ui-field-border-color--focus-within: var(--color-destructive);
--ui-field-ring-color: var(--color-destructive);
border-color: var(--color-destructive);
}
}Attributes
| Attribute | Kind | Values | Notes |
|---|---|---|---|
data-ui="input" | Identity | free value | Identity token in data-ui. |
Requires: fields.
Hooks
Set a hook on :root, on a subtree, or inline on one element. A state hook (--…--hover) themes that state only; the primitive keeps its other states.
| Hook | Default | State |
|---|---|---|
--ui-input-border-color | var(--ui-field-border-color) | — |
--ui-input-border-style | var(--ui-field-border-style) | — |
--ui-input-border-width | var(--ui-field-border-width) | — |
--ui-input-calendar-picker-color | currentColor | — |
--ui-input-calendar-picker-icon-size | calc(var(--spacing) * 4) | — |
--ui-input-calendar-picker-mask | url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 256 256'%3E%3Crect width='256' height='256' fill='none'/%3E%3Crect x='40' y='40' width='176' height='176' rx='8' fill='none' stroke='currentColor' stroke-linecap='round' stroke-linejoin='round' stroke-width='16'/%3E%3Cline x1='176' y1='24' x2='176' y2='56' fill='none' stroke='currentColor' stroke-linecap='round' stroke-linejoin='round' stroke-width='16'/%3E%3Cline x1='80' y1='24' x2='80' y2='56' fill='none' stroke='currentColor' stroke-linecap='round' stroke-linejoin='round' stroke-width='16'/%3E%3Cline x1='40' y1='88' x2='216' y2='88' fill='none' stroke='currentColor' stroke-linecap='round' stroke-linejoin='round' stroke-width='16'/%3E%3C/svg%3E") | — |
--ui-input-calendar-picker-radius | var(--ui-field-inset-child-radius) | — |
--ui-input-calendar-picker-size | calc(var(--spacing) * 6) | — |
--ui-input-font-size | var(--ui-field-font-size) | — |
--ui-input-leading | var(--ui-field-leading) | — |
--ui-input-min-w | 16ch | — |
--ui-input-pl | var(--ui-field-px) | — |
--ui-input-pr | var(--ui-field-px) | — |
--ui-input-rounded | var(--ui-field-rounded) | — |
--ui-input-w | 100% | — |