Skip to main content

Overview

Tokenami includes common selectors like hover, focus, and active out of the box. However, you can define your own custom selectors to handle more complex styling scenarios like parent state, nested elements, and media query combinations.

Default Selectors

Tokenami provides these selectors by default:
selectors: {
  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',
}

Configuring Custom Selectors

Add custom selectors in your tokenami.config.ts file using the selectors key. 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 > &',
    'data-active': '&[data-state="active"]',
    'has-focus': '&:has(:focus)',
  },
});

Nested Selectors

Selectors can be nested using arrays to combine media queries or other conditional logic:
export default createConfig({
  selectors: {
    // Only apply hover on devices that support it
    hover: ['@media (hover: hover) and (pointer: fine)', '&:hover'],
  },
});

Usage Examples

Parent State

Style an element based on its parent’s state:
import { css } from '@tokenami/css';

function Card() {
  return (
    <div className="parent">
      <img src="..." alt="" />
      <button style={css({
        '--parent-hover_color': 'var(--color_primary)',
        '--parent-hover_transform': 'scale(1.05)',
      })}>
        Hover parent to see effect
      </button>
    </div>
  );
}

Data Attributes

Style elements based on data attributes:
function Toggle() {
  return (
    <button 
      data-state="active"
      style={css({
        '--background': 'var(--color_gray)',
        '--data-active_background': 'var(--color_primary)',
      })}
    >
      Toggle
    </button>
  );
}

Descendant Elements

Style descendant elements using underscores for spaces:
function Article() {
  return (
    <article style={css({
      '--{&_p}_color': 'var(--color_text)',
      '--{&_a}_color': 'var(--color_link)',
      '--{&_a:hover}_text-decoration': 'underline',
    })}>
      <p>This is a paragraph with a <a href="#">link</a>.</p>
    </article>
  );
}
Arbitrary selectors using {&...} syntax can only style the current element and its descendants. They cannot target parents or siblings.

Common Use Cases

selectors: {
  'parent-hover': '.parent:hover > &',
  'parent-focus': '.parent:focus-within &',
}
selectors: {
  'not-first': '&:not(:first-child)',
  'not-last': '&:not(:last-child)',
  'empty': '&:empty',
}
selectors: {
  'checked': '&:checked',
  'indeterminate': '&:indeterminate',
  'required': '&:required',
  'invalid': '&:invalid',
}
selectors: {
  // Only apply hover styles on devices with hover capability
  hover: ['@media (hover: hover) and (pointer: fine)', '&:hover'],
}

Combining Selectors

You can combine custom selectors with breakpoints:
<button style={css({
  '--color': 'var(--color_black)',
  '--hover_color': 'var(--color_primary)',
  '--md_hover_color': 'var(--color_secondary)', // Different color on medium screens
})} />

Best Practices

Use semantic names that describe when the selector applies
Keep selector specificity low for easier overrides
Document complex selectors with comments in your config
Avoid overly specific selectors that couple styles to DOM structure

Next Steps

Property Aliases

Create shorthand names for CSS properties

Arbitrary Selectors

Learn about one-off arbitrary selectors