Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,51 @@ This API only allows for setting CSS variables as primitive values. For more com
> [!IMPORTANT]
> By using `VariableContext` you may need to disable the `inlineVariable` optimization

## Pseudo-elements

React Native has no pseudo-elements. It has two props that stand in for one declaration each, and `::selection` / `::placeholder` compile to those props:

| CSS | React Native prop | On a |
| ---------------------------------- | ---------------------- | ----------- |
| `::selection { background-color }` | `selectionColor` | `TextInput` |
| `::placeholder { color }` | `placeholderTextColor` | `TextInput` |

`::selection { background-color }`, not `::selection { color }`. In CSS `color` inside `::selection` is the colour of the selected text and `background-color` is the band painted behind it; React Native's `selectionColor` is that band.

Every other declaration inside a pseudo-element is dropped:

```css
.input::selection {
background-color: red; /* → selectionColor */
color: white; /* dropped */
width: 10px; /* dropped */
}
```

The compiler records each drop, but nothing in the Metro pipeline reads that record — a `expo start` build prints nothing, and the only thing you observe is that the declaration has no effect on native. Two places do read it: `compile()`, and a jest test through `registerCSS(css, { debug: true })`.

```js
compile(css).warnings();
// { values: { "::selection": ["color", "width"] } }
```

They are dropped rather than applied because a pseudo-element's declarations belong to the pseudo-element. Applying them to the host would paint the element itself — a `::selection { color }` would set the element's text colour, and through `currentColor` its whole subtree.

A custom property is dropped the same way, and for the same reason: it would land on the host as a variable and every descendant would read it.

```css
.input::selection {
--brand: blue; /* dropped, reported as "--brand" */
}
```

With `inlineVariables` left on, a custom property declared once is substituted into its uses and its declaration removed before the pseudo-element is scoped at all — nothing reaches the pseudo-element, so nothing is dropped and nothing is reported. `inlineVariables: false`, the setting the `VariableContext` section above asks for, keeps every declaration, and that is where this drop costs the most.

> [!IMPORTANT]
> This is native only. On web the CSS file is served to the browser unchanged, so `::selection` and `::placeholder` behave exactly as CSS specifies and no declaration is dropped. A rule that is meaningful on both platforms should say so in `background-color` for `::selection` and `color` for `::placeholder`; anything else styles the browser and nothing else.

With Tailwind, the native prop comes from `selection:bg-*`. `selection:text-*` is `color` and is dropped on native, though it still works in a browser.

## Optimizations

CSS is a dynamic styling language that use highly optimized engines that are not available in React Native. Instead, we optimize the styles to improve performance
Expand Down
Loading