Use when building or styling a component in libs/ui-react or libs/ui-rnative (and their visualization libs) — the cross-platform styling principles, plus routing to the platform mechanics: Tailwind + cva + cn on web, useStyleSheet + themeJS + lx on React Native. Load this before writing component styles.
Scanned 9/12/2026
Install to Claude Code
npx -y skills add LedgerHQ/lumen --skill component-styling --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Component Styling?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/ledgerhq-component-styling)More formats (shields.io, HTML) on the badges page.
---
name: component-styling
description: >-
Use when building or styling a component in libs/ui-react or libs/ui-rnative
(and their visualization libs) — the cross-platform styling principles, plus
routing to the platform mechanics: Tailwind + cva + cn on web, useStyleSheet +
themeJS + lx on React Native. Load this before writing component styles.
paths: libs/ui-react/**/*.tsx, libs/ui-react/**/*.ts, libs/ui-rnative/**/*.tsx, libs/ui-rnative/**/*.ts
---
# Component styling
Lumen ships the same components on web and React Native. The *vocabulary* of
design tokens is shared across both (`bg-muted` ↔ `t.colors.bg.muted`); the
*mechanism* is not. This skill states the principles that hold on both platforms,
then routes you to the platform mechanics.
## Route by lib
Derive the touched lib from the path (see the `Libraries` table in `AGENTS.md`),
then read the matching reference:
| Lib | Read |
| --- | --- |
| `libs/ui-react` | `references/react.md` (Tailwind + cva + cn) |
| `libs/ui-rnative` | `references/rnative.md` (`useStyleSheet` + themeJS + `lx`) |
**Never apply one platform's mechanics to the other lib.** A className on an RN
file or a `useStyleSheet` call on a web file is always wrong.
## Shared principles (both platforms)
These hold regardless of mechanism — the references show how each realises them.
- **Design tokens, never raw values.** Colours, typography, spacing and sizes
come from the design system, not from raw Tailwind palette classes / arbitrary
sizes (web) or hardcoded literals (RN). The token catalogues live in
`references/*`.
- **Props-drill to the top-level element.** Behaviour must never be hidden. When
a prop legitimately targets a *nested* element (a nested `ref`, `className`,
or `style`), give it a distinct name (`contentClassName`, `inputStyle`) rather
than overloading the top-level prop.
- **Compose variants declaratively.** Appearance / size / state maps are declared
once at the top of the styling layer (`cva` on web, `Record<Variant, value>`
inside `useStyleSheet` on RN), never assembled ad hoc in the render body.
- **Never build a style value by concatenation.** ``text-${color}`` and its RN
equivalents are wrong — use complete token strings and a merge helper (`cn` on
web, `StyleSheet.flatten` on RN).
## Review checks
Rules verifiable from a diff. Lint already covers unknown/invalid Tailwind
classnames, banned imports and formatting — see the "already enforced" list in
`pr-review`; don't raise those here.
| Check | Applies to | Detect | Skip |
| --- | --- | --- | --- |
| Raw Tailwind value instead of a token | react | `text-gray-`, `bg-blue-`, `font-bold`, `text-sm`, `w-[`, `h-[` | `.mdx` doc-table markup |
| Dynamic classname built by concatenation | react | `` className={`...${ ` `` | — |
| Classnames in a bare string variable instead of `cn()` / `cva()` | react | `const … = 'flex …'` outside a `cn(` / `cva(` call | strings inlined in `className` |
| `cva` skipped where variants/many classes exist (or forced for 1–2 classes) | react | inline variant branching in JSX | — |
| `data-[state=…]` on a Base UI primitive (or `data-[open]` on a Radix one) | react | import source vs attribute form | — |
| Hardcoded colour/spacing/size literal instead of a `t.*` token | rnative | numeric or `'#…'` inside `useStyleSheet` | geometry constants under `Components/visualization/` |
| Large stylesheet inlined in the component body instead of a `useXStyles` hook | rnative | `useStyleSheet` call inline in the component | small one-off styles |
| Consumer `style`/`lx` merged before internal styles (consumer can't win) | rnative | `flatten([style, styles.root])` (reversed order) | — |
| Nested override overloads the top-level prop instead of a distinct name | both | one `className`/`style` prop feeding an inner element | — |
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!