Conditional Styles
Learn how to use conditional and responsive styles in Chakra.
Overview
Chakra allows you to write styles for pseudo states, media queries, and custom data attributes with the conditional style props.
Usage
For example, here's how to change the background color of a button when it's hovered:
<Box bg="red.500" _hover={{ bg: "red.700" }}>
Hover me
</Box>Nested condition
Conditional values can be nested to create complex selector rules.
Here's how to change the background color of an element when in focus on hover:
<Box bg={{ base: "red.500", _hover: { _focus: "red.700" } }}>
Hover & Focus me
</Box>At Rules
This also works with the supported at-rules (@media, @layer, @container,
@supports, and @page):
<Box
css={{
"@container (min-width: 10px)": {
color: "green.300",
},
}}
>
Hello
</Box>Pseudo Classes
Hover, Active, Focus, and Disabled
Here's an example of how to style the hover, active, focus, and disabled states of an element
<chakra.button
_hover={{ bg: "red.700" }}
_active={{ bg: "red.900" }}
_focus={{ bg: "red.800" }}
_disabled={{ opacity: "0.5" }}
>
Hover me > Hover me
</chakra.button>First, Last, Odd, Even
Here's an example of how to style the first, last, odd, and even elements in a list
<Box as="ul">
{items.map((item) => (
<Box
as="li"
key={item}
_first={{ color: "red.500" }}
_last={{ color: "red.800" }}
>
{item}
</Box>
))}
</Box>You can also style even and odd elements using the _even and _odd modifier
<table>
<tbody>
{items.map((item) => (
<chakra.tr key={item} _even={{ bg: "gray.100" }} _odd={{ bg: "white" }}>
<td>{item}</td>
</chakra.tr>
))}
</tbody>
</table>Pseudo Elements
Before and After
To style the ::before and ::after pseudo elements of an element, use the
_before and _after modifiers
<Box _before={{ content: '"👋"' }} after={{ content: '"🥂"' }}>
Hello
</Box>Placeholder
To style the placeholder text of any input or textarea, use the _placeholder
modifier:
<chakra.input
placeholder="Enter your name"
_placeholder={{ color: "gray.500" }}
/>File Inputs
To style the file input button, use the _file modifier:
<chakra.input
type="file"
_file={{ bg: "gray.500", px: "4", py: "2", marginEnd: "3" }}
/>Media Queries
Reduced Motion
Use the _motionReduce and _motionSafe modifiers to style an element based on
the user's motion preference:
<Box _motionSafe={{ transition: "all 0.3s" }}>Hello</Box>Color Scheme
The prefers-color-scheme media feature is used to detect if the user has
requested the system to use a light or dark color theme.
Use the _osLight and _osDark modifiers to style an element based on the
user's color scheme preference:
<chakra.div bg={{ base: "white", _osDark: "black" }}>Hello</chakra.div>Color Contrast
The prefers-contrast media feature is used to detect if the user has requested
the system use a high or low contrast theme.
Use the _highContrast and _lessContrast modifiers to style an element based
on the user's color contrast preference:
<Box bg={{ base: "white", _highContrast: "black" }}>Hello</Box>Orientation
The orientation media feature is used to detect if the user has a device in
portrait or landscape mode.
Use the _portrait and _landscape modifiers to style an element based on the
user's device orientation:
<Box pb="4" _portrait={{ pb: "8" }}>
Hello
</Box>Selectors
Arbitrary selectors
For arbitrary, use the css prop to write styles for one-off selectors:
<Box css={{ "&[data-state=closed]": { color: "red.300" } }} />Here's another example that targets the child elements of a parent element:
<Box
css={{
"& > *": { margin: "2" },
}}
/>Group Selectors
To style an element based on its parent element's state or attribute, add the
group class to the parent element, and use any of the _group* modifiers on
the child element.
<div className="group">
<Text _groupHover={{ bg: "red.500" }}>Hover me</Text>
</div>This modifier works for every pseudo class modifiers like _groupHover,
_groupActive, _groupFocus, and _groupDisabled, etc.
Sibling Selectors
To style an element based on its sibling element's state or attribute, add the
peer class to the sibling element, and use any of the _peer* modifiers on
the target element.
<div>
<p className="peer">Hover me</p>
<Box _peerHover={{ bg: "red.500" }}>I'll change by bg</Box>
</div>Note: This only works for when the element marked with peer is a
previous siblings, that is, it comes before the element you want to start.
Data Attribute
LTR and RTL
To style an element based on the direction of the text, use the _ltr and
_rtl modifiers
<div dir="ltr">
<Box _ltr={{ ml: "3" }} _rtl={{ mr: "3" }}>
Hello
</Box>
</div>State
To style an element based on its data-{state} attribute, use the corresponding
_{state} modifier
<Box data-loading _loading={{ bg: "gray.500" }}>
Hello
</Box>This works for common states like data-active, data-disabled, data-focus,
data-hover, data-invalid, data-required, and data-valid.
<Box data-active _active={{ bg: "gray.500" }}>
Hello
</Box>Orientation
To style an element based on its data-orientation attribute, use the
_horizontal and _vertical modifiers
<Box
data-orientation="horizontal"
_horizontal={{ bg: "red.500" }}
_vertical={{ bg: "blue.500" }}
>
Hello
</Box>ARIA Attribute
To style an element based on its aria-{state}=true attribute, use the
corresponding _{state} prop
<Box aria-expanded="true" _expanded={{ bg: "gray.500" }}>
Hello
</Box>Reference
Here's a list of all the condition props you can use in Chakra:
| Condition name | Selector |
|---|---|
_hover | &:is(:hover, [data-hover]) |
_focus | &:is(:focus, [data-focus]) |
_focusWithin | &:focus-within |
_focusVisible | &:is(:focus-visible, [data-focus-visible]) |
_disabled | &:is(:disabled, [disabled], [data-disabled]) |
_active | &:is(:active, [data-active]) |
_visited | &:visited |
_target | &:target |
_readOnly | &:is(:read-only, [data-read-only]) |
_readWrite | &:read-write |
_empty | &:is(:empty, [data-empty]) |
_checked | &:is(:checked, [data-checked], [aria-checked=true]) |
_enabled | &:enabled |
_expanded | &:is([aria-expanded=true], [data-expanded]) |
_highlighted | &[data-highlighted] |
_before | &::before |
_after | &::after |
_firstLetter | &::first-letter |
_firstLine | &::first-line |
_marker | &::marker |
_selection | &::selection |
_file | &::file-selector-button |
_backdrop | &::backdrop |
_first | &:first-child |
_last | &:last-child |
_only | &:only-child |
_even | &:even |
_odd | &:odd |
_firstOfType | &:first-of-type |
_lastOfType | &:last-of-type |
_onlyOfType | &:only-of-type |
_peerFocus | .peer:is(:focus, [data-focus]) ~ & |
_peerHover | .peer:is(:hover, [data-hover]) ~ & |
_peerActive | .peer:is(:active, [data-active]) ~ & |
_peerFocusWithin | .peer:focus-within ~ & |
_peerFocusVisible | .peer:is(:focus-visible, [data-focus-visible]) ~ & |
_peerDisabled | .peer:is(:disabled, [disabled], [data-disabled]) ~ & |
_peerChecked | .peer:is(:checked, [data-checked], [aria-checked=true]) ~ & |
_peerInvalid | .peer:is(:invalid, [data-invalid], [aria-invalid=true]) ~ & |
_peerExpanded | .peer:is([aria-expanded=true], [data-expanded]) ~ & |
_peerPlaceholderShown | .peer:placeholder-shown ~ & |
_groupFocus | .group:is(:focus, [data-focus]) & |
_groupHover | .group:is(:hover, [data-hover]) & |
_groupActive | .group:is(:active, [data-active]) & |
_groupFocusWithin | .group:focus-within & |
_groupFocusVisible | .group:is(:focus-visible, [data-focus-visible]) & |
_groupDisabled | .group:is(:disabled, [disabled], [data-disabled]) & |
_groupChecked | .group:is(:checked, [data-checked], [aria-checked=true]) & |
_groupExpanded | .group:is([aria-expanded=true], [data-expanded]) & |
_groupInvalid | .group:invalid & |
_indeterminate | &:is(:indeterminate, [data-indeterminate], [aria-checked=mixed]) |
_required | &:is(:required, [data-required], [aria-required=true]) |
_valid | &:is(:valid, [data-valid]) |
_invalid | &:is(:invalid, [data-invalid], [aria-invalid=true]) |
_autofill | &:autofill |
_inRange | &:in-range |
_outOfRange | &:out-of-range |
_placeholder | &::placeholder |
_placeholderShown | &:placeholder-shown |
_pressed | &:is([aria-pressed=true], [data-pressed]) |
_selected | &:is([aria-selected=true], [data-selected]) |
_default | &:default |
_optional | &:optional |
_open | &[open] |
_fullscreen | &:fullscreen |
_loading | &:is([data-loading], [aria-busy=true]) |
_currentPage | &[aria-current=page] |
_currentStep | &[aria-current=step] |
_motionReduce | @media (prefers-reduced-motion: reduce) |
_motionSafe | @media (prefers-reduced-motion: no-preference) |
_print | @media print |
_landscape | @media (orientation: landscape) |
_portrait | @media (orientation: portrait) |
_dark | &.dark, .dark & |
_light | &.light, .light & |
_osDark | @media (prefers-color-scheme: dark) |
_osLight | @media (prefers-color-scheme: light) |
_highContrast | @media (forced-colors: active) |
_lessContrast | @media (prefers-contrast: less) |
_moreContrast | @media (prefers-contrast: more) |
_starting | @starting-style |
_ltr | [dir=ltr] & |
_rtl | [dir=rtl] & |
_scrollbar | &::-webkit-scrollbar |
_scrollbarThumb | &::-webkit-scrollbar-thumb |
_scrollbarTrack | &::-webkit-scrollbar-track |
_horizontal | &[data-orientation=horizontal] |
_vertical | &[data-orientation=vertical] |
Customization
Chakra lets you create your own conditions, so you're not limited to the ones in the default preset. Learn more about customizing conditions here.