Form State Modifiers
useFormStateModifiers is included in the Modifiers Preset (useModifiersPreset) and you can configure it through the preset's formStates option. For most projects, applying it via the preset is the recommended approach.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 { useColorDesignTokens } from "@styleframe/theme";
import { useBackgroundColorUtility, useBorderColorUtility } from "@styleframe/theme";
import { useCheckedModifier } from "@styleframe/theme";
const s = styleframe();
const { ref } = s;
const { colorPrimary } = useColorDesignTokens(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',
error: '#dc3545',
}, [valid, invalid]);
export default s;
._border-color\:success { border-color: #28a745; }
._border-color\:error { border-color: #dc3545; }
._valid\:border-color\:success {
&:valid { border-color: #28a745; }
}
._invalid\:border-color\:error {
&:invalid { border-color: #dc3545; }
}
<input
type="email"
required
class="_valid:border-color:success _invalid:border-color:error"
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',
error: '#dc3545',
}, [valid, invalid]);
useOpacityUtility(s, {
50: '0.5',
}, [disabled]);
export default s;
<input
type="email"
required
class="_valid:border-color:success _invalid:border-color:error"
>
<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',
error: '#dc3545',
warning: '#ffc107',
}, [valid, invalid, required]);
useTextColorUtility(s, {
success: '#28a745',
error: '#dc3545',
}, [valid, invalid]);
export default s;
<form>
<input
type="email"
required
class="_invalid:border-color:error _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 { useColorDesignTokens } from "@styleframe/theme";
import { useBackgroundColorUtility, useBorderColorUtility } from "@styleframe/theme";
import { useCheckedModifier, useFocusVisibleModifier } from "@styleframe/theme";
const s = styleframe();
const { ref } = s;
const { colorPrimary } = useColorDesignTokens(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.