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>
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 | @media (hover: hover)&:is(:hover, [data-hover]):not(:disabled, [data-disabled]) |
_active | &:is(:active, [data-active]):not(:disabled, [data-disabled], [data-state=open]) |
_focus | &:is(:focus, [data-focus]) |
_focusWithin | &:is(:focus-within, [data-focus-within]) |
_focusVisible | &:is(:focus-visible, [data-focus-visible]) |
_disabled | &:is(:disabled, [disabled], [data-disabled], [aria-disabled=true]) |
_visited | &:visited |
_target | &:target |
_readOnly | &:is([data-readonly], [aria-readonly=true], [readonly]) |
_readWrite | &:read-write |
_empty | &:is(:empty, [data-empty]) |
_checked | &:is(:checked, [data-checked], [aria-checked=true], [data-state=checked]) |
_enabled | &:enabled |
_expanded | &:is([aria-expanded=true], [data-expanded], [data-state=expanded]) |
_highlighted | &[data-highlighted] |
_complete | &[data-complete] |
_incomplete | &[data-incomplete] |
_dragging | &[data-dragging] |
_before | &::before |
_after | &::after |
_firstLetter | &::first-letter |
_firstLine | &::first-line |
_marker | &::marker |
_selection | &::selection |
_file | &::file-selector-button |
_backdrop | &::backdrop |
_first | &:first-of-type |
_last | &:last-of-type |
_notFirst | &:not(:first-of-type) |
_notLast | &:not(:last-of-type) |
_only | &:only-child |
_even | &:nth-of-type(even) |
_odd | &:nth-of-type(odd) |
_peerFocus | .peer:is(:focus, [data-focus]) ~ & |
_peerHover | .peer:is(:hover, [data-hover]):not(:disabled, [data-disabled]) ~ & |
_peerActive | .peer:is(:active, [data-active]):not(:disabled, [data-disabled]) ~ & |
_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], [data-state=checked]) ~ & |
_peerInvalid | .peer:is(:invalid, [data-invalid], [aria-invalid=true]) ~ & |
_peerExpanded | .peer:is([aria-expanded=true], [data-expanded], [data-state=expanded]) ~ & |
_peerPlaceholderShown | .peer:placeholder-shown ~ & |
_groupFocus | .group:is(:focus, [data-focus]) & |
_groupHover | .group:is(:hover, [data-hover]):not(:disabled, [data-disabled]) & |
_groupActive | .group:is(:active, [data-active]):not(:disabled, [data-disabled]) & |
_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], [data-state=checked]) & |
_groupExpanded | .group:is([aria-expanded=true], [data-expanded], [data-state=expanded]) & |
_groupInvalid | .group:invalid & |
_indeterminate | &:is(:indeterminate, [data-indeterminate], [aria-checked=mixed], [data-state=indeterminate]) |
_required | &:is([data-required], [aria-required=true]) |
_valid | &:is([data-valid], [data-state=valid]) |
_invalid | &:is([data-invalid], [aria-invalid=true], [data-state=invalid]) |
_autofill | &:autofill |
_inRange | &:is(:in-range, [data-in-range]) |
_outOfRange | &:is(:out-of-range, [data-outside-range]) |
_placeholder | &::placeholder, &[data-placeholder] |
_placeholderShown | &:is(:placeholder-shown, [data-placeholder-shown]) |
_pressed | &:is([aria-pressed=true], [data-pressed]) |
_selected | &:is([aria-selected=true], [data-selected]) |
_grabbed | &:is([aria-grabbed=true], [data-grabbed]) |
_underValue | &[data-state=under-value] |
_overValue | &[data-state=over-value] |
_atValue | &[data-state=at-value] |
_default | &:default |
_optional | &:optional |
_open | &:is([open], [data-open], [data-state=open]) |
_closed | &:is([closed], [data-closed], [data-state=closed]) |
_fullscreen | &is(:fullscreen, [data-fullscreen]) |
_loading | &:is([data-loading], [aria-busy=true]) |
_hidden | &:is([hidden], [data-hidden]) |
_current | &[data-current] |
_currentPage | &[aria-current=page] |
_currentStep | &[aria-current=step] |
_today | &[data-today] |
_unavailable | &[data-unavailable] |
_rangeStart | &[data-range-start] |
_rangeEnd | &[data-range-end] |
_now | &[data-now] |
_topmost | &[data-topmost] |
_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 .chakra-theme:not(.light) & |
_light | :root &, .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) |
_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] |
_icon | & :where(svg) |
_starting | @starting-style |
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.