Form State
Overview
Form state modifiers let you apply utility styles conditionally based on form element states. They wrap utility declarations in CSS pseudo-class selectors like :disabled, :checked, and :invalid, generating variant utility classes that adapt to form element behavior.
Why Use Form State Modifiers?
Form state modifiers help you:
- Style form validation states: Apply different colors and styles for valid and invalid inputs
- Handle disabled states: Reduce opacity or change appearance for disabled form elements
- React to user input: Style checked checkboxes, selected radios, and filled autofill fields
- Build accessible forms: Provide visual feedback that matches the semantic state of form controls
useDisabledModifier
The useDisabledModifier() function creates a modifier that applies styles when a form element is disabled.
import { styleframe } from "styleframe";
import { useOpacityUtility, useCursorUtility } from "@styleframe/theme";
import { useDisabledModifier } from "@styleframe/theme";
const s = styleframe();
const disabled = useDisabledModifier(s);
useOpacityUtility(s, { 50: '0.5' }, [disabled]);
useCursorUtility(s, { 'not-allowed': 'not-allowed' }, [disabled]);
export default s;
._opacity\:50 { opacity: 0.5; }
._cursor\:not-allowed { cursor: not-allowed; }
._disabled\:opacity\:50 { &:disabled { opacity: 0.5; } }
._disabled\:cursor\:not-allowed { &:disabled { cursor: not-allowed; } }
<button disabled class="_disabled:opacity:50 _disabled:cursor:not-allowed">
Disabled button
</button>
CSS Selector
| Modifier Name | CSS Selector |
|---|---|
disabled | &:disabled |
useEnabledModifier
The useEnabledModifier() function creates a modifier that applies styles when a form element is enabled.
CSS Selector
| Modifier Name | CSS Selector |
|---|---|
enabled | &:enabled |
useCheckedModifier
The useCheckedModifier() function creates a modifier that applies styles when a checkbox or radio button is checked.
import { styleframe } from "styleframe";
import { useColor } from "@styleframe/theme";
import { useBackgroundColorUtility, useBorderColorUtility } from "@styleframe/theme";
import { useCheckedModifier } from "@styleframe/theme";
const s = styleframe();
const { ref } = s;
const { colorPrimary } = useColor(s, { primary: '#006cff' } as const);
const checked = useCheckedModifier(s);
useBackgroundColorUtility(s, {
primary: ref(colorPrimary),
}, [checked]);
useBorderColorUtility(s, {
primary: ref(colorPrimary),
}, [checked]);
export default s;
._background-color\:primary { background-color: var(--color--primary); }
._border-color\:primary { border-color: var(--color--primary); }
._checked\:background-color\:primary {
&:checked { background-color: var(--color--primary); }
}
._checked\:border-color\:primary {
&:checked { border-color: var(--color--primary); }
}
<input
type="checkbox"
class="_checked:background-color:primary _checked:border-color:primary"
>
CSS Selector
| Modifier Name | CSS Selector |
|---|---|
checked | &:checked |
useIndeterminateModifier
The useIndeterminateModifier() function creates a modifier that applies styles when a checkbox is in an indeterminate state.
CSS Selector
| Modifier Name | CSS Selector |
|---|---|
indeterminate | &:indeterminate |
useRequiredModifier
The useRequiredModifier() function creates a modifier that applies styles to required form fields.
CSS Selector
| Modifier Name | CSS Selector |
|---|---|
required | &:required |
useOptionalModifier
The useOptionalModifier() function creates a modifier that applies styles to optional form fields.
CSS Selector
| Modifier Name | CSS Selector |
|---|---|
optional | &:optional |
useValidModifier
The useValidModifier() function creates a modifier that applies styles when a form element passes validation.
import { styleframe } from "styleframe";
import { useBorderColorUtility } from "@styleframe/theme";
import { useValidModifier, useInvalidModifier } from "@styleframe/theme";
const s = styleframe();
const valid = useValidModifier(s);
const invalid = useInvalidModifier(s);
useBorderColorUtility(s, {
success: '#28a745',
danger: '#dc3545',
}, [valid, invalid]);
export default s;
._border-color\:success { border-color: #28a745; }
._border-color\:danger { border-color: #dc3545; }
._valid\:border-color\:success {
&:valid { border-color: #28a745; }
}
._invalid\:border-color\:danger {
&:invalid { border-color: #dc3545; }
}
<input
type="email"
required
class="_valid:border-color:success _invalid:border-color:danger"
placeholder="Enter email"
>
CSS Selector
| Modifier Name | CSS Selector |
|---|---|
valid | &:valid |
useInvalidModifier
The useInvalidModifier() function creates a modifier that applies styles when a form element fails validation.
CSS Selector
| Modifier Name | CSS Selector |
|---|---|
invalid | &:invalid |
usePlaceholderShownModifier
The usePlaceholderShownModifier() function creates a modifier that applies styles when a form element's placeholder is visible (i.e., the input is empty).
import { styleframe } from "styleframe";
import { useTextColorUtility } from "@styleframe/theme";
import { usePlaceholderShownModifier } from "@styleframe/theme";
const s = styleframe();
const placeholderShown = usePlaceholderShownModifier(s);
useTextColorUtility(s, {
muted: '#6c757d',
}, [placeholderShown]);
export default s;
._text-color\:muted { color: #6c757d; }
._placeholder-shown\:text-color\:muted {
&:placeholder-shown { color: #6c757d; }
}
<!-- Style input when placeholder is visible (empty) -->
<input
class="_placeholder-shown:text-color:muted"
placeholder="Type something..."
>
CSS Selector
| Modifier Name | CSS Selector |
|---|---|
placeholder-shown | &:placeholder-shown |
useAutofillModifier
The useAutofillModifier() function creates a modifier that applies styles when a form element has been autofilled by the browser.
CSS Selector
| Modifier Name | CSS Selector |
|---|---|
autofill | &:autofill |
useReadOnlyModifier
The useReadOnlyModifier() function creates a modifier that applies styles to read-only form elements.
CSS Selector
| Modifier Name | CSS Selector |
|---|---|
read-only | &:read-only |
useFormStateModifiers
The useFormStateModifiers() function registers all form state modifiers at once and returns them as a destructurable object.
import { styleframe } from "styleframe";
import { useBorderColorUtility, useOpacityUtility } from "@styleframe/theme";
import { useFormStateModifiers } from "@styleframe/theme";
const s = styleframe();
const { disabled, valid, invalid, checked } = useFormStateModifiers(s);
useBorderColorUtility(s, {
success: '#28a745',
danger: '#dc3545',
}, [valid, invalid]);
useOpacityUtility(s, {
50: '0.5',
}, [disabled]);
export default s;
<input
type="email"
required
class="_valid:border-color:success _invalid:border-color:danger"
>
<button disabled class="_disabled:opacity:50">
Submit
</button>
Returned Modifiers
| Key | Modifier Name | CSS Selector |
|---|---|---|
disabled | disabled | &:disabled |
enabled | enabled | &:enabled |
checked | checked | &:checked |
indeterminate | indeterminate | &:indeterminate |
required | required | &:required |
optional | optional | &:optional |
valid | valid | &:valid |
invalid | invalid | &:invalid |
placeholderShown | placeholder-shown | &:placeholder-shown |
autofill | autofill | &:autofill |
readOnly | read-only | &:read-only |
readonly | readonly | &:read-only |
Examples
Form Validation Feedback
import { styleframe } from "styleframe";
import { useBorderColorUtility, useTextColorUtility } from "@styleframe/theme";
import { useFormStateModifiers } from "@styleframe/theme";
const s = styleframe();
const { valid, invalid, required, disabled } = useFormStateModifiers(s);
useBorderColorUtility(s, {
success: '#28a745',
danger: '#dc3545',
warning: '#ffc107',
}, [valid, invalid, required]);
useTextColorUtility(s, {
success: '#28a745',
danger: '#dc3545',
}, [valid, invalid]);
export default s;
<form>
<input
type="email"
required
class="_invalid:border-color:danger _valid:border-color:success"
placeholder="Email address"
>
<input
type="text"
class="_required:border-color:warning"
required
placeholder="Required field"
>
</form>
Custom Checkbox
import { styleframe } from "styleframe";
import { useColor } from "@styleframe/theme";
import { useBackgroundColorUtility, useBorderColorUtility } from "@styleframe/theme";
import { useCheckedModifier, useFocusVisibleModifier } from "@styleframe/theme";
const s = styleframe();
const { ref } = s;
const { colorPrimary } = useColor(s, { primary: '#006cff' } as const);
const checked = useCheckedModifier(s);
const focusVisible = useFocusVisibleModifier(s);
useBackgroundColorUtility(s, {
primary: ref(colorPrimary),
white: '#ffffff',
}, [checked]);
useBorderColorUtility(s, {
primary: ref(colorPrimary),
muted: '#dee2e6',
}, [checked, focusVisible]);
export default s;
<input
type="checkbox"
class="_background-color:white _border-color:muted _checked:background-color:primary _checked:border-color:primary"
>
Best Practices
- Combine valid/invalid with border colors: Provide clear visual feedback for form validation states
- Use disabled with opacity: Reduce opacity for disabled elements to indicate they cannot be interacted with
- Pair placeholder-shown with labels: Create floating label patterns by combining
placeholder-shownwith CSS transforms - Test autofill styling: Browser autofill can override your styles; use the
autofillmodifier to handle this - Use required with visual indicators: Add visual cues for required fields using the
requiredmodifier
FAQ
:disabled pseudo-class only works on native form elements. For custom components or <div>-based controls, use useAriaDisabledModifier to target aria-disabled="true" instead.[disabled, hover] to a utility generates _disabled:property:value, _hover:property:value, and combined variants. This lets you create nuanced interactions like hover effects that differ for enabled vs disabled states.useReadOnlyModifier (generates modifier name read-only) and useReadonlyModifier (generates modifier name readonly) target the same CSS pseudo-class &:read-only. They are aliases for convenience based on your preferred naming convention.:placeholder-shown matches when the input value is empty and the placeholder is visible. This is independent of validation states, so you can combine it with :valid or :invalid for more specific styling logic.