Conditional Styles
Learn how to use conditional and responsive styles in Chakra.
Chakra allows you to write styles for pseudo states, media queries, and custom data attributes with the conditional style props.
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>
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>
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>
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>
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>
To style the ::before
and ::after
pseudo elements of an element, use the
_before
and _after
modifiers
<Box _before={{ content: '"👋"' }} after={{ content: '"🥂"' }}>
Hello
</Box>
To style the placeholder text of any input or textarea, use the _placeholder
modifier:
<chakra.input
placeholder="Enter your name"
_placeholder={{ color: "gray.500" }}
/>
To style the file input button, use the _file
modifier:
<chakra.input
type="file"
_file={{ bg: "gray.500", px: "4", py: "2", marginEnd: "3" }}
/>
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>
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>
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>
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>
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" },
}}
/>
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.
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.
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>
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>
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>
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>
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] |
Chakra lets you create your own conditions, so you're not limited to the ones in the default preset. Learn more about customizing conditions
.