Skip to content

feat(ui): Introduce mosaic recipes and conditions#8830

Open
alexcarpenter wants to merge 12 commits into
mainfrom
carp/mosaic-recipes
Open

feat(ui): Introduce mosaic recipes and conditions#8830
alexcarpenter wants to merge 12 commits into
mainfrom
carp/mosaic-recipes

Conversation

@alexcarpenter

@alexcarpenter alexcarpenter commented Jun 11, 2026

Copy link
Copy Markdown
Member

Description

Mosaic styling system: slot recipes + data-attribute contract + appearance cascade. Replaces cva.

Public contract — data attributes

<button data-cl-slot="button" data-cl-color="primary" data-cl-size="sm" data-cl-disabled>
  • data-cl-slot — slot id
  • data-cl-<state> — presence-only, omitted when false
  • data-cl-<variant> — resolved variant (defaults emitted too, so sm vs md targetable)

Style 3 ways, same attrs

[data-cl-slot='button'][data-cl-size='sm'] { border-radius: 4px; }
appearance={{ elements: { button: { _disabled: { opacity: 0.4 } } } }}
<Button className="MyButton" />

Authoring — defineSlotRecipe

export const buttonRecipe = defineSlotRecipe(theme => ({
  slot: 'button',                                   // shorthand → implicit `root` slot
  base: { display: 'inline-flex', _disabled: { opacity: 0.5 } },
  variants: { size: { sm: { ...theme.text('xs') }, md: { ...theme.text('sm') } } },
  defaultVariants: { size: 'md' },
}));
declare module '../registry' { interface MosaicSlotRegistry { button: true } }

type ButtonProps = React.ComponentPropsWithRef<'button'> & RecipeVariantProps<typeof buttonRecipe>;

const Button = forwardRef<HTMLButtonElement, ButtonProps>(({ size, disabled, sx, ...rest }, ref) => {
  const { root } = useRecipe(buttonRecipe, { variants: { size }, state: { disabled: !!disabled }, sx });
  return <button ref={ref} disabled={disabled} {...rest} {...root} />;
});

root = { 'data-cl-slot', 'data-cl-size', 'data-cl-disabled'?, css }. css merged base → variants → compound → sx → appearance.

Multi-slot + sugar

useRecipe(cardRecipe, { variants: { tone } }); // → { root, header, body }
useSlot('avatarBox', { state: { disabled } }); // attrs + appearance css, no variants
slot('badgeText');                             // → { 'data-cl-slot': 'badgeText' }

appearance — variables global, elements scopable

<MosaicProvider scope="signIn" appearance={{
  variables: { rounded: { md: '1rem' } },     // tokens — global only, not scopable
  elements: {
    button: { backgroundColor: 'red' },        // global
    signIn: { button: { color: 'lime' } },     // scoped → wins inside <SignIn>
  },
}} />

Conditions

_hover@media (hover: hover) { &:hover }, _disabled&[data-cl-disabled], _focusVisible, _invalid, … Work in recipes / sx / appearance.elements. Merge by key (consumer wins), expand once at the end.

appearance={{ elements: { button: { _hover: { backgroundColor: 'red' } } } }} // overrides recipe hover

Misc

  • Slot registry via module augmentation — no central list, slot id co-located with recipe.
  • StyleRule = csstype CSSObject → CSS-prop autocomplete in elements/sx/recipes.
  • Removed cva.ts / cva.test.ts (engine absorbed into slot-recipe.ts).
  • Docs: references/mosaic-architecture.md.

Test

turbo test --filter @clerk/ui          # 52 pass
turbo build --filter @clerk/ui --filter @clerk/swingset   # type-check clean

Checklist

  • pnpm test runs as expected.
  • pnpm build runs as expected.
  • (If applicable) JSDoc comments have been added or updated for any package exports
  • (If applicable) Documentation has been updated

Type of change

  • 🐛 Bug fix
  • 🌟 New feature
  • 🔨 Breaking change
  • 📖 Refactoring / dependency upgrade / documentation
  • other:

Summary by CodeRabbit

  • New Features

    • Slot-recipe styling with data-attribute styling contract, scoped appearance layers, and named condition support (_hover, _disabled, etc.).
    • New Dialog component, stories, and grouped docs/navigation; docs viewer is now group-aware.
    • Story previews mount on the client before rendering playgrounds.
  • Bug Fixes

    • Improved appearance/variable resolution and CSS layering precedence.
  • Tests

    • Expanded coverage for recipes, slots, appearance parsing, conditions, and slot resolution.
  • Documentation

    • Updated architecture and migration guidance for the slot-recipe system.
  • Chores

    • Removed legacy CVA utilities; components migrated to slot recipes.

@vercel

vercel Bot commented Jun 11, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
clerk-js-sandbox Ready Ready Preview, Comment Jun 11, 2026 7:10pm
swingset Ready Ready Preview, Comment Jun 11, 2026 7:10pm

Request Review

@coderabbitai

coderabbitai Bot commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Replaces CVA-based styling with a slot-recipe system: slot registry and appearance parsing, condition expansion, slot resolution, defineSlotRecipe/useRecipe and useSlot, MosaicProvider now accepts appearance+scope, migrates Button/Input/Dialog to recipes, removes headless data-cl-slot emission, updates swingset/docs/storybook, and adds tests and docs.

Changes

Mosaic Slot Recipes Refactor

Layer / File(s) Summary
Slot Registry & Appearance System
packages/ui/src/mosaic/registry.ts, packages/ui/src/mosaic/appearance.ts, packages/ui/src/mosaic/MosaicProvider.tsx
Adds MosaicSlotRegistry typing, MosaicAppearance shape, parseMosaicAppearance, MosaicAppearanceProvider/hook, and updates MosaicProvider to accept appearance + scope and provide parsed element layers.
Slot Recipe Core Implementation
packages/ui/src/mosaic/slot-recipe.ts
Adds StyleRule/SxProp types, defineSlotRecipe and useRecipe with theme-resolve caching, variant/compound merging, sx merging, appearance cascade, condition expansion, and per-slot SlotProps.
Conditions, Slot Resolution & Utilities
packages/ui/src/mosaic/conditions.ts, packages/ui/src/mosaic/resolveSlot.ts, packages/ui/src/mosaic/useSlot.ts, packages/ui/src/mosaic/utils.ts
Adds conditions and expandConditions, resolveSlotCss/resolveSlotClassName, useSlot/slot primitives, stateToAttrs, and reduces utils to alpha only.
Primitive Bridge & useSlot
packages/ui/src/mosaic/primitives/withMosaicSlot.tsx, packages/ui/src/mosaic/primitives/dialog.tsx, packages/ui/src/mosaic/useSlot.ts
Introduces withMosaicSlot bridge and rewires dialog primitive parts to accept recipe slot props (css, data-cl-slot, state attrs).
Button, Input & Dialog Component Migrations
packages/ui/src/mosaic/components/button.tsx, packages/ui/src/mosaic/components/input.tsx, packages/ui/src/mosaic/components/dialog.tsx
Migrates Button/Input to buttonRecipe/inputRecipe and useRecipe-driven props; refactors Dialog to a multi-slot dialogRecipe and per-slot wrappers.
Headless Dialog Alignment & Tests
packages/headless/src/primitives/dialog/*, packages/headless/src/primitives/dialog/dialog.test.tsx, packages/headless/src/primitives/dialog/README.md
Removes headless-emitted data-cl-slot defaults, updates tests to use testids/roles and data-cl-open assertions, and documents that data-cl-slot is applied by the styled layer.
Swingset Docs & Storybook Integration
packages/swingset/src/components/DocsViewer.tsx, packages/swingset/src/lib/registry.ts, packages/swingset/src/components/StoryPreview.tsx, packages/swingset/src/stories/*
Makes DocsViewer group-aware, changes registry lookup to group+slug, StoryPreview gates client mount and passes playground variables via appearance={{ variables }}, updates stories to reference recipes, and adds Dialog MDX/story.
Tests & Docs
packages/ui/src/mosaic/__tests__/*, references/mosaic-architecture.md, .changeset/mosaic-slot-recipes.md
Adds/updates tests for provider parsing, conditions, slot resolution, recipe behavior, component tests, simplifies utils tests, rewrites architecture docs for slot-recipe system, and adds a changeset.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Possibly related PRs

Suggested reviewers

  • brkalow
  • Ephem

"A rabbit hops through the mosaic halls,
slots and recipes answer data calls.
Conditions expand with a gentle cheer,
appearance layers whisper near.
A tasty refactor—hip and clear! 🥕"

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 59.38% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat(ui): Introduce mosaic recipes and conditions' clearly and concisely describes the main change—the introduction of a new styling system (mosaic recipes and conditions) to the UI package.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions

github-actions Bot commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

API Changes Report

Generated by Break Check on 2026-06-11T19:13:28.693Z

Summary

Metric Count
Packages analyzed 19
Packages with changes 0
🔴 Breaking changes 0
🟡 Non-breaking changes 0
🟢 Additions 0

Note
Break Check could not snapshot 3 subpaths; the diff below excludes them.

  • @clerk/astro ./env: Internal Error: Unable to determine module for: /home/runner/_work/javascript/javascript/packages/astro/env.d.ts You have encountered a software defect. Please consider reporting the issue to the maintainers of this application.
  • @clerk/shared ./cookie: Internal Error: Unable to follow symbol for "Cookies" You have encountered a software defect. Please consider reporting the issue to the maintainers of this application.
  • @clerk/testing ./cypress: Symbol not found for identifier: Cypress

No API Changes Detected

All packages have stable APIs with no detected changes.


Report generated by Break Check

Last ran on d46e35c.

@pkg-pr-new

pkg-pr-new Bot commented Jun 11, 2026

Copy link
Copy Markdown

Open in StackBlitz

@clerk/astro

npm i https://pkg.pr.new/@clerk/astro@8830

@clerk/backend

npm i https://pkg.pr.new/@clerk/backend@8830

@clerk/chrome-extension

npm i https://pkg.pr.new/@clerk/chrome-extension@8830

@clerk/clerk-js

npm i https://pkg.pr.new/@clerk/clerk-js@8830

@clerk/expo

npm i https://pkg.pr.new/@clerk/expo@8830

@clerk/expo-passkeys

npm i https://pkg.pr.new/@clerk/expo-passkeys@8830

@clerk/express

npm i https://pkg.pr.new/@clerk/express@8830

@clerk/fastify

npm i https://pkg.pr.new/@clerk/fastify@8830

@clerk/hono

npm i https://pkg.pr.new/@clerk/hono@8830

@clerk/localizations

npm i https://pkg.pr.new/@clerk/localizations@8830

@clerk/nextjs

npm i https://pkg.pr.new/@clerk/nextjs@8830

@clerk/nuxt

npm i https://pkg.pr.new/@clerk/nuxt@8830

@clerk/react

npm i https://pkg.pr.new/@clerk/react@8830

@clerk/react-router

npm i https://pkg.pr.new/@clerk/react-router@8830

@clerk/shared

npm i https://pkg.pr.new/@clerk/shared@8830

@clerk/tanstack-react-start

npm i https://pkg.pr.new/@clerk/tanstack-react-start@8830

@clerk/testing

npm i https://pkg.pr.new/@clerk/testing@8830

@clerk/ui

npm i https://pkg.pr.new/@clerk/ui@8830

@clerk/upgrade

npm i https://pkg.pr.new/@clerk/upgrade@8830

@clerk/vue

npm i https://pkg.pr.new/@clerk/vue@8830

commit: d46e35c

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
packages/ui/src/mosaic/slot-recipe.ts (1)

269-311: ⚡ Quick win

Consider extracting the kebab utility to a shared module.

The kebab helper at line 310 is duplicated in useSlot.ts line 52. Extracting it to a shared utility module would reduce duplication and ensure consistency.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/ui/src/mosaic/slot-recipe.ts` around lines 269 - 311, Extract the
local kebab function into a shared utility module (e.g., export function
kebab(value: string): string) and replace the duplicated implementations by
importing that exported kebab in both slot-recipe.ts (used by variantsToAttrs
and stateToAttrs) and useSlot.ts; update the files to remove the local kebab
definitions, add the import, and ensure the shared util is exported for reuse
and covered by existing tests/typings.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.changeset/mosaic-slot-recipes.md:
- Around line 1-2: The empty changeset file .changeset/mosaic-slot-recipes.md
must be populated with a proper changeset: add a YAML frontmatter listing the
package "`@clerk/ui`" and set the bump type to "minor", and include a concise
summary explaining the mosaic slot recipes feature and the new public APIs
(e.g., defineSlotRecipe, useRecipe, MosaicProvider enhancements) so release
tooling and consumers understand the change.

In `@packages/ui/src/mosaic/appearance.ts`:
- Around line 63-88: Add a JSDoc block above the exported function
parseMosaicAppearance describing its purpose (parses a MosaicAppearance.elements
object into global+scoped layers), document the parameters (appearance?:
MosaicAppearance, scope?: string) with types and meanings, document the return
type (ParsedMosaicElements) and behavior (global keys collected first, then
scoped keys if scope matches SCOPE_KEY_SET), and include a short example showing
input appearance and the resulting array (as in the suggested snippet); ensure
the JSDoc is on the exported function so Clerk Docs will pick it up and mention
usage by MosaicProvider.

---

Nitpick comments:
In `@packages/ui/src/mosaic/slot-recipe.ts`:
- Around line 269-311: Extract the local kebab function into a shared utility
module (e.g., export function kebab(value: string): string) and replace the
duplicated implementations by importing that exported kebab in both
slot-recipe.ts (used by variantsToAttrs and stateToAttrs) and useSlot.ts; update
the files to remove the local kebab definitions, add the import, and ensure the
shared util is exported for reuse and covered by existing tests/typings.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Repository UI (inherited)

Review profile: CHILL

Plan: Pro

Run ID: 2013d262-0780-4240-b47e-2a8106383146

📥 Commits

Reviewing files that changed from the base of the PR and between 478f110 and 3312bd4.

📒 Files selected for processing (23)
  • .changeset/mosaic-slot-recipes.md
  • packages/swingset/src/components/StoryPreview.tsx
  • packages/swingset/src/stories/button.stories.tsx
  • packages/swingset/src/stories/input.stories.tsx
  • packages/ui/src/mosaic/MosaicProvider.tsx
  • packages/ui/src/mosaic/__tests__/MosaicProvider.test.tsx
  • packages/ui/src/mosaic/__tests__/conditions.test.ts
  • packages/ui/src/mosaic/__tests__/cva.test.ts
  • packages/ui/src/mosaic/__tests__/resolveSlot.test.ts
  • packages/ui/src/mosaic/__tests__/slot-recipe.test.ts
  • packages/ui/src/mosaic/__tests__/utils.test.ts
  • packages/ui/src/mosaic/appearance.ts
  • packages/ui/src/mosaic/components/__tests__/button.test.tsx
  • packages/ui/src/mosaic/components/button.tsx
  • packages/ui/src/mosaic/components/input.tsx
  • packages/ui/src/mosaic/conditions.ts
  • packages/ui/src/mosaic/cva.ts
  • packages/ui/src/mosaic/registry.ts
  • packages/ui/src/mosaic/resolveSlot.ts
  • packages/ui/src/mosaic/slot-recipe.ts
  • packages/ui/src/mosaic/useSlot.ts
  • packages/ui/src/mosaic/utils.ts
  • references/mosaic-architecture.md
💤 Files with no reviewable changes (3)
  • packages/ui/src/mosaic/tests/cva.test.ts
  • packages/ui/src/mosaic/utils.ts
  • packages/ui/src/mosaic/cva.ts

Comment thread .changeset/mosaic-slot-recipes.md
Comment thread packages/ui/src/mosaic/appearance.ts
@alexcarpenter alexcarpenter requested review from a team, Ephem and brkalow June 11, 2026 15:37
@changeset-bot

changeset-bot Bot commented Jun 11, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: d46e35c

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 0 packages

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/ui/src/mosaic/MosaicProvider.tsx`:
- Around line 47-56: The useInsertionEffect in MosaicProvider currently runs on
every render because it has no dependency array; update it to only re-run when
its inputs change (e.g., include cssLayerName and cache.key in the dependency
array) so you don't repeatedly re-scan and wrap all
style[data-emotion^="${cache.key}"] tags each render, while retaining the
idempotent `@layer` check; if you intentionally need per-render execution, add a
concise comment in MosaicProvider explaining why it must run every render
despite the cost and referencing useInsertionEffect, cssLayerName, and
cache.key.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Repository UI (inherited)

Review profile: CHILL

Plan: Pro

Run ID: 5b0b5aad-c7ff-4050-bfd8-1e8f526decfb

📥 Commits

Reviewing files that changed from the base of the PR and between a422767 and 400c9c2.

📒 Files selected for processing (3)
  • packages/swingset/src/components/StoryPreview.tsx
  • packages/ui/src/mosaic/MosaicProvider.tsx
  • packages/ui/src/mosaic/__tests__/MosaicProvider.test.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/ui/src/mosaic/tests/MosaicProvider.test.tsx

Comment thread packages/ui/src/mosaic/MosaicProvider.tsx Outdated
Replaces the emotionCache.insert monkey-patch with a useInsertionEffect
that rewrites <style> textContent after emotion injects but before paint,
avoiding the need to intercept the internal insert API.
Slot identity now lives in the mosaic styled layer. The dialog primitives
keep their runtime state attrs (data-cl-open, data-cl-starting-style, …)
but no longer emit data-cl-slot; tests locate parts via role/text/testid.
Replace the cva/styled Dialog with a multi-slot dialogRecipe that owns slot
identity and base styles. Each part reads its slot via useRecipe and spreads
it onto the bridged headless primitive through the new withMosaicSlot bridge.
Removes the obsolete styled/types/withMosaicTheme cva helpers.
The insertion effect rewrote emotion's <style> textContent to wrap rules in
@layer, which desynced from emotion's rule bookkeeping and dropped component
styles on client navigation. Reverting to normal emotion insertion; the
@layer/cssLayerName ordering will be revisited separately.
Adds a styled mosaic Dialog story (Components) that uses a Button as the
trigger via the render prop. Routes are now group-aware (/components/<c>
and /primitives/<c>) so the styled Dialog and the headless Dialog primitive
no longer collide on a title-only slug.
Comment on lines -38 to -48
if (cssLayerName) {
const prevInsert = emotionCache.insert.bind(emotionCache);
emotionCache.insert = (selector: string, serialized: SerializedStyles, sheet: any, shouldCache: boolean) => {
if (serialized && typeof serialized.styles === 'string') {
const newSerialized = { ...serialized };
newSerialized.styles = `@layer ${cssLayerName} {${serialized.styles}}`;
return prevInsert(selector, newSerialized, sheet, shouldCache);
}
return prevInsert(selector, serialized, sheet, shouldCache);
};
}

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed for now, need to figure out a more reliable solution for this feature that supports SSR.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/swingset/src/stories/dialog.component.stories.tsx`:
- Around line 14-19: Change the story function Default to accept a single
parameter of type Record<string, unknown> (e.g., Default(args: Record<string,
unknown>)) and cast that generic args object to the concrete prop types used in
the story before passing them through; locate the Default function and update
how props are forwarded into Dialog.Trigger's render and the Button (cast args
to the proper HTMLAttributes/prop types) so the story follows the shared
knobs-to-props contract.

In `@packages/ui/src/mosaic/MosaicProvider.tsx`:
- Around line 25-33: The provider removed the standalone variables prop and now
only reads appearance?.variables, which breaks backward compatibility; update
MosaicProviderProps to reintroduce variables (e.g., variables?:
Partial<MosaicVariables>) and change the memo that builds theme to merge/precede
props.variables with appearance?.variables (e.g.,
resolveVariables(defaultMosaicVariables, variables ?? appearance?.variables) or
otherwise merge so existing consumers using the variables prop keep their
overrides), leaving parseMosaicAppearance(scope, appearance) unchanged;
reference MosaicProvider, MosaicProviderProps, appearance, variables,
resolveVariables, parseMosaicAppearance, and defaultMosaicVariables when making
the change.

In `@references/mosaic-architecture.md`:
- Line 263: Update the sentence at line 263 to distinguish between two cases:
(1) when a variant map entry is null — explain that pickSlot converts falsy
entries (null → undefined) and mergeInto only merges truthy sources so those
variant-map entries result in no styles; and (2) when a variant prop is
explicitly passed as null — explain that it becomes the string key "null" for
lookup so styles only apply if the recipe defines a "null" variant. Reference
the existing terms pickSlot, mergeInto, variant map, variant prop, and recipe in
the revised wording to make the distinction clear.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Repository UI (inherited)

Review profile: CHILL

Plan: Pro

Run ID: a7499a18-dc14-484f-9dfc-07d4ac22bb15

📥 Commits

Reviewing files that changed from the base of the PR and between 400c9c2 and 157fec5.

📒 Files selected for processing (44)
  • .changeset/mosaic-slot-recipes.md
  • packages/headless/src/primitives/dialog/README.md
  • packages/headless/src/primitives/dialog/dialog-backdrop.tsx
  • packages/headless/src/primitives/dialog/dialog-close.tsx
  • packages/headless/src/primitives/dialog/dialog-description.tsx
  • packages/headless/src/primitives/dialog/dialog-popup.tsx
  • packages/headless/src/primitives/dialog/dialog-title.tsx
  • packages/headless/src/primitives/dialog/dialog-trigger.tsx
  • packages/headless/src/primitives/dialog/dialog-viewport.tsx
  • packages/headless/src/primitives/dialog/dialog.test.tsx
  • packages/swingset/src/app/[group]/[component]/page.tsx
  • packages/swingset/src/app/components/[component]/page.tsx
  • packages/swingset/src/components/ClientRoot.tsx
  • packages/swingset/src/components/DocsViewer.tsx
  • packages/swingset/src/components/StoryPreview.tsx
  • packages/swingset/src/components/app-sidebar.tsx
  • packages/swingset/src/components/ui/sidebar.tsx
  • packages/swingset/src/lib/registry.ts
  • packages/swingset/src/stories/button.stories.tsx
  • packages/swingset/src/stories/dialog.component.mdx
  • packages/swingset/src/stories/dialog.component.stories.tsx
  • packages/swingset/src/stories/input.stories.tsx
  • packages/ui/src/mosaic/MosaicProvider.tsx
  • packages/ui/src/mosaic/__tests__/MosaicProvider.test.tsx
  • packages/ui/src/mosaic/__tests__/conditions.test.ts
  • packages/ui/src/mosaic/__tests__/cva.test.ts
  • packages/ui/src/mosaic/__tests__/resolveSlot.test.ts
  • packages/ui/src/mosaic/__tests__/slot-recipe.test.ts
  • packages/ui/src/mosaic/__tests__/utils.test.ts
  • packages/ui/src/mosaic/appearance.ts
  • packages/ui/src/mosaic/components/__tests__/button.test.tsx
  • packages/ui/src/mosaic/components/button.tsx
  • packages/ui/src/mosaic/components/dialog.tsx
  • packages/ui/src/mosaic/components/input.tsx
  • packages/ui/src/mosaic/conditions.ts
  • packages/ui/src/mosaic/cva.ts
  • packages/ui/src/mosaic/primitives/dialog.tsx
  • packages/ui/src/mosaic/primitives/withMosaicSlot.tsx
  • packages/ui/src/mosaic/registry.ts
  • packages/ui/src/mosaic/resolveSlot.ts
  • packages/ui/src/mosaic/slot-recipe.ts
  • packages/ui/src/mosaic/useSlot.ts
  • packages/ui/src/mosaic/utils.ts
  • references/mosaic-architecture.md
💤 Files with no reviewable changes (4)
  • packages/swingset/src/app/components/[component]/page.tsx
  • packages/ui/src/mosaic/cva.ts
  • packages/ui/src/mosaic/tests/cva.test.ts
  • packages/ui/src/mosaic/utils.ts
✅ Files skipped from review due to trivial changes (2)
  • packages/swingset/src/stories/dialog.component.mdx
  • .changeset/mosaic-slot-recipes.md
🚧 Files skipped from review as they are similar to previous changes (15)
  • packages/ui/src/mosaic/tests/conditions.test.ts
  • packages/ui/src/mosaic/resolveSlot.ts
  • packages/swingset/src/stories/button.stories.tsx
  • packages/ui/src/mosaic/tests/utils.test.ts
  • packages/ui/src/mosaic/appearance.ts
  • packages/swingset/src/components/StoryPreview.tsx
  • packages/ui/src/mosaic/components/tests/button.test.tsx
  • packages/ui/src/mosaic/registry.ts
  • packages/ui/src/mosaic/tests/MosaicProvider.test.tsx
  • packages/ui/src/mosaic/components/input.tsx
  • packages/ui/src/mosaic/useSlot.ts
  • packages/ui/src/mosaic/tests/slot-recipe.test.ts
  • packages/ui/src/mosaic/components/button.tsx
  • packages/ui/src/mosaic/conditions.ts
  • packages/ui/src/mosaic/slot-recipe.ts

Comment thread packages/swingset/src/stories/dialog.component.stories.tsx
Comment thread packages/ui/src/mosaic/MosaicProvider.tsx
Comment thread references/mosaic-architecture.md Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
packages/ui/src/mosaic/__tests__/MosaicProvider.test.tsx (2)

44-46: 💤 Low value

Consider using JSX for the wrapper instead of React.createElement.

The React.createElement calls work correctly but are verbose. JSX would be more idiomatic and readable in test wrappers:

wrapper: ({ children }) => <MosaicProvider appearance={appearance} scope="signIn">{children}</MosaicProvider>

This pattern applies to lines 60 and 67 as well.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/ui/src/mosaic/__tests__/MosaicProvider.test.tsx` around lines 44 -
46, Replace the verbose React.createElement usage in the test hook wrappers with
JSX to improve readability: update the wrapper function passed to renderHook
that currently uses React.createElement(MosaicProvider, { appearance, scope:
'signIn' }, children) (and the other two similar instances) to return JSX for
MosaicProvider with props appearance and scope and the children inside; target
the useMosaicAppearance test wrappers and any other wrapper lambdas referencing
MosaicProvider to make this change.

56-71: ⚡ Quick win

Add a test for the useMosaicTheme error case.

According to the implementation in MosaicProvider.tsx, useMosaicTheme throws an error when used outside a MosaicProvider. This error handling is part of the public contract and should be verified:

🧪 Suggested test case
it('throws when useMosaicTheme is used outside MosaicProvider', () => {
  expect(() => {
    renderHook(() => useMosaicTheme());
  }).toThrow('useMosaicTheme must be used within a MosaicProvider');
});
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/ui/src/mosaic/__tests__/MosaicProvider.test.tsx` around lines 56 -
71, Add a test to MosaicProvider.test.tsx that verifies useMosaicTheme throws
when called outside a MosaicProvider: create an it() case that calls
renderHook(() => useMosaicTheme()) with no wrapper and assert it throws with the
exact message 'useMosaicTheme must be used within a MosaicProvider' (use
expect(() => renderHook(...)).toThrow(...)); reference the useMosaicTheme hook
and MosaicProvider to locate where to add the test.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@packages/ui/src/mosaic/__tests__/MosaicProvider.test.tsx`:
- Around line 44-46: Replace the verbose React.createElement usage in the test
hook wrappers with JSX to improve readability: update the wrapper function
passed to renderHook that currently uses React.createElement(MosaicProvider, {
appearance, scope: 'signIn' }, children) (and the other two similar instances)
to return JSX for MosaicProvider with props appearance and scope and the
children inside; target the useMosaicAppearance test wrappers and any other
wrapper lambdas referencing MosaicProvider to make this change.
- Around line 56-71: Add a test to MosaicProvider.test.tsx that verifies
useMosaicTheme throws when called outside a MosaicProvider: create an it() case
that calls renderHook(() => useMosaicTheme()) with no wrapper and assert it
throws with the exact message 'useMosaicTheme must be used within a
MosaicProvider' (use expect(() => renderHook(...)).toThrow(...)); reference the
useMosaicTheme hook and MosaicProvider to locate where to add the test.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Repository UI (inherited)

Review profile: CHILL

Plan: Pro

Run ID: b903ac2f-a067-4bfd-b12b-aba9e19123b1

📥 Commits

Reviewing files that changed from the base of the PR and between 157fec5 and fb83adb.

📒 Files selected for processing (1)
  • packages/ui/src/mosaic/__tests__/MosaicProvider.test.tsx

return (
<Dialog.Root>
<Dialog.Trigger
render={(props: Omit<HTMLAttributes<HTMLElement>, 'color'>) => <Button {...props}>Open dialog</Button>}

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we need to be strict about prop variants names not being dom attributes to avoid this dance. noted to tackle in future work.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant