Tokenami provides two ways to apply conditional styles: arbitrary selectors for quick prototyping and custom selectors for reusable patterns.
Arbitrary Selectors
Arbitrary selectors let you prototype quickly by writing CSS selectors inline. Use curly braces {} to define any selector:
import { css } from '@tokenami/css' ;
< div style = { css ({
'--{&:hover}_color' : 'var(--color_primary)' ,
'--{&:has(:focus)}_border-color' : 'var(--color_highlight)' ,
'--{&[data-state=open]}_border-color' : 'var(--color_primary)' ,
}) } />
Styling Descendants
Arbitrary selectors can style the current element and its descendants. Use an underscore _ to represent spaces in your selector:
import { css } from '@tokenami/css' ;
< div style = { css ({
// Style all <p> elements inside this div
'--{&_p}_color' : 'var(--color_primary)' ,
// Style links inside paragraphs
'--{&_p_a}_color' : 'var(--color_blue)' ,
// Complex selectors
'--{&_.card_h2}_font-size' : 'var(--text-size_xl)' ,
}) } />
Arbitrary Selector Examples
Hover and
Data attributes
Parent state
Structural selectors
< button style = { css ({
'--{&:hover}_background' : 'var(--color_blue)' ,
'--{&:focus-visible}_outline' : 'var(--line_ring)' ,
}) } >
Click me
</ button >
Custom Selectors
For commonly-used selectors, define them in your configuration to create reusable shorthand names. This makes your code cleaner and more maintainable.
Defining Custom Selectors
Add custom selectors to your tokenami.config.ts. Use the ampersand & to mark where the current element’s selector should be injected:
import { createConfig } from '@tokenami/css' ;
export default createConfig ({
selectors: {
'parent-hover' : '.parent:hover > &' ,
hover: '&:hover' ,
focus: '&:focus' ,
'focus-visible' : '&:focus-visible' ,
active: '&:active' ,
disabled: '&:disabled' ,
} ,
}) ;
Using Custom Selectors
Once defined, use custom selectors with an underscore prefix:
import { css } from '@tokenami/css' ;
< button style = { css ({
'--hover_background' : 'var(--color_primary)' ,
'--focus-visible_outline' : 'var(--line_ring)' ,
'--disabled_opacity' : 0.5 ,
}) } />
Built-in Selectors
Tokenami includes common selectors by default:
after - &::after
before - &::before
even - &:nth-child(even)
odd - &:nth-child(odd)
first-child - &:first-child
last-child - &:last-child
placeholder - &::placeholder
hover - &:hover
focus - &:focus
focus-visible - &:focus-visible
focus-within - &:focus-within
active - &:active
disabled - &:disabled
Advanced Custom Selectors
Nested Selectors
Custom selectors can include media queries or other at-rules by using arrays:
import { createConfig } from '@tokenami/css' ;
export default createConfig ({
selectors: {
// Only apply hover on devices with hover capability
hover: [ '@media (hover: hover) and (pointer: fine)' , '&:hover' ],
// Multiple conditions
'safe-hover' : [
'@media (hover: hover) and (pointer: fine)' ,
'&:not(:disabled):hover, &:not(:disabled):focus-visible' ,
],
} ,
}) ;
Parent-based Selectors
Create selectors that depend on parent state:
import { createConfig } from '@tokenami/css' ;
export default createConfig ({
selectors: {
'parent-hover' : '.parent:hover > &' ,
'group-hover' : '.group:hover &' ,
'group-focus' : '.group:focus-within &' ,
} ,
}) ;
Usage:
import { css } from '@tokenami/css' ;
< div className = "parent" >
< img src = "..." alt = "" />
< button style = { css ({
'--parent-hover_opacity' : 1 ,
'--parent-hover_color' : 'var(--color_primary)' ,
}) } >
Edit
</ button >
</ div >
Complex Selector Examples
Group patterns
Custom elements
export default createConfig ({
selectors: {
'group-hover' : [
'@media (hover: hover) and (pointer: fine)' ,
'.group:hover &' ,
],
'hover-within' : [
'@media (hover: hover) and (pointer: fine)' ,
'&:has(:where(a[href],button,input,select,textarea,[tabindex="0"]):not([tabindex="-1"]):not([disabled])):hover' ,
],
} ,
}) ;
Combining Selectors with Breakpoints
Both arbitrary and custom selectors can be combined with responsive breakpoints:
import { css } from '@tokenami/css' ;
< div style = { css ({
// Mobile
'--hover_background' : 'var(--color_blue)' ,
// Medium screens and up
'--md_hover_background' : 'var(--color_green)' ,
// Large screens and up
'--lg_hover_background' : 'var(--color_purple)' ,
// Arbitrary selector with breakpoint
'--md_{&:has(:focus)}_border' : 'var(--line_thick)' ,
}) } />
Best Practices
Use custom selectors for common patterns Define frequently-used selectors in your config to keep code clean and consistent.
Use arbitrary selectors for one-offs Use inline arbitrary selectors for unique cases that don’t justify a custom selector.
Limit descendant styling Arbitrary selectors work for current element and descendants, but avoid deep nesting for maintainability.
Consider hover support Wrap hover selectors in media queries to respect user preferences and device capabilities.
Arbitrary selectors are intentionally limited to the current element and its descendants to maintain encapsulation and predictability in your styles.