trendmicro-frontend/tonic-ui/tonic-ui-sx
Claude Code and agent skill by trendmicro-frontend/tonic-ui
- community
Checking that the SKILL.md file is still in the repository.
Choose sx, __sx, and composeSx when authoring or reviewing Tonic UI Box-based components, including wrappers and headless-library prop getters. Use for base styles, style precedence, consumer overrides that do not apply, or incoming __sx overrides that are dropped.
Install
npx vskill@latest install trendmicro-frontend/tonic-ui/tonic-ui-sxSKILL.md
Tonic UI: sx vs __sx
All docs/ and packages/ paths are relative to the repository root. Component paths such as button/Button.js are relative to packages/react/src/. Read composition and verification when implementing a wrapper, transition, multi-feature prop getter, or regression check. Its worked examples and verification cases supplement the core rules below.
When to Use
Use this skill for component base styles, useXxxStyle() hooks, Box style routing, incoming __sx folds, wrappers overriding children, and headless-library prop getters such as react-table's getRowProps() / getHeaderProps(). It applies to any package rendering Tonic UI Box-based components, including external data-grid integrations. Also use it to investigate dropped wrapper styles or consumer overrides that lose.
Current Integration Status
Full __sx integration is not complete in this Tonic UI checkout. The helper, Box channel, and useSlot composition exist, and some components use them; the examples below describe the target styling convention, not proof that every component already follows it. Inspect the affected implementation before relying on it, and do not expand a task into full styling integration without a user request.
The one rule that governs everything: precedence-by-origin
A component's base styling always loses to a consumer override, regardless of which CSS property each touches. A wrapper's child-override beats the child's base but still loses to the end consumer. To make that true structurally (not by convention), Tonic UI routes who wrote the style to a fixed channel — and the channel decides precedence.
So the question is never "what does this style do?" but "who is writing it?"
- You are the component author, styling your own `Box` →
__sx(lowest tier). - You are a wrapper, overriding a child component you render → also
__sx(folded into the child's incoming__sx). - You are the consumer (app developer) →
sx(highest tier), or style props / pseudo props.
A component never writes its own styling to `sx`. sx is the consumer's escape hatch.
"Writing it" isn't limited to a component's own render body. A higher-order component wrapping a child, or a headless-library plugin contributing props via a getter function (e.g. react-table's getRowProps()/getHeaderProps(), composed across features by a shallow merge), is just as much "the component author" for this purpose as a render body is — the same hand-built-style-object, hand-merged-via-ensureArray-or-object-spread failure shows up there too, just one layer removed from JSX.
The four channels of Box
Box concatenates four ordered channels into one Emotion class. For declarations of the same property at the same selector specificity, a later channel wins.
| Channel | Who writes it | Tier | Notes | |
|---|---|---|---|---|
| `__sx` (base sx) | Library / component internals | 0 — lowest | A component's own base styling. Full sx-object format. Internal: not in public BoxProps, never forwarded to the DOM. | |
Style props (px, bg, color, …) | Consumer | 1 | Flat layout/appearance props, resolved by the system transform. | |
Pseudo props (_hover, _active, _focusVisible, …) | Consumer | 3 | Each a single prop carrying a style object. | |
| `sx` | Consumer | 4 — highest | The consumer's arbitrary-style escape hatch. |
__sx and sx accept the identical authoring format: flat declarations, nested selectors ('& svg', '&:hover'), pseudo shortcuts (_hover), theme => … functions, design tokens, responsive arrays. They differ only in tier (who they belong to), not in what they can express.
Why __sx exists (the problem it solves)
Before __sx, base styling was authored as style props (tier 1) + pseudo props (tier 3). That made precedence a function of which kind of prop a value is — not who wrote it. Two failures followed:
- A component that needed nested selectors or pseudo-elements (which flat style props
can't express) had to author base styling via sx — but then the component's sx outranked the consumer's style props and sx (single shared slot), so the consumer could no longer override. Backwards.
- Wrappers overriding a child had to squat on the consumer's
sxchannel and hand-merge the
consumer's sx back on top (sx={[ownSx, ...ensureArray(sx)]}) — done inconsistently across the codebase, silently dropping overrides when an author forgot the spread.
__sx is a dedicated lowest-priority channel that reuses the sx transform. Base styling goes there; consumer style props and sx sit above it and override it. Precedence-by-kind becomes precedence-by-origin, with no per-author merge bets.
How to author base styling: the uniform fold
Every base-styling component follows the same call site. You cannot predict which components a future wrapper will inject __sx into, so apply this uniformly — don't pick and choose:
Why each part matters:
- `__sx` is destructured out of `rest` so it is not applied twice (once via the spread,
once via the explicit prop).
- `composeSx(styleProps, __sxProp)` is placed *after* `{...rest}` so the explicit
__sx
wins the spread, and the incoming __sx (already folded) lands after the component's own base → an injected override beats the component's base, while the consumer's sx (tier 4) still beats everything.
- `useXxxStyle()` returns the component's complete base — flat layout, pseudo rules, and
nested selectors in one value (an object, or an array when conditional/order-sensitive parts must compose). There is no separate get*Sx function; it was folded into useXxxStyle.
Destructure `__sx: __sxProp` (and `...rest`) directly from `useDefaultProps` — in one line. Do not capture the whole bag as const props = useDefaultProps(...). You need two things the opaque bag can't give you cleanly: the incoming __sx pulled out (so it isn't applied twice) and a ...rest that no longer contains it (so the explicit __sx={composeSx(...)} after the spread wins). The one-line destructure is the canonical form:
composeSx: array composition, never object merge
composeSx is variadic — pass any number of sx-values (composeSx(base, a, b)); arrays are flattened and undefined is skipped. It returns an array for the __sx/sx prop. Do not spread it as props.
If you author the element through `useSlot`, you don't fold `__sx` yourself. useSlot composes both ref (via useMergeRefs) and __sx (via composeSx) across props and slotProps internally — put the base in props.__sx, pass the consumer's slot props as slotProps, and the base stays below the slot's __sx. Do not strip __sx out of the slot props or hand-merge it; that's the hook's job (mirrors how it merges ref):
This is the single most important pitfall. Combining two sx-objects must use array composition ([a, b]), not object merge ({...a, ...b}):
- Array → both objects are emitted; source order resolves conflicts per declaration.
An incoming &:hover partially overrides the base &:hover (changes color, keeps the base background).
- Object merge → a nested key like
&:hoverfrombreplacesa's&:hover
entirely. The base hover styling is discarded. This is the wrong behavior and a common bug.
When there is no incoming value to fold (e.g. a sub-element whose style has no consumer __sx slot), pass the style object directly — composeSx adds nothing:
Transition and Prop-Getter Invariants
- Animation state derived from an enum and static configuration belongs in
__sx.
Fold incoming __sx last in both Box and function-child render paths.
- Function children must spread the handoff onto a Box-based element. Keep the
caller's style separate and explicitly destructured as an animation input.
- DOM-measured values belong in inline
style; conditionally include measured keys
so an undefined measurement does not erase the caller's value.
- Each prop-getter composition layer must fold multiple contributors'
__sxvia
composeSx, not shallow object spread.
- Extract base styling into
styles.js/use<Component>Style(), including static
values. Read the linked reference for the worked examples.
Consumer usage — sx
App developers override component styling with sx (highest tier), or with style props / pseudo props. They never touch __sx (it's internal and off the public type):
Both remain fully functional, coequal tiers — this doesn't change. Going forward, sx is the preferred authoring convention for new/migrated consumer JSX, including on the layout primitives (Box/Flex/Grid/Stack/StackItem/Space). The style-props-to-sx codemod (packages/codemod/src/style-props-to-sx) mechanically migrates existing flat-prop usage to sx.
The specificity boundary (read before promising "consumers can override anything")
Channel order resolves ties only at equal selector specificity. Specificity still applies:
- A flat base declaration in
__sx(e.g.{ color }) is overridable by a consumer flat
style prop or sx.
- A nested/pseudo base rule in
__sx(e.g.{ '&:hover': { color } },{ '& svg': {…} })
has higher specificity than a consumer's flat style prop, so a flat style prop will not override it — only a consumer rule of equal-or-higher specificity (a matching sx / _hover) will. This is inherent to CSS, not a quirk of __sx. Don't tell a consumer a flat color prop will override a base &:hover color.
Naming conventions
This is the start of the file. Read the full SKILL.md on GitHub.
Security
- Security scanPassed, 100 of 100Pattern checks on the skill's files.
- Model reviewNot runA model review is scheduled for the next rescan.
Quality evaluation
- Task evaluationNot runNo evaluation yet. Run one in Skill Studio.
Source
- SKILL.md in the repositoryCheckingskills/internal/tonic-ui-sx/SKILL.md
Popularity
- Repository stars
- 133
- Repository forks
- 39
- 7-day trend score
- 0
Stars and forks are for the whole GitHub repository, not this skill alone. Not refreshed yet.
Works with
Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.
- Claude Code
- Cursor
- GitHub Copilot
- Windsurf
- Codex
- Gemini CLI
- Cline
- Amp
- Roo Code
- Goose
- Continue
- Kilo Code
- Junie
- Trae
- OpenHands
- Qwen Code
- AI Agent
- Tabnine Chat
- Sourcegraph Cody
- Amazon Q
- Firebase Genkit
- Sweep
- Devin
- Phind
- Mentat
- Claude Desktop
- Aider
- GPT Pilot
- Plandex
- bolt.new
- v0
- Replit Agent
- Anse
- TypingMind
- BetterChatGPT
- LibreChat
- Warp AI
- Pieces
- Fig AI
Embed badge
[](https://verified-skill.com/skills/trendmicro-frontend/tonic-ui/tonic-ui-sx)