Skip to content

feat(ui): add Mosaic Popover component - #9241

Open
alexcarpenter wants to merge 2 commits into
mainfrom
carp/mosaic-popover
Open

feat(ui): add Mosaic Popover component#9241
alexcarpenter wants to merge 2 commits into
mainfrom
carp/mosaic-popover

Conversation

@alexcarpenter

@alexcarpenter alexcarpenter commented Jul 24, 2026

Copy link
Copy Markdown
Member

Description

Adds a reusable Mosaic Popover component to @clerk/ui, styled with StyleX on top of the @clerk/headless popover primitive.

Preview: https://swingset-git-carp-mosaic-popover.clerkstage.dev/components/popover

The popup is a flexible floating card: consumers drop any inner content into Popover.Content plus an optional Popover.Footer. The convenience Popover wraps trigger + portalled, positioned popup; the compound parts (Popover.Root, Popover.Positioner, Popover.Popup, …) are exposed for custom layouts.

Styling notes

  • Themed with StyleX (not slot recipes). Each styled part carries a stable .cl-<slot> class; consumers override from a later CSS layer.
  • Enter/exit transition (opacity + scale) is driven off the popup's own data-starting-style / data-ending-style attributes via StyleX :where([data-*]) conditional keys, and disabled under prefers-reduced-motion: reduce.
  • Only styles.css ships from the isolated mosaic build; the mosaic JS is not publicly importable yet, so the changeset is empty.

Docs

  • Swingset docs added (popover.component.stories.tsx + .mdx, archetype A · compound) and wired into the registry + docModules.

How to test

  • pnpm --filter @clerk/ui test src/mosaic/components/popover (5 tests)
  • pnpm --filter @clerk/ui build:mosaic — verifies the transition + reduced-motion CSS lands in styles.css
  • pnpm run dev:swingset → Components → Popover

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

  • 🌟 New feature

@vercel

vercel Bot commented Jul 24, 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 Jul 29, 2026 12:40am
swingset Ready Ready Preview, Comment Jul 29, 2026 12:40am

Request Review

@changeset-bot

changeset-bot Bot commented Jul 24, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: fccd08a

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

@pkg-pr-new

pkg-pr-new Bot commented Jul 24, 2026

Copy link
Copy Markdown

Open in StackBlitz

@clerk/astro

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

@clerk/backend

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

@clerk/chrome-extension

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

@clerk/clerk-js

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

@clerk/electron

npm i https://pkg.pr.new/@clerk/electron@9241

@clerk/electron-passkeys

npm i https://pkg.pr.new/@clerk/electron-passkeys@9241

@clerk/eslint-plugin

npm i https://pkg.pr.new/@clerk/eslint-plugin@9241

@clerk/expo

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

@clerk/expo-google-signin

npm i https://pkg.pr.new/@clerk/expo-google-signin@9241

@clerk/expo-passkeys

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

@clerk/express

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

@clerk/fastify

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

@clerk/hono

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

@clerk/localizations

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

@clerk/nextjs

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

@clerk/nuxt

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

@clerk/react

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

@clerk/react-router

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

@clerk/shared

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

@clerk/tanstack-react-start

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

@clerk/testing

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

@clerk/ui

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

@clerk/upgrade

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

@clerk/vue

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

commit: fccd08a

@github-actions

github-actions Bot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

API Changes Report

Generated by Break Check on 2026-07-29T00:41:33.976Z

Summary

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

No API Changes Detected

All packages have stable APIs with no detected changes.


Report generated by Break Check

Last ran on fccd08a.

@coderabbitai

coderabbitai Bot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

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

Review profile: CHILL

Plan: Pro Plus

Run ID: 27506258-19c7-43e8-b2da-ee73147005ec

📥 Commits

Reviewing files that changed from the base of the PR and between 27e9be7 and fccd08a.

📒 Files selected for processing (6)
  • .changeset/mosaic-popover.md
  • .claude/skills/mosaic/references/stylex.md
  • packages/swingset/src/components/DocsViewer.tsx
  • packages/swingset/src/lib/registry.ts
  • packages/swingset/src/stories/popover.component.mdx
  • packages/swingset/src/stories/popover.component.stories.tsx
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • clerk/clerk_go (manual)
  • clerk/dashboard (manual)
  • clerk/accounts (manual)
  • clerk/backoffice (manual)
  • clerk/clerk (manual)
  • clerk/clerk-docs (manual)
  • clerk/cloudflare-workers (manual)
🚧 Files skipped from review as they are similar to previous changes (5)
  • packages/swingset/src/components/DocsViewer.tsx
  • .changeset/mosaic-popover.md
  • packages/swingset/src/stories/popover.component.stories.tsx
  • packages/swingset/src/lib/registry.ts
  • packages/swingset/src/stories/popover.component.mdx

📝 Walkthrough

Walkthrough

Adds a StyleX-based Mosaic Popover built on headless primitives, with compound parts, forwarded refs, configurable state, placement, and styling. Adds behavioral tests and public exports. Adds Storybook and MDX documentation, registers the component in Swingset, and maps its documentation route. Extends StyleX guidance for styling data-* state attributes.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Possibly related PRs

  • clerk/javascript#9230: Adds another Mosaic component through the same Swingset documentation and registry integration points.
  • clerk/javascript#9252: Extends the same Swingset wiring and Mosaic StyleX exports for a different component.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main change: adding the Mosaic Popover component.
Description check ✅ Passed The description is clearly related to the changeset and accurately describes the new Popover component, docs, styling, and tests.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
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.

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.

@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/popover.component.mdx`:
- Around line 5-15: Update the introductory paragraph in the Popover MDX to
state that the composed Mosaic layer owns open/placement state and wires
together the `@clerk/headless` popover primitive with StyleX theming. Add a
composition array to the sole Story component naming each direct dependency.
- Around line 41-42: Update the controlled popover example in the story to
import useState from React, and add a composition array to its Story declaration
listing the direct dependencies required by the composed layer.

In `@packages/ui/src/mosaic/components/popover/popover.styles.ts`:
- Around line 47-58: Update the popup style container around the visible
maxWidth/minWidth declarations to add a viewport-relative maxHeight, ensuring
the overall popup remains within the viewport so the content overflow can scroll
without displacing the footer. Adjust the minWidth constraint to remain
compatible with maxWidth on narrow viewports, using a responsive width
constraint rather than forcing 18rem below the available viewport width.
🪄 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), Organization UI (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: c5cd8a53-2b61-4f94-a5c6-7a936df2a45f

📥 Commits

Reviewing files that changed from the base of the PR and between 01f2c12 and 23353f2.

📒 Files selected for processing (11)
  • .changeset/mosaic-popover.md
  • packages/swingset/src/components/DocsViewer.tsx
  • packages/swingset/src/lib/registry.ts
  • packages/swingset/src/stories/popover.component.mdx
  • packages/swingset/src/stories/popover.component.stories.tsx
  • packages/ui/src/mosaic/components/popover/index.ts
  • packages/ui/src/mosaic/components/popover/popover.styles.ts
  • packages/ui/src/mosaic/components/popover/popover.test.tsx
  • packages/ui/src/mosaic/components/popover/popover.tsx
  • packages/ui/src/mosaic/styles/index.ts
  • packages/ui/tsdown.mosaic.config.mts
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • clerk/clerk_go (manual)
  • clerk/dashboard (manual)
  • clerk/accounts (manual)
  • clerk/backoffice (manual)
  • clerk/clerk (manual)
  • clerk/clerk-docs (manual)
  • clerk/cloudflare-workers (manual)
  • clerk/cli (auto-detected)
  • clerk/clerk-ios (auto-detected)
  • clerk/clerk-android (auto-detected)

Comment on lines +5 to +15
The Mosaic `Popover` — the styled Mosaic component composed from the `@clerk/headless` popover
primitive and themed with StyleX. It is a reusable floating card anchored to a trigger: drop any
inner content into `Popover.Content` and an optional `Popover.Footer`. It inherits the primitive's
positioning, focus management, and ARIA wiring.

## Example

<Story
name='Default'
storyModule={PopoverStories}
/>

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.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Add the composed-layer documentation metadata.

Document which layer owns open/placement state and which lower-layer pieces are wired together, then pass a composition array to this sole <Story>.

As per coding guidelines, “For Archetype C (composed layer) MDX files, lead with a single paragraph on what state is owned and which lower-layer pieces are wired together, then render a single <Story> with a composition array naming each direct dependency.”

🤖 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/swingset/src/stories/popover.component.mdx` around lines 5 - 15,
Update the introductory paragraph in the Popover MDX to state that the composed
Mosaic layer owns open/placement state and wires together the `@clerk/headless`
popover primitive with StyleX theming. Add a composition array to the sole Story
component naming each direct dependency.

Source: Coding guidelines

Comment on lines +41 to +42
```tsx
const [open, setOpen] = useState(false);

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.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

file="packages/swingset/src/stories/popover.component.mdx"

echo "== File outline =="
wc -l "$file"
echo
nl -ba "$file" | sed -n '1,220p'

echo
echo "== Search for composition usage in similar stories =="
rg -n "composition\s*:" packages/swingset/src/stories -g '*.mdx' || true

echo
echo "== Search for useState import in this file =="
rg -n "useState|from 'react'|from \"react\"" "$file" || true

Repository: clerk/javascript

Length of output: 270


🏁 Script executed:

#!/bin/bash
set -euo pipefail

file="packages/swingset/src/stories/popover.component.mdx"

echo "== File contents =="
cat -n "$file"

echo
echo "== MDX stories with composition arrays =="
rg -n "composition\s*:" packages/swingset/src/stories -g '*.mdx' || true

echo
echo "== Popover references elsewhere =="
rg -n "popover" packages/swingset/src/stories -g '*.mdx' -g '*.ts' -g '*.tsx' || true

Repository: clerk/javascript

Length of output: 9929


🏁 Script executed:

#!/bin/bash
set -euo pipefail

file="packages/swingset/src/stories/popover.component.mdx"

echo "== File contents =="
cat -n "$file"

Repository: clerk/javascript

Length of output: 5749


🏁 Script executed:

#!/bin/bash
set -euo pipefail

sed -n '1,140p' packages/swingset/src/stories/popover.component.mdx

Repository: clerk/javascript

Length of output: 4924


Import useState and add the composition metadata

  • The controlled example uses useState without importing it, so the snippet won’t compile as written.
  • This composed-layer story also needs a composition array on <Story> to document the direct dependencies.
🤖 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/swingset/src/stories/popover.component.mdx` around lines 41 - 42,
Update the controlled popover example in the story to import useState from
React, and add a composition array to its Story declaration listing the direct
dependencies required by the composed layer.

Comment on lines +47 to +58
maxWidth: 'calc(100vw - 2rem)',
minWidth: '18rem',
},

// Flexible inner content region. Scrolls on overflow so tall content never
// pushes the footer out of view.
content: {
padding: space['4'],
display: 'flex',
flexDirection: 'column',
minHeight: 0,
overflowY: 'auto',

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.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the target file and surrounding popover styles.
FILE="packages/ui/src/mosaic/components/popover/popover.styles.ts"
echo "== file outline =="
ast-grep outline "$FILE" --view expanded || true

echo
echo "== relevant lines =="
nl -ba "$FILE" | sed -n '1,220p'

echo
echo "== search for related popover sizing/positioning styles =="
rg -n "maxHeight|maxWidth|minWidth|overflowY|popover|content:" packages/ui/src/mosaic/components/popover -S || true

Repository: clerk/javascript

Length of output: 337


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE="packages/ui/src/mosaic/components/popover/popover.styles.ts"

echo "== file with line numbers =="
awk '{printf "%4d: %s\n", NR, $0}' "$FILE" | sed -n '1,220p'

echo
echo "== surrounding popover files =="
fd -t f . packages/ui/src/mosaic/components/popover

echo
echo "== related references =="
rg -n "maxHeight|maxWidth|minWidth|overflowY|overflow|popover" packages/ui/src/mosaic/components/popover -S || true

Repository: clerk/javascript

Length of output: 6065


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE="packages/ui/src/mosaic/components/popover/popover.tsx"

echo "== file with line numbers =="
awk '{printf "%4d: %s\n", NR, $0}' "$FILE" | sed -n '1,220p'

echo
echo "== popover tests =="
awk '{printf "%4d: %s\n", NR, $0}' "packages/ui/src/mosaic/components/popover/popover.test.tsx" | sed -n '1,220p'

Repository: clerk/javascript

Length of output: 8904


Constrain the popup to the viewport.
overflowY: 'auto' on the content won’t help unless the popup itself has a height cap, so tall content can still push the footer off-screen. On narrow screens, minWidth: '18rem' also overrides maxWidth: 'calc(100vw - 2rem)' once the viewport drops below 20rem.

Proposed fix
     transitionTimingFunction: 'ease-out',
+    maxHeight: 'calc(100dvh - 2rem)',
     maxWidth: 'calc(100vw - 2rem)',
-    minWidth: '18rem',
+    minWidth: 'min(18rem, calc(100vw - 2rem))',
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
maxWidth: 'calc(100vw - 2rem)',
minWidth: '18rem',
},
// Flexible inner content region. Scrolls on overflow so tall content never
// pushes the footer out of view.
content: {
padding: space['4'],
display: 'flex',
flexDirection: 'column',
minHeight: 0,
overflowY: 'auto',
transitionTimingFunction: 'ease-out',
maxHeight: 'calc(100dvh - 2rem)',
maxWidth: 'calc(100vw - 2rem)',
minWidth: 'min(18rem, calc(100vw - 2rem))',
},
// Flexible inner content region. Scrolls on overflow so tall content never
// pushes the footer out of view.
content: {
padding: space['4'],
display: 'flex',
flexDirection: 'column',
minHeight: 0,
overflowY: 'auto',
🤖 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/components/popover/popover.styles.ts` around lines 47
- 58, Update the popup style container around the visible maxWidth/minWidth
declarations to add a viewport-relative maxHeight, ensuring the overall popup
remains within the viewport so the content overflow can scroll without
displacing the footer. Adjust the minWidth constraint to remain compatible with
maxWidth on narrow viewports, using a responsive width constraint rather than
forcing 18rem below the available viewport width.

Reusable StyleX-styled popover built on the @clerk/headless popover
primitive: a flexible popup card with content + footer slots, enter/exit
transition driven off self data-attributes, and swingset docs.
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