# Styling recipes by library Copyable starting points for every styling stack, for each of the three ways to use the picker. Every snippet targets the same public surface: `className` / `style`, the `--epr-*` variables and the stable `[data-epr-part]` selectors. Nothing here overrides structural layout (viewport overflow, grid geometry, cell positions), so keyboard navigation and virtualization keep working. **One rule decides what a snippet can do.** Color variables (`--epr-bg-color`, `--epr-highlight-color`, …) theme the *built-in look*. `unstyled` and a bare primitives `Root` remove the built-in look, so color variables do nothing there and you style the parts directly. Size variables (`--epr-emoji-size`, paddings, heights) apply in every mode. `Root appearance="default"` brings the built-in look into a composition. | Path | Component | You write | | --- | --- | --- | | **Theme** the built-in look | `` | variables on one class, optional part tweaks | | **Unstyled** supplied layout | `` | a stylesheet for every part | | **Composed** layout | `emoji-picker-react/primitives` | layout, parts and (optionally) your own components | Parts you can target: `root`, `panel`, `search`, `search-input`, `search-clear`, `skin-tone`, `skin-tone-button`, `category-nav`, `category-tab`, `viewport`, `list`, `category`, `category-label`, `category-content`, `emoji`, `variation-picker`, `preview`, `empty`, `loading`, `load-error`, `reactions`, `reaction`. State: `[data-epr-active]` on the hovered or focused emoji, active tab and active tone; `[data-epr-highlighted]` on the keyboard-highlighted emoji with `keepSearchFocus`; `[aria-selected="true"]` on the current tab; `[aria-pressed="true"]` on the current tone. Two surfaces are transparent under `unstyled` or a bare Root until you give them a background: the sticky `category-label` and the `variation-picker` overlay. Always keep a visible `:focus-visible` style on buttons and the input. Every stack below has a runnable version in [`stories/integrations`](../../stories/integrations) and, per design, in [`stories/recipes`](../../stories/recipes) (25 designs × 7 stacks, screenshot- and axe-tested). The [`example/`](../../example) app shows the three paths in plain CSS. --- ## Plain CSS ### Theme ```css .brand-picker { --epr-bg-color: #faf5ff; --epr-category-label-bg-color: #faf5ffe6; --epr-search-input-bg-color: #f3e8ff; --epr-picker-border-color: #e9d5ff; --epr-text-color: #6b21a8; --epr-highlight-color: #7c3aed; --epr-hover-bg-color: #ede9fe; --epr-focus-bg-color: #ddd6fe; --epr-picker-border-radius: 16px; --epr-emoji-size: 26px; } .brand-picker [data-epr-part='category-label'] { font-size: 12px; letter-spacing: 0.08em; text-transform: uppercase; } ``` ```jsx ``` Variables are declared by the library at zero specificity, so one class wins in any load order; no `!important`, no `aside.EmojiPickerReact` selectors. ### Unstyled ```css .paper-picker { --epr-emoji-size: 28px; background: Canvas; color: CanvasText; border: 1px solid #8884; border-radius: 14px; font: 14px/1.4 system-ui, sans-serif; } .paper-picker [data-epr-part='search'] { padding: 12px 12px 8px; } .paper-picker [data-epr-part='search-input'] { padding-inline: 12px 36px; border: 1px solid #8886; border-radius: 10px; background: transparent; color: inherit; font: inherit; } .paper-picker :is([data-epr-part='emoji'], [data-epr-part='category-tab'], [data-epr-part='search-clear'], [data-epr-part='skin-tone-button']) { border: 0; border-radius: 8px; background: transparent; color: inherit; cursor: pointer; } .paper-picker [data-epr-part='category-tab'] { color: #8888; } .paper-picker [data-epr-part='category-tab'][aria-selected='true'] { color: #2563eb; } .paper-picker [data-epr-part='emoji'][data-epr-active] { background: #8882; } .paper-picker :is(button, input):focus-visible { outline: 2px solid #2563eb; outline-offset: -2px; } .paper-picker [data-epr-part='category-label'] { background: Canvas; color: #888; font-size: 11px; font-weight: 600; text-transform: uppercase; } .paper-picker [data-epr-part='variation-picker'] { background: Canvas; border: 1px solid #8884; border-radius: 10px; box-shadow: 0 4px 12px #0003; } ``` ```jsx ``` ### Composed ```jsx import * as Picker from 'emoji-picker-react/primitives'; insert(e.emoji)}> ``` ```css .card { height: 440px; background: #0b1220; color: #e2e8f0; border-radius: 16px; } .card [data-epr-part='panel'] { gap: 8px; } .tabs { border-bottom: 1px solid #1e293b; padding: 0 12px; } /* …then the same part rules as the unstyled sheet above. */ ``` A bare Root has no height: give it one so the viewport scrolls and virtualizes. Render Panel and put flex/gap classes or styles on it directly; `[data-epr-part='panel']` is its stable selector. --- ## CSS Modules Module classes land on the root; the parts are global attribute selectors, so wrap them in `:global()`. ```css /* picker.module.css */ .picker { --epr-highlight-color: #0f766e; --epr-hover-bg-color: #ccfbf1; border-radius: 14px; } .picker :global([data-epr-part='category-label']) { font-weight: 600; text-transform: none; } .picker :global([data-epr-part='emoji']) { border-radius: 6px; } ``` ```jsx import styles from './picker.module.css'; ; // theme ; // unstyled: add rules for every part … // composed ``` Parts also take their own module class directly: ``, ``. --- ## Tailwind CSS (v4) Tailwind utilities live in cascade layers and cannot beat unlayered CSS, so opt the picker into a layer declared *before* Tailwind's: ```css /* app.css */ @layer epr, theme, base, components, utilities; @import 'tailwindcss'; ``` Then pass `cssLayer="epr"`; every utility now wins without `!important`. ### Theme Arbitrary properties set variables; arbitrary variants reach the parts: ```jsx ``` ### Unstyled Add `unstyled` and style the parts with variants instead of variables: ```jsx ``` ### Composed Utilities go straight onto each part, and `components` lets you write the buttons yourself: ```jsx import * as Picker from 'emoji-picker-react/primitives'; const components: Picker.PickerComponents = { Emoji: ({ emoji, className, ...props }) => (