Appearance
Form input
A form input with :focus, :disabled, :invalid, :placeholder, and :read-only styling embedded inside variant expansions. Use this pattern when state pseudo-classes are part of the component's vocabulary.
Authoring
ts
// recipes/form-input.config.ts
import { defineComponent } from 'varia'
export default defineComponent('form-input', {
base: 'block w-full rounded-md border bg-white px-3 py-2 text-base shadow-sm placeholder:text-gray-400 focus:outline-none focus:ring-2 focus:ring-offset-1 disabled:bg-gray-50 disabled:text-gray-500 disabled:cursor-not-allowed',
variants: {
state: {
default: 'border-gray-300 focus:border-blue-500 focus:ring-blue-500',
error: 'border-red-500 text-red-700 placeholder:text-red-300 focus:border-red-500 focus:ring-red-500 invalid:border-red-500',
success: 'border-emerald-500 focus:border-emerald-500 focus:ring-emerald-500',
},
s: {
sm: 'px-2 py-1 text-sm',
md: 'px-3 py-2 text-base',
lg: 'px-4 py-3 text-lg',
},
readonly: 'read-only:bg-gray-50 read-only:cursor-default',
},
})Every state-driven style lives next to the value it modifies:
placeholder:text-gray-400inbasestyles the placeholder uniformly.invalid:border-red-500only inside theerrorstate expansion. The consumer opts in by writingform-input-state-error.read-only:utilities only emit when the consumer addsform-input-readonlyto the element.
Live preview
Consumption
html
<input class="form-input form-input-state-default form-input-s-md" />
<input class="form-input form-input-state-error form-input-s-md"
aria-invalid="true" />
<input class="form-input form-input-state-default form-input-s-lg form-input-readonly"
readonly value="cannot edit" />
<input class="form-input form-input-state-default form-input-s-md" disabled />Generated class names
| Class | Purpose |
|---|---|
form-input | Base input styling, including :focus, :disabled, ::placeholder |
form-input-state-default / -error / -success | Visual mode |
form-input-s-sm / -md / -lg | Size |
form-input-readonly | Toggles :read-only styling |