Skip to content
Merged
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
2 changes: 2 additions & 0 deletions .changeset/tall-pans-tickle.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
---
---
42 changes: 21 additions & 21 deletions .claude/skills/mosaic/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
---
name: mosaic
description: >-
Work on Mosaic UI: styling a component with slot recipes (`defineSlotRecipe` /
`useRecipe` / slots / variants), or building a flow — authoring a state machine
Work on Mosaic UI: styling a component with StyleX (`stylex.create`, `--cl-*`
tokens, `themeProps`), or building a flow — authoring a state machine
(`setup`, states/guards/`invoke`, wiring to React with `useMachine`/`useActor`/
`useSelector`), writing the controller (Clerk adapter) or view (rendering) layer,
testing any of those layers, or migrating a legacy / pre-Mosaic component into the
Expand All @@ -15,9 +15,10 @@ description: >-

Two things live under Mosaic, and this skill covers the how-to for both:

- **Styled components** are authored with **slot recipes** — one recipe owns a
part's slot identity (`data-cl-slot`), variants, state, and appearance
cascade; `useRecipe` resolves it and hands back per-slot props to spread.
- **Styled components** are authored with **StyleX** — `stylex.create` declares
the styles, `themeProps` emits the part's public identity (the `.cl-<slot>`
class plus `data-<axis>` attrs), and `mergeStyleProps` fuses the two with the
consumer's `className`/`style`.
- **Flows** follow a **machine → controller → view** split that keeps Clerk
resource logic out of visual components and makes behavior testable without a
running Clerk app:
Expand All @@ -35,25 +36,24 @@ view Rendering only: receives a snapshot plus explicit props, renders UI,
```

`references/mosaic-architecture.md` (repo root, read by all agents) is the
canonical contract for the whole design system — tokens, theme delivery, the
`data-cl-*` styling API, slot recipes, appearance/cascade/scope, and the "Flow
and data architecture" section that defines the split. Read it for the _what_;
this skill is the _how-to_.
canonical contract for the whole design system — the `--cl-*` tokens, the
`.cl-<slot>` + `data-<axis>` styling API, the CSS build, and the "Flow and data
architecture" section that defines the split. Read it for the _what_; this skill
is the _how-to_.

## Which reference to read

| You are… | Read |
| -------------------------------------------------------------------- | ------------------------------------------------------ |
| Building on / authoring a headless primitive (`@clerk/headless`) | `references/headless.md` |
| Styling a component with StyleX (tokens, `stylex.create`, CSS build) | `references/stylex.md` |
| Building an enter/exit transition, or any motion that reads as wrong | `references/motion.md` |
| Styling a component the legacy way (slot recipes, `useRecipe`) | `references/styling.md` |
| Authoring or debugging a state machine, or wiring one to React | `references/machines.md` → in-tree `machine/README.md` |
| Writing the controller (Clerk adapter, permissions, revalidate) | `references/controllers.md` |
| Writing the view (rendering a snapshot, sending events) | `references/views.md` |
| Testing a machine, controller, or view | `references/testing.md` |
| Migrating a legacy component into Mosaic (the end-to-end workflow) | `references/migration.md` |
| Running the parity audit that guards a migration | `references/parity-audit.md` |
| You are… | Read |
| ---------------------------------------------------------------------- | ------------------------------------------------------ |
| Building on / authoring a headless primitive (`@clerk/headless`) | `references/headless.md` |
| Styling a component (tokens, `stylex.create`, `themeProps`, CSS build) | `references/stylex.md` |
| Building an enter/exit transition, or any motion that reads as wrong | `references/motion.md` |
| Authoring or debugging a state machine, or wiring one to React | `references/machines.md` → in-tree `machine/README.md` |
| Writing the controller (Clerk adapter, permissions, revalidate) | `references/controllers.md` |
| Writing the view (rendering a snapshot, sending events) | `references/views.md` |
| Testing a machine, controller, or view | `references/testing.md` |
| Migrating a legacy component into Mosaic (the end-to-end workflow) | `references/migration.md` |
| Running the parity audit that guards a migration | `references/parity-audit.md` |

The migration workflow (`migration.md`) ties the flow references together: it
treats the legacy component as the spec and drives you through the machine,
Expand Down
17 changes: 8 additions & 9 deletions .claude/skills/mosaic/references/stylex.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
# Styling a component with StyleX

Mosaic styling is migrating off the Emotion slot-recipe engine (`styling.md`)
onto **StyleX** (`@stylexjs/stylex` 0.19). StyleX is compile-time atomic CSS: the
style objects become hashed atom classes plus one static stylesheet, with zero
runtime. This file is the authoring model for the StyleX layer; read it against
the pilot, `packages/ui/src/mosaic/components/button/`.

The public contract is unchanged from the recipe era — consumers still target
`--cl-*` vars, the `.cl-<slot>` class, and `data-<axis>` attrs, never StyleX's
hashed `x…` atoms. What changes is how a component is authored internally.
Mosaic is styled with **StyleX** (`@stylexjs/stylex` 0.19). StyleX is
compile-time atomic CSS: the style objects become hashed atom classes plus one
static stylesheet, with zero runtime. This file is the authoring model; read it
against the reference component, `packages/ui/src/mosaic/components/button/`.

Consumers target `--cl-*` vars, the `.cl-<slot>` class, and `data-<axis>` attrs,
never StyleX's hashed `x…` atoms. That public contract is fixed; everything
below is how a component is authored internally to produce it.

The guidance below is opinionated and evidence-backed: it codifies patterns
proven out across a full StyleX component library (tokens, ~100 components,
Expand Down
197 changes: 0 additions & 197 deletions .claude/skills/mosaic/references/styling.md

This file was deleted.

5 changes: 3 additions & 2 deletions .claude/skills/mosaic/references/testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -132,8 +132,9 @@ gate from `checkAuthorization`, `loading` until Clerk is loaded, and that

Build a plain `snapshot` object and pass a `vi.fn()` `send`. Assert what renders
per `snapshot.value` and that interactions send the right event. **Wrap the view
in `<MosaicProvider>`** — it's not a Clerk provider; it supplies the theme
context that `useRecipe`/`useSlot` read. No Clerk providers or fixtures.
in `<MosaicProvider>`** — it's not a Clerk provider; it supplies the icon-override
context, and wrapping keeps the test tree matching production. No Clerk providers
or fixtures.

```tsx
import { fireEvent, render, screen } from '@testing-library/react';
Expand Down
6 changes: 3 additions & 3 deletions packages/headless/src/primitives/dialog/dialog.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ import { Dialog } from './index';

afterEach(() => cleanup());

// Headless parts no longer emit `data-cl-slot` — slot identity is applied by the styled
// (mosaic) layer. Tests locate the surface-only parts (backdrop, viewport, trigger) via
// `data-testid` and everything else via its accessible role or text.
// Headless parts carry no slot identity — that is applied by the styled (mosaic) layer.
// Tests locate the surface-only parts (backdrop, viewport, trigger) via `data-testid` and
// everything else via its accessible role or text.
function renderDialog(props: Partial<React.ComponentProps<typeof Dialog.Root>> = {}) {
return render(
<Dialog.Root {...props}>
Expand Down
Loading
Loading