fix(ui): render the Avatar fallback as a blank placeholder - #9398

Merged
alexcarpenter merged 4 commits into
mainfrom
carp/avatar-skeleton-fallback
Aug 11, 2026
Merged

fix(ui): render the Avatar fallback as a blank placeholder#9398
alexcarpenter merged 4 commits into
mainfrom
carp/avatar-skeleton-fallback

Conversation

@alexcarpenter

@alexcarpenteralexcarpenter commented Aug 11, 2026

Copy link
Copy Markdown
Member

Description

Avatar.Fallback keeps its API — it still takes children, initials or an icon — but it no longer paints them. The children move into a new avatar-fallback-content slot held at visibility: hidden, so what's left is the neutral fill on its own. visibility is the one property that takes the content out of the page, the accessibility tree, and the tab order together, and unlike inert it leaves hit-testing alone, so a click still reaches the button row these sit in. A consumer who wants the initials back overrides that one slot.

The fallback also pulses now, but only while an image is genuinely on its way (status === 'loading', reflected as data-pending). Someone who simply has no picture is not waiting on anything, so their mark holds still rather than pulsing for as long as it's on screen. The pulse matches skeleton.tsx's timing and drops to none under prefers-reduced-motion.

user-button.view.tsx is comments only — it still computes and passes initials(name), so the name is there for anyone who styles the slot back into view.

Swingset gets a Pending example. /api/pending-image holds its response for a minute with cache-control: no-store, so the pulse is the component's own status rather than a pinned attribute.

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:

Avatar.Fallback still takes children, but it no longer paints them: an
entity without a picture reads as an absent image rather than as two
letters. The children move into an `avatar-fallback-content` slot held at
`visibility: hidden`, which takes them out of the page, the accessibility
tree, and the tab order at once while leaving hit-testing alone, so a
consumer can style them back into view.
The fallback pulses only while an image is genuinely resolving
(`data-pending`); someone who has no picture is not waiting on anything,
so their mark holds still.
@changeset-bot

changeset-botBot commented Aug 11, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: d013999

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

@vercel

vercelBot commented Aug 11, 2026

Copy link
Copy Markdown

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

ProjectDeploymentActionsUpdated (UTC)
clerk-js-sandboxReadyReadyPreviewAug 11, 2026 6:11pm
swingsetReadyReadyPreviewAug 11, 2026 6:11pm

Request Review

@pkg-pr-new

pkg-pr-newBot commented Aug 11, 2026

Copy link
Copy Markdown

Open in StackBlitz

@clerk/astro

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

@clerk/backend

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

@clerk/chrome-extension

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

@clerk/clerk-js

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

@clerk/electron

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

@clerk/electron-passkeys

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

@clerk/eslint-plugin

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

@clerk/expo

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

@clerk/expo-google-signin

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

@clerk/expo-passkeys

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

@clerk/express

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

@clerk/fastify

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

@clerk/hono

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

@clerk/localizations

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

@clerk/nextjs

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

@clerk/nuxt

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

@clerk/react

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

@clerk/react-router

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

@clerk/shared

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

@clerk/tanstack-react-start

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

@clerk/testing

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

@clerk/ui

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

@clerk/upgrade

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

@clerk/vue

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

commit: d013999

@github-actions

github-actionsBot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

API Changes Report

Generated by Break Check on 2026-08-11T18:13:20.778Z

Summary

MetricCount
Packages analyzed19
Packages with changes0
🔴 Breaking changes0
🟡 Non-breaking changes0
🟢 Additions0

No API Changes Detected

All packages have stable APIs with no detected changes.


Report generated by Break Check

Last ran on d013999.

@coderabbitai

coderabbitaiBot commented Aug 11, 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: 528247a8-b031-4843-a13b-d341dea903a3

📥 Commits

Reviewing files that changed from the base of the PR and between 207eecd and d013999.

📒 Files selected for processing (6)
  • packages/swingset/src/app/api/pending-image/route.ts
  • packages/swingset/src/stories/avatar.stories.tsx
  • packages/ui/src/mosaic/components/avatar/avatar.styles.ts
  • packages/ui/src/mosaic/components/avatar/avatar.test.tsx
  • packages/ui/src/mosaic/user-button/__tests__/user-button.view.test.tsx
  • packages/ui/src/mosaic/user-button/user-button.view.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)
  • clerk/cli(auto-detected)
  • clerk/clerk-ios(auto-detected)
  • clerk/clerk-android(auto-detected)
💤 Files with no reviewable changes (1)
  • packages/swingset/src/stories/avatar.stories.tsx
🚧 Files skipped from review as they are similar to previous changes (5)
  • packages/ui/src/mosaic/user-button/tests/user-button.view.test.tsx
  • packages/ui/src/mosaic/components/avatar/avatar.test.tsx
  • packages/ui/src/mosaic/components/avatar/avatar.styles.ts
  • packages/ui/src/mosaic/user-button/user-button.view.tsx
  • packages/swingset/src/app/api/pending-image/route.ts

📝 Walkthrough

Walkthrough

Avatar fallbacks now hide fallback content by default and wrap it in a dedicated content slot. The fallback exposes pending state while an image request remains unresolved, applies a pulse animation, and stops pulsing after an error. Tests cover pending, error, and no-source states. A pending-image endpoint and story demonstrate the behavior. Avatar documentation and user-button comments were updated. An empty Changeset file was added.

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

Suggested reviewers:maxyinger

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 28.57% 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 nameStatusExplanation
Title check✅ PassedThe title clearly summarizes the main Avatar fallback behavior change.
Description check✅ PassedThe description accurately explains the hidden fallback content, pending pulse behavior, tests, and example changes.
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.

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

@coderabbitaicoderabbitaiBot 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 @.changeset/mosaic-avatar-skeleton-fallback.md:
- Around line 1-2: Add an `@clerk/ui` changeset entry in
mosaic-avatar-skeleton-fallback.md with the appropriate release bump and a
concise release note describing the updated default Avatar.Fallback output; do
not leave the changeset empty.
In `@packages/swingset/src/app/api/pending-image/route.ts`:
- Around line 17-29: Add unit tests covering the GET route’s delayed response
contract: use fake timers to verify normal resolution after HOLD_MS and
request-signal cancellation before the delay completes, and assert both PNG
content type and no-store cache-control headers for the returned Response.
In `@packages/ui/src/mosaic/components/avatar/avatar.styles.ts`:
- Around line 5-7: Reduce comments across the listed sites to only terse
non-obvious rationale: in avatar.styles.ts lines 5-7 and 53-58, replace each
explanation with one concise rationale line, and remove lines 63-64; remove
narration at avatar.test.tsx lines 16-18, 228-230, and 261-262; shorten the
accessibility and avatarName comments in user-button.view.tsx lines 135-136 and
186-188, remove lines 157-158 and 683, and remove narration in
user-button.view.test.tsx lines 653-654; shorten the route-purpose and cache
comments in pending-image/route.ts lines 1-4 and 13-14, remove line 7; and
remove the story narration in avatar.stories.tsx lines 49-50. Preserve code
behavior and leave comments only where they explain non-obvious rationale.
🪄 Autofix

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: ea7ed4b3-ea6a-424f-80b8-5477ae8450cd

📥 Commits

Reviewing files that changed from the base of the PR and between 3132d40 and 07a07ba.

📒 Files selected for processing (9)
  • .changeset/mosaic-avatar-skeleton-fallback.md
  • packages/swingset/src/app/api/pending-image/route.ts
  • packages/swingset/src/stories/avatar.mdx
  • packages/swingset/src/stories/avatar.stories.tsx
  • packages/ui/src/mosaic/components/avatar/avatar.styles.ts
  • packages/ui/src/mosaic/components/avatar/avatar.test.tsx
  • packages/ui/src/mosaic/components/avatar/avatar.tsx
  • packages/ui/src/mosaic/user-button/__tests__/user-button.view.test.tsx
  • packages/ui/src/mosaic/user-button/user-button.view.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)
  • clerk/cli(auto-detected)
  • clerk/clerk-ios(auto-detected)
  • clerk/clerk-android(auto-detected)

Comment thread.changeset/mosaic-avatar-skeleton-fallback.md
Comment threadpackages/swingset/src/app/api/pending-image/route.ts
Comment threadpackages/ui/src/mosaic/components/avatar/avatar.styles.ts Outdated
@alexcarpenter
alexcarpenter merged commit a9099bc into mainAug 11, 2026
52 checks passed
@alexcarpenter
alexcarpenter deleted the carp/avatar-skeleton-fallback branch August 11, 2026 20:32
Sign up for freeto 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.

3 participants

@alexcarpenter@maxyinger@austincalvelage
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

fix(ui): render the Avatar fallback as a blank placeholder - #9398

Merged
alexcarpenter merged 4 commits into
mainfrom
carp/avatar-skeleton-fallback
Aug 11, 2026
Merged

fix(ui): render the Avatar fallback as a blank placeholder#9398
alexcarpenter merged 4 commits into
mainfrom
carp/avatar-skeleton-fallback

Conversation

@alexcarpenter

@alexcarpenteralexcarpenter commented Aug 11, 2026

Copy link
Copy Markdown
Member

Description

Avatar.Fallback keeps its API — it still takes children, initials or an icon — but it no longer paints them. The children move into a new avatar-fallback-content slot held at visibility: hidden, so what's left is the neutral fill on its own. visibility is the one property that takes the content out of the page, the accessibility tree, and the tab order together, and unlike inert it leaves hit-testing alone, so a click still reaches the button row these sit in. A consumer who wants the initials back overrides that one slot.

The fallback also pulses now, but only while an image is genuinely on its way (status === 'loading', reflected as data-pending). Someone who simply has no picture is not waiting on anything, so their mark holds still rather than pulsing for as long as it's on screen. The pulse matches skeleton.tsx's timing and drops to none under prefers-reduced-motion.

user-button.view.tsx is comments only — it still computes and passes initials(name), so the name is there for anyone who styles the slot back into view.

Swingset gets a Pending example. /api/pending-image holds its response for a minute with cache-control: no-store, so the pulse is the component's own status rather than a pinned attribute.

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:

Avatar.Fallback still takes children, but it no longer paints them: an
entity without a picture reads as an absent image rather than as two
letters. The children move into an `avatar-fallback-content` slot held at
`visibility: hidden`, which takes them out of the page, the accessibility
tree, and the tab order at once while leaving hit-testing alone, so a
consumer can style them back into view.
The fallback pulses only while an image is genuinely resolving
(`data-pending`); someone who has no picture is not waiting on anything,
so their mark holds still.
@changeset-bot

changeset-botBot commented Aug 11, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: d013999

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

@vercel

vercelBot commented Aug 11, 2026

Copy link
Copy Markdown

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

ProjectDeploymentActionsUpdated (UTC)
clerk-js-sandboxReadyReadyPreviewAug 11, 2026 6:11pm
swingsetReadyReadyPreviewAug 11, 2026 6:11pm

Request Review

@pkg-pr-new

pkg-pr-newBot commented Aug 11, 2026

Copy link
Copy Markdown

Open in StackBlitz

@clerk/astro

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

@clerk/backend

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

@clerk/chrome-extension

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

@clerk/clerk-js

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

@clerk/electron

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

@clerk/electron-passkeys

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

@clerk/eslint-plugin

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

@clerk/expo

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

@clerk/expo-google-signin

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

@clerk/expo-passkeys

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

@clerk/express

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

@clerk/fastify

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

@clerk/hono

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

@clerk/localizations

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

@clerk/nextjs

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

@clerk/nuxt

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

@clerk/react

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

@clerk/react-router

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

@clerk/shared

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

@clerk/tanstack-react-start

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

@clerk/testing

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

@clerk/ui

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

@clerk/upgrade

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

@clerk/vue

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

commit: d013999

@github-actions

github-actionsBot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

API Changes Report

Generated by Break Check on 2026-08-11T18:13:20.778Z

Summary

MetricCount
Packages analyzed19
Packages with changes0
🔴 Breaking changes0
🟡 Non-breaking changes0
🟢 Additions0

No API Changes Detected

All packages have stable APIs with no detected changes.


Report generated by Break Check

Last ran on d013999.

@coderabbitai

coderabbitaiBot commented Aug 11, 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: 528247a8-b031-4843-a13b-d341dea903a3

📥 Commits

Reviewing files that changed from the base of the PR and between 207eecd and d013999.

📒 Files selected for processing (6)
  • packages/swingset/src/app/api/pending-image/route.ts
  • packages/swingset/src/stories/avatar.stories.tsx
  • packages/ui/src/mosaic/components/avatar/avatar.styles.ts
  • packages/ui/src/mosaic/components/avatar/avatar.test.tsx
  • packages/ui/src/mosaic/user-button/__tests__/user-button.view.test.tsx
  • packages/ui/src/mosaic/user-button/user-button.view.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)
  • clerk/cli(auto-detected)
  • clerk/clerk-ios(auto-detected)
  • clerk/clerk-android(auto-detected)
💤 Files with no reviewable changes (1)
  • packages/swingset/src/stories/avatar.stories.tsx
🚧 Files skipped from review as they are similar to previous changes (5)
  • packages/ui/src/mosaic/user-button/tests/user-button.view.test.tsx
  • packages/ui/src/mosaic/components/avatar/avatar.test.tsx
  • packages/ui/src/mosaic/components/avatar/avatar.styles.ts
  • packages/ui/src/mosaic/user-button/user-button.view.tsx
  • packages/swingset/src/app/api/pending-image/route.ts

📝 Walkthrough

Walkthrough

Avatar fallbacks now hide fallback content by default and wrap it in a dedicated content slot. The fallback exposes pending state while an image request remains unresolved, applies a pulse animation, and stops pulsing after an error. Tests cover pending, error, and no-source states. A pending-image endpoint and story demonstrate the behavior. Avatar documentation and user-button comments were updated. An empty Changeset file was added.

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

Suggested reviewers:maxyinger

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 28.57% 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 nameStatusExplanation
Title check✅ PassedThe title clearly summarizes the main Avatar fallback behavior change.
Description check✅ PassedThe description accurately explains the hidden fallback content, pending pulse behavior, tests, and example changes.
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.

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

@coderabbitaicoderabbitaiBot 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 @.changeset/mosaic-avatar-skeleton-fallback.md:
- Around line 1-2: Add an `@clerk/ui` changeset entry in
mosaic-avatar-skeleton-fallback.md with the appropriate release bump and a
concise release note describing the updated default Avatar.Fallback output; do
not leave the changeset empty.
In `@packages/swingset/src/app/api/pending-image/route.ts`:
- Around line 17-29: Add unit tests covering the GET route’s delayed response
contract: use fake timers to verify normal resolution after HOLD_MS and
request-signal cancellation before the delay completes, and assert both PNG
content type and no-store cache-control headers for the returned Response.
In `@packages/ui/src/mosaic/components/avatar/avatar.styles.ts`:
- Around line 5-7: Reduce comments across the listed sites to only terse
non-obvious rationale: in avatar.styles.ts lines 5-7 and 53-58, replace each
explanation with one concise rationale line, and remove lines 63-64; remove
narration at avatar.test.tsx lines 16-18, 228-230, and 261-262; shorten the
accessibility and avatarName comments in user-button.view.tsx lines 135-136 and
186-188, remove lines 157-158 and 683, and remove narration in
user-button.view.test.tsx lines 653-654; shorten the route-purpose and cache
comments in pending-image/route.ts lines 1-4 and 13-14, remove line 7; and
remove the story narration in avatar.stories.tsx lines 49-50. Preserve code
behavior and leave comments only where they explain non-obvious rationale.
🪄 Autofix

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: ea7ed4b3-ea6a-424f-80b8-5477ae8450cd

📥 Commits

Reviewing files that changed from the base of the PR and between 3132d40 and 07a07ba.

📒 Files selected for processing (9)
  • .changeset/mosaic-avatar-skeleton-fallback.md
  • packages/swingset/src/app/api/pending-image/route.ts
  • packages/swingset/src/stories/avatar.mdx
  • packages/swingset/src/stories/avatar.stories.tsx
  • packages/ui/src/mosaic/components/avatar/avatar.styles.ts
  • packages/ui/src/mosaic/components/avatar/avatar.test.tsx
  • packages/ui/src/mosaic/components/avatar/avatar.tsx
  • packages/ui/src/mosaic/user-button/__tests__/user-button.view.test.tsx
  • packages/ui/src/mosaic/user-button/user-button.view.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)
  • clerk/cli(auto-detected)
  • clerk/clerk-ios(auto-detected)
  • clerk/clerk-android(auto-detected)

Comment thread.changeset/mosaic-avatar-skeleton-fallback.md
Comment threadpackages/swingset/src/app/api/pending-image/route.ts
Comment threadpackages/ui/src/mosaic/components/avatar/avatar.styles.ts Outdated
@alexcarpenter
alexcarpenter merged commit a9099bc into mainAug 11, 2026
52 checks passed
@alexcarpenter
alexcarpenter deleted the carp/avatar-skeleton-fallback branch August 11, 2026 20:32
Sign up for freeto 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.

3 participants

@alexcarpenter@maxyinger@austincalvelage
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(ui): render the Avatar fallback as a blank placeholder - #9398

Merged
alexcarpenter merged 4 commits into
mainfrom
carp/avatar-skeleton-fallback
Aug 11, 2026
Merged

fix(ui): render the Avatar fallback as a blank placeholder#9398
alexcarpenter merged 4 commits into
mainfrom
carp/avatar-skeleton-fallback

Conversation

@alexcarpenter

@alexcarpenteralexcarpenter commented Aug 11, 2026

Copy link
Copy Markdown
Member

Description

Avatar.Fallback keeps its API — it still takes children, initials or an icon — but it no longer paints them. The children move into a new avatar-fallback-content slot held at visibility: hidden, so what's left is the neutral fill on its own. visibility is the one property that takes the content out of the page, the accessibility tree, and the tab order together, and unlike inert it leaves hit-testing alone, so a click still reaches the button row these sit in. A consumer who wants the initials back overrides that one slot.

The fallback also pulses now, but only while an image is genuinely on its way (status === 'loading', reflected as data-pending). Someone who simply has no picture is not waiting on anything, so their mark holds still rather than pulsing for as long as it's on screen. The pulse matches skeleton.tsx's timing and drops to none under prefers-reduced-motion.

user-button.view.tsx is comments only — it still computes and passes initials(name), so the name is there for anyone who styles the slot back into view.

Swingset gets a Pending example. /api/pending-image holds its response for a minute with cache-control: no-store, so the pulse is the component's own status rather than a pinned attribute.

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:

Avatar.Fallback still takes children, but it no longer paints them: an
entity without a picture reads as an absent image rather than as two
letters. The children move into an `avatar-fallback-content` slot held at
`visibility: hidden`, which takes them out of the page, the accessibility
tree, and the tab order at once while leaving hit-testing alone, so a
consumer can style them back into view.
The fallback pulses only while an image is genuinely resolving
(`data-pending`); someone who has no picture is not waiting on anything,
so their mark holds still.
@changeset-bot

changeset-botBot commented Aug 11, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: d013999

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

@vercel

vercelBot commented Aug 11, 2026

Copy link
Copy Markdown

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

ProjectDeploymentActionsUpdated (UTC)
clerk-js-sandboxReadyReadyPreviewAug 11, 2026 6:11pm
swingsetReadyReadyPreviewAug 11, 2026 6:11pm

Request Review

@pkg-pr-new

pkg-pr-newBot commented Aug 11, 2026

Copy link
Copy Markdown

Open in StackBlitz

@clerk/astro

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

@clerk/backend

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

@clerk/chrome-extension

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

@clerk/clerk-js

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

@clerk/electron

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

@clerk/electron-passkeys

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

@clerk/eslint-plugin

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

@clerk/expo

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

@clerk/expo-google-signin

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

@clerk/expo-passkeys

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

@clerk/express

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

@clerk/fastify

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

@clerk/hono

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

@clerk/localizations

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

@clerk/nextjs

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

@clerk/nuxt

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

@clerk/react

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

@clerk/react-router

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

@clerk/shared

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

@clerk/tanstack-react-start

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

@clerk/testing

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

@clerk/ui

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

@clerk/upgrade

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

@clerk/vue

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

commit: d013999

@github-actions

github-actionsBot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

API Changes Report

Generated by Break Check on 2026-08-11T18:13:20.778Z

Summary

MetricCount
Packages analyzed19
Packages with changes0
🔴 Breaking changes0
🟡 Non-breaking changes0
🟢 Additions0

No API Changes Detected

All packages have stable APIs with no detected changes.


Report generated by Break Check

Last ran on d013999.

@coderabbitai

coderabbitaiBot commented Aug 11, 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: 528247a8-b031-4843-a13b-d341dea903a3

📥 Commits

Reviewing files that changed from the base of the PR and between 207eecd and d013999.

📒 Files selected for processing (6)
  • packages/swingset/src/app/api/pending-image/route.ts
  • packages/swingset/src/stories/avatar.stories.tsx
  • packages/ui/src/mosaic/components/avatar/avatar.styles.ts
  • packages/ui/src/mosaic/components/avatar/avatar.test.tsx
  • packages/ui/src/mosaic/user-button/__tests__/user-button.view.test.tsx
  • packages/ui/src/mosaic/user-button/user-button.view.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)
  • clerk/cli(auto-detected)
  • clerk/clerk-ios(auto-detected)
  • clerk/clerk-android(auto-detected)
💤 Files with no reviewable changes (1)
  • packages/swingset/src/stories/avatar.stories.tsx
🚧 Files skipped from review as they are similar to previous changes (5)
  • packages/ui/src/mosaic/user-button/tests/user-button.view.test.tsx
  • packages/ui/src/mosaic/components/avatar/avatar.test.tsx
  • packages/ui/src/mosaic/components/avatar/avatar.styles.ts
  • packages/ui/src/mosaic/user-button/user-button.view.tsx
  • packages/swingset/src/app/api/pending-image/route.ts

📝 Walkthrough

Walkthrough

Avatar fallbacks now hide fallback content by default and wrap it in a dedicated content slot. The fallback exposes pending state while an image request remains unresolved, applies a pulse animation, and stops pulsing after an error. Tests cover pending, error, and no-source states. A pending-image endpoint and story demonstrate the behavior. Avatar documentation and user-button comments were updated. An empty Changeset file was added.

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

Suggested reviewers:maxyinger

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 28.57% 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 nameStatusExplanation
Title check✅ PassedThe title clearly summarizes the main Avatar fallback behavior change.
Description check✅ PassedThe description accurately explains the hidden fallback content, pending pulse behavior, tests, and example changes.
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.

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

@coderabbitaicoderabbitaiBot 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 @.changeset/mosaic-avatar-skeleton-fallback.md:
- Around line 1-2: Add an `@clerk/ui` changeset entry in
mosaic-avatar-skeleton-fallback.md with the appropriate release bump and a
concise release note describing the updated default Avatar.Fallback output; do
not leave the changeset empty.
In `@packages/swingset/src/app/api/pending-image/route.ts`:
- Around line 17-29: Add unit tests covering the GET route’s delayed response
contract: use fake timers to verify normal resolution after HOLD_MS and
request-signal cancellation before the delay completes, and assert both PNG
content type and no-store cache-control headers for the returned Response.
In `@packages/ui/src/mosaic/components/avatar/avatar.styles.ts`:
- Around line 5-7: Reduce comments across the listed sites to only terse
non-obvious rationale: in avatar.styles.ts lines 5-7 and 53-58, replace each
explanation with one concise rationale line, and remove lines 63-64; remove
narration at avatar.test.tsx lines 16-18, 228-230, and 261-262; shorten the
accessibility and avatarName comments in user-button.view.tsx lines 135-136 and
186-188, remove lines 157-158 and 683, and remove narration in
user-button.view.test.tsx lines 653-654; shorten the route-purpose and cache
comments in pending-image/route.ts lines 1-4 and 13-14, remove line 7; and
remove the story narration in avatar.stories.tsx lines 49-50. Preserve code
behavior and leave comments only where they explain non-obvious rationale.
🪄 Autofix

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: ea7ed4b3-ea6a-424f-80b8-5477ae8450cd

📥 Commits

Reviewing files that changed from the base of the PR and between 3132d40 and 07a07ba.

📒 Files selected for processing (9)
  • .changeset/mosaic-avatar-skeleton-fallback.md
  • packages/swingset/src/app/api/pending-image/route.ts
  • packages/swingset/src/stories/avatar.mdx
  • packages/swingset/src/stories/avatar.stories.tsx
  • packages/ui/src/mosaic/components/avatar/avatar.styles.ts
  • packages/ui/src/mosaic/components/avatar/avatar.test.tsx
  • packages/ui/src/mosaic/components/avatar/avatar.tsx
  • packages/ui/src/mosaic/user-button/__tests__/user-button.view.test.tsx
  • packages/ui/src/mosaic/user-button/user-button.view.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)
  • clerk/cli(auto-detected)
  • clerk/clerk-ios(auto-detected)
  • clerk/clerk-android(auto-detected)

Comment thread.changeset/mosaic-avatar-skeleton-fallback.md
Comment threadpackages/swingset/src/app/api/pending-image/route.ts
Comment threadpackages/ui/src/mosaic/components/avatar/avatar.styles.ts Outdated
@alexcarpenter
alexcarpenter merged commit a9099bc into mainAug 11, 2026
52 checks passed
@alexcarpenter
alexcarpenter deleted the carp/avatar-skeleton-fallback branch August 11, 2026 20:32
Sign up for freeto 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.

3 participants

@alexcarpenter@maxyinger@austincalvelage
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(ui): render the Avatar fallback as a blank placeholder - #9398

Merged
alexcarpenter merged 4 commits into
mainfrom
carp/avatar-skeleton-fallback
Aug 11, 2026
Merged

fix(ui): render the Avatar fallback as a blank placeholder#9398
alexcarpenter merged 4 commits into
mainfrom
carp/avatar-skeleton-fallback

Conversation

@alexcarpenter

@alexcarpenteralexcarpenter commented Aug 11, 2026

Copy link
Copy Markdown
Member

Description

Avatar.Fallback keeps its API — it still takes children, initials or an icon — but it no longer paints them. The children move into a new avatar-fallback-content slot held at visibility: hidden, so what's left is the neutral fill on its own. visibility is the one property that takes the content out of the page, the accessibility tree, and the tab order together, and unlike inert it leaves hit-testing alone, so a click still reaches the button row these sit in. A consumer who wants the initials back overrides that one slot.

The fallback also pulses now, but only while an image is genuinely on its way (status === 'loading', reflected as data-pending). Someone who simply has no picture is not waiting on anything, so their mark holds still rather than pulsing for as long as it's on screen. The pulse matches skeleton.tsx's timing and drops to none under prefers-reduced-motion.

user-button.view.tsx is comments only — it still computes and passes initials(name), so the name is there for anyone who styles the slot back into view.

Swingset gets a Pending example. /api/pending-image holds its response for a minute with cache-control: no-store, so the pulse is the component's own status rather than a pinned attribute.

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:

Avatar.Fallback still takes children, but it no longer paints them: an
entity without a picture reads as an absent image rather than as two
letters. The children move into an `avatar-fallback-content` slot held at
`visibility: hidden`, which takes them out of the page, the accessibility
tree, and the tab order at once while leaving hit-testing alone, so a
consumer can style them back into view.
The fallback pulses only while an image is genuinely resolving
(`data-pending`); someone who has no picture is not waiting on anything,
so their mark holds still.
@changeset-bot

changeset-botBot commented Aug 11, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: d013999

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

@vercel

vercelBot commented Aug 11, 2026

Copy link
Copy Markdown

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

ProjectDeploymentActionsUpdated (UTC)
clerk-js-sandboxReadyReadyPreviewAug 11, 2026 6:11pm
swingsetReadyReadyPreviewAug 11, 2026 6:11pm

Request Review

@pkg-pr-new

pkg-pr-newBot commented Aug 11, 2026

Copy link
Copy Markdown

Open in StackBlitz

@clerk/astro

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

@clerk/backend

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

@clerk/chrome-extension

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

@clerk/clerk-js

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

@clerk/electron

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

@clerk/electron-passkeys

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

@clerk/eslint-plugin

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

@clerk/expo

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

@clerk/expo-google-signin

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

@clerk/expo-passkeys

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

@clerk/express

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

@clerk/fastify

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

@clerk/hono

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

@clerk/localizations

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

@clerk/nextjs

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

@clerk/nuxt

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

@clerk/react

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

@clerk/react-router

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

@clerk/shared

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

@clerk/tanstack-react-start

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

@clerk/testing

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

@clerk/ui

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

@clerk/upgrade

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

@clerk/vue

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

commit: d013999

@github-actions

github-actionsBot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

API Changes Report

Generated by Break Check on 2026-08-11T18:13:20.778Z

Summary

MetricCount
Packages analyzed19
Packages with changes0
🔴 Breaking changes0
🟡 Non-breaking changes0
🟢 Additions0

No API Changes Detected

All packages have stable APIs with no detected changes.


Report generated by Break Check

Last ran on d013999.

@coderabbitai

coderabbitaiBot commented Aug 11, 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: 528247a8-b031-4843-a13b-d341dea903a3

📥 Commits

Reviewing files that changed from the base of the PR and between 207eecd and d013999.

📒 Files selected for processing (6)
  • packages/swingset/src/app/api/pending-image/route.ts
  • packages/swingset/src/stories/avatar.stories.tsx
  • packages/ui/src/mosaic/components/avatar/avatar.styles.ts
  • packages/ui/src/mosaic/components/avatar/avatar.test.tsx
  • packages/ui/src/mosaic/user-button/__tests__/user-button.view.test.tsx
  • packages/ui/src/mosaic/user-button/user-button.view.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)
  • clerk/cli(auto-detected)
  • clerk/clerk-ios(auto-detected)
  • clerk/clerk-android(auto-detected)
💤 Files with no reviewable changes (1)
  • packages/swingset/src/stories/avatar.stories.tsx
🚧 Files skipped from review as they are similar to previous changes (5)
  • packages/ui/src/mosaic/user-button/tests/user-button.view.test.tsx
  • packages/ui/src/mosaic/components/avatar/avatar.test.tsx
  • packages/ui/src/mosaic/components/avatar/avatar.styles.ts
  • packages/ui/src/mosaic/user-button/user-button.view.tsx
  • packages/swingset/src/app/api/pending-image/route.ts

📝 Walkthrough

Walkthrough

Avatar fallbacks now hide fallback content by default and wrap it in a dedicated content slot. The fallback exposes pending state while an image request remains unresolved, applies a pulse animation, and stops pulsing after an error. Tests cover pending, error, and no-source states. A pending-image endpoint and story demonstrate the behavior. Avatar documentation and user-button comments were updated. An empty Changeset file was added.

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

Suggested reviewers:maxyinger

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 28.57% 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 nameStatusExplanation
Title check✅ PassedThe title clearly summarizes the main Avatar fallback behavior change.
Description check✅ PassedThe description accurately explains the hidden fallback content, pending pulse behavior, tests, and example changes.
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.

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

@coderabbitaicoderabbitaiBot 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 @.changeset/mosaic-avatar-skeleton-fallback.md:
- Around line 1-2: Add an `@clerk/ui` changeset entry in
mosaic-avatar-skeleton-fallback.md with the appropriate release bump and a
concise release note describing the updated default Avatar.Fallback output; do
not leave the changeset empty.
In `@packages/swingset/src/app/api/pending-image/route.ts`:
- Around line 17-29: Add unit tests covering the GET route’s delayed response
contract: use fake timers to verify normal resolution after HOLD_MS and
request-signal cancellation before the delay completes, and assert both PNG
content type and no-store cache-control headers for the returned Response.
In `@packages/ui/src/mosaic/components/avatar/avatar.styles.ts`:
- Around line 5-7: Reduce comments across the listed sites to only terse
non-obvious rationale: in avatar.styles.ts lines 5-7 and 53-58, replace each
explanation with one concise rationale line, and remove lines 63-64; remove
narration at avatar.test.tsx lines 16-18, 228-230, and 261-262; shorten the
accessibility and avatarName comments in user-button.view.tsx lines 135-136 and
186-188, remove lines 157-158 and 683, and remove narration in
user-button.view.test.tsx lines 653-654; shorten the route-purpose and cache
comments in pending-image/route.ts lines 1-4 and 13-14, remove line 7; and
remove the story narration in avatar.stories.tsx lines 49-50. Preserve code
behavior and leave comments only where they explain non-obvious rationale.
🪄 Autofix

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: ea7ed4b3-ea6a-424f-80b8-5477ae8450cd

📥 Commits

Reviewing files that changed from the base of the PR and between 3132d40 and 07a07ba.

📒 Files selected for processing (9)
  • .changeset/mosaic-avatar-skeleton-fallback.md
  • packages/swingset/src/app/api/pending-image/route.ts
  • packages/swingset/src/stories/avatar.mdx
  • packages/swingset/src/stories/avatar.stories.tsx
  • packages/ui/src/mosaic/components/avatar/avatar.styles.ts
  • packages/ui/src/mosaic/components/avatar/avatar.test.tsx
  • packages/ui/src/mosaic/components/avatar/avatar.tsx
  • packages/ui/src/mosaic/user-button/__tests__/user-button.view.test.tsx
  • packages/ui/src/mosaic/user-button/user-button.view.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)
  • clerk/cli(auto-detected)
  • clerk/clerk-ios(auto-detected)
  • clerk/clerk-android(auto-detected)

Comment thread.changeset/mosaic-avatar-skeleton-fallback.md
Comment threadpackages/swingset/src/app/api/pending-image/route.ts
Comment threadpackages/ui/src/mosaic/components/avatar/avatar.styles.ts Outdated
@alexcarpenter
alexcarpenter merged commit a9099bc into mainAug 11, 2026
52 checks passed
@alexcarpenter
alexcarpenter deleted the carp/avatar-skeleton-fallback branch August 11, 2026 20:32
Sign up for freeto 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.

3 participants

@alexcarpenter@maxyinger@austincalvelage
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

fix(ui): render the Avatar fallback as a blank placeholder - #9398

Merged
alexcarpenter merged 4 commits into
mainfrom
carp/avatar-skeleton-fallback
Aug 11, 2026
Merged

fix(ui): render the Avatar fallback as a blank placeholder#9398
alexcarpenter merged 4 commits into
mainfrom
carp/avatar-skeleton-fallback

Conversation

@alexcarpenter

@alexcarpenteralexcarpenter commented Aug 11, 2026

Copy link
Copy Markdown
Member

Description

Avatar.Fallback keeps its API — it still takes children, initials or an icon — but it no longer paints them. The children move into a new avatar-fallback-content slot held at visibility: hidden, so what's left is the neutral fill on its own. visibility is the one property that takes the content out of the page, the accessibility tree, and the tab order together, and unlike inert it leaves hit-testing alone, so a click still reaches the button row these sit in. A consumer who wants the initials back overrides that one slot.

The fallback also pulses now, but only while an image is genuinely on its way (status === 'loading', reflected as data-pending). Someone who simply has no picture is not waiting on anything, so their mark holds still rather than pulsing for as long as it's on screen. The pulse matches skeleton.tsx's timing and drops to none under prefers-reduced-motion.

user-button.view.tsx is comments only — it still computes and passes initials(name), so the name is there for anyone who styles the slot back into view.

Swingset gets a Pending example. /api/pending-image holds its response for a minute with cache-control: no-store, so the pulse is the component's own status rather than a pinned attribute.

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:

Avatar.Fallback still takes children, but it no longer paints them: an
entity without a picture reads as an absent image rather than as two
letters. The children move into an `avatar-fallback-content` slot held at
`visibility: hidden`, which takes them out of the page, the accessibility
tree, and the tab order at once while leaving hit-testing alone, so a
consumer can style them back into view.
The fallback pulses only while an image is genuinely resolving
(`data-pending`); someone who has no picture is not waiting on anything,
so their mark holds still.
@changeset-bot

changeset-botBot commented Aug 11, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: d013999

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

@vercel

vercelBot commented Aug 11, 2026

Copy link
Copy Markdown

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

ProjectDeploymentActionsUpdated (UTC)
clerk-js-sandboxReadyReadyPreviewAug 11, 2026 6:11pm
swingsetReadyReadyPreviewAug 11, 2026 6:11pm

Request Review

@pkg-pr-new

pkg-pr-newBot commented Aug 11, 2026

Copy link
Copy Markdown

Open in StackBlitz

@clerk/astro

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

@clerk/backend

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

@clerk/chrome-extension

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

@clerk/clerk-js

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

@clerk/electron

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

@clerk/electron-passkeys

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

@clerk/eslint-plugin

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

@clerk/expo

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

@clerk/expo-google-signin

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

@clerk/expo-passkeys

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

@clerk/express

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

@clerk/fastify

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

@clerk/hono

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

@clerk/localizations

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

@clerk/nextjs

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

@clerk/nuxt

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

@clerk/react

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

@clerk/react-router

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

@clerk/shared

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

@clerk/tanstack-react-start

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

@clerk/testing

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

@clerk/ui

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

@clerk/upgrade

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

@clerk/vue

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

commit: d013999

@github-actions

github-actionsBot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

API Changes Report

Generated by Break Check on 2026-08-11T18:13:20.778Z

Summary

MetricCount
Packages analyzed19
Packages with changes0
🔴 Breaking changes0
🟡 Non-breaking changes0
🟢 Additions0

No API Changes Detected

All packages have stable APIs with no detected changes.


Report generated by Break Check

Last ran on d013999.

@coderabbitai

coderabbitaiBot commented Aug 11, 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: 528247a8-b031-4843-a13b-d341dea903a3

📥 Commits

Reviewing files that changed from the base of the PR and between 207eecd and d013999.

📒 Files selected for processing (6)
  • packages/swingset/src/app/api/pending-image/route.ts
  • packages/swingset/src/stories/avatar.stories.tsx
  • packages/ui/src/mosaic/components/avatar/avatar.styles.ts
  • packages/ui/src/mosaic/components/avatar/avatar.test.tsx
  • packages/ui/src/mosaic/user-button/__tests__/user-button.view.test.tsx
  • packages/ui/src/mosaic/user-button/user-button.view.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)
  • clerk/cli(auto-detected)
  • clerk/clerk-ios(auto-detected)
  • clerk/clerk-android(auto-detected)
💤 Files with no reviewable changes (1)
  • packages/swingset/src/stories/avatar.stories.tsx
🚧 Files skipped from review as they are similar to previous changes (5)
  • packages/ui/src/mosaic/user-button/tests/user-button.view.test.tsx
  • packages/ui/src/mosaic/components/avatar/avatar.test.tsx
  • packages/ui/src/mosaic/components/avatar/avatar.styles.ts
  • packages/ui/src/mosaic/user-button/user-button.view.tsx
  • packages/swingset/src/app/api/pending-image/route.ts

📝 Walkthrough

Walkthrough

Avatar fallbacks now hide fallback content by default and wrap it in a dedicated content slot. The fallback exposes pending state while an image request remains unresolved, applies a pulse animation, and stops pulsing after an error. Tests cover pending, error, and no-source states. A pending-image endpoint and story demonstrate the behavior. Avatar documentation and user-button comments were updated. An empty Changeset file was added.

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

Suggested reviewers:maxyinger

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 28.57% 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 nameStatusExplanation
Title check✅ PassedThe title clearly summarizes the main Avatar fallback behavior change.
Description check✅ PassedThe description accurately explains the hidden fallback content, pending pulse behavior, tests, and example changes.
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.

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

@coderabbitaicoderabbitaiBot 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 @.changeset/mosaic-avatar-skeleton-fallback.md:
- Around line 1-2: Add an `@clerk/ui` changeset entry in
mosaic-avatar-skeleton-fallback.md with the appropriate release bump and a
concise release note describing the updated default Avatar.Fallback output; do
not leave the changeset empty.
In `@packages/swingset/src/app/api/pending-image/route.ts`:
- Around line 17-29: Add unit tests covering the GET route’s delayed response
contract: use fake timers to verify normal resolution after HOLD_MS and
request-signal cancellation before the delay completes, and assert both PNG
content type and no-store cache-control headers for the returned Response.
In `@packages/ui/src/mosaic/components/avatar/avatar.styles.ts`:
- Around line 5-7: Reduce comments across the listed sites to only terse
non-obvious rationale: in avatar.styles.ts lines 5-7 and 53-58, replace each
explanation with one concise rationale line, and remove lines 63-64; remove
narration at avatar.test.tsx lines 16-18, 228-230, and 261-262; shorten the
accessibility and avatarName comments in user-button.view.tsx lines 135-136 and
186-188, remove lines 157-158 and 683, and remove narration in
user-button.view.test.tsx lines 653-654; shorten the route-purpose and cache
comments in pending-image/route.ts lines 1-4 and 13-14, remove line 7; and
remove the story narration in avatar.stories.tsx lines 49-50. Preserve code
behavior and leave comments only where they explain non-obvious rationale.
🪄 Autofix

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: ea7ed4b3-ea6a-424f-80b8-5477ae8450cd

📥 Commits

Reviewing files that changed from the base of the PR and between 3132d40 and 07a07ba.

📒 Files selected for processing (9)
  • .changeset/mosaic-avatar-skeleton-fallback.md
  • packages/swingset/src/app/api/pending-image/route.ts
  • packages/swingset/src/stories/avatar.mdx
  • packages/swingset/src/stories/avatar.stories.tsx
  • packages/ui/src/mosaic/components/avatar/avatar.styles.ts
  • packages/ui/src/mosaic/components/avatar/avatar.test.tsx
  • packages/ui/src/mosaic/components/avatar/avatar.tsx
  • packages/ui/src/mosaic/user-button/__tests__/user-button.view.test.tsx
  • packages/ui/src/mosaic/user-button/user-button.view.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)
  • clerk/cli(auto-detected)
  • clerk/clerk-ios(auto-detected)
  • clerk/clerk-android(auto-detected)

Comment thread.changeset/mosaic-avatar-skeleton-fallback.md
Comment threadpackages/swingset/src/app/api/pending-image/route.ts
Comment threadpackages/ui/src/mosaic/components/avatar/avatar.styles.ts Outdated
@alexcarpenter
alexcarpenter merged commit a9099bc into mainAug 11, 2026
52 checks passed
@alexcarpenter
alexcarpenter deleted the carp/avatar-skeleton-fallback branch August 11, 2026 20:32
Sign up for freeto 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.

3 participants

@alexcarpenter@maxyinger@austincalvelage
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(ui): render the Avatar fallback as a blank placeholder - #9398

Merged
alexcarpenter merged 4 commits into
mainfrom
carp/avatar-skeleton-fallback
Aug 11, 2026
Merged

fix(ui): render the Avatar fallback as a blank placeholder#9398
alexcarpenter merged 4 commits into
mainfrom
carp/avatar-skeleton-fallback

Conversation

@alexcarpenter

@alexcarpenteralexcarpenter commented Aug 11, 2026

Copy link
Copy Markdown
Member

Description

Avatar.Fallback keeps its API — it still takes children, initials or an icon — but it no longer paints them. The children move into a new avatar-fallback-content slot held at visibility: hidden, so what's left is the neutral fill on its own. visibility is the one property that takes the content out of the page, the accessibility tree, and the tab order together, and unlike inert it leaves hit-testing alone, so a click still reaches the button row these sit in. A consumer who wants the initials back overrides that one slot.

The fallback also pulses now, but only while an image is genuinely on its way (status === 'loading', reflected as data-pending). Someone who simply has no picture is not waiting on anything, so their mark holds still rather than pulsing for as long as it's on screen. The pulse matches skeleton.tsx's timing and drops to none under prefers-reduced-motion.

user-button.view.tsx is comments only — it still computes and passes initials(name), so the name is there for anyone who styles the slot back into view.

Swingset gets a Pending example. /api/pending-image holds its response for a minute with cache-control: no-store, so the pulse is the component's own status rather than a pinned attribute.

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:

Avatar.Fallback still takes children, but it no longer paints them: an
entity without a picture reads as an absent image rather than as two
letters. The children move into an `avatar-fallback-content` slot held at
`visibility: hidden`, which takes them out of the page, the accessibility
tree, and the tab order at once while leaving hit-testing alone, so a
consumer can style them back into view.
The fallback pulses only while an image is genuinely resolving
(`data-pending`); someone who has no picture is not waiting on anything,
so their mark holds still.
@changeset-bot

changeset-botBot commented Aug 11, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: d013999

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

@vercel

vercelBot commented Aug 11, 2026

Copy link
Copy Markdown

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

ProjectDeploymentActionsUpdated (UTC)
clerk-js-sandboxReadyReadyPreviewAug 11, 2026 6:11pm
swingsetReadyReadyPreviewAug 11, 2026 6:11pm

Request Review

@pkg-pr-new

pkg-pr-newBot commented Aug 11, 2026

Copy link
Copy Markdown

Open in StackBlitz

@clerk/astro

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

@clerk/backend

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

@clerk/chrome-extension

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

@clerk/clerk-js

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

@clerk/electron

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

@clerk/electron-passkeys

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

@clerk/eslint-plugin

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

@clerk/expo

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

@clerk/expo-google-signin

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

@clerk/expo-passkeys

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

@clerk/express

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

@clerk/fastify

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

@clerk/hono

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

@clerk/localizations

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

@clerk/nextjs

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

@clerk/nuxt

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

@clerk/react

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

@clerk/react-router

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

@clerk/shared

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

@clerk/tanstack-react-start

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

@clerk/testing

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

@clerk/ui

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

@clerk/upgrade

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

@clerk/vue

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

commit: d013999

@github-actions

github-actionsBot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

API Changes Report

Generated by Break Check on 2026-08-11T18:13:20.778Z

Summary

MetricCount
Packages analyzed19
Packages with changes0
🔴 Breaking changes0
🟡 Non-breaking changes0
🟢 Additions0

No API Changes Detected

All packages have stable APIs with no detected changes.


Report generated by Break Check

Last ran on d013999.

@coderabbitai

coderabbitaiBot commented Aug 11, 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: 528247a8-b031-4843-a13b-d341dea903a3

📥 Commits

Reviewing files that changed from the base of the PR and between 207eecd and d013999.

📒 Files selected for processing (6)
  • packages/swingset/src/app/api/pending-image/route.ts
  • packages/swingset/src/stories/avatar.stories.tsx
  • packages/ui/src/mosaic/components/avatar/avatar.styles.ts
  • packages/ui/src/mosaic/components/avatar/avatar.test.tsx
  • packages/ui/src/mosaic/user-button/__tests__/user-button.view.test.tsx
  • packages/ui/src/mosaic/user-button/user-button.view.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)
  • clerk/cli(auto-detected)
  • clerk/clerk-ios(auto-detected)
  • clerk/clerk-android(auto-detected)
💤 Files with no reviewable changes (1)
  • packages/swingset/src/stories/avatar.stories.tsx
🚧 Files skipped from review as they are similar to previous changes (5)
  • packages/ui/src/mosaic/user-button/tests/user-button.view.test.tsx
  • packages/ui/src/mosaic/components/avatar/avatar.test.tsx
  • packages/ui/src/mosaic/components/avatar/avatar.styles.ts
  • packages/ui/src/mosaic/user-button/user-button.view.tsx
  • packages/swingset/src/app/api/pending-image/route.ts

📝 Walkthrough

Walkthrough

Avatar fallbacks now hide fallback content by default and wrap it in a dedicated content slot. The fallback exposes pending state while an image request remains unresolved, applies a pulse animation, and stops pulsing after an error. Tests cover pending, error, and no-source states. A pending-image endpoint and story demonstrate the behavior. Avatar documentation and user-button comments were updated. An empty Changeset file was added.

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

Suggested reviewers:maxyinger

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 28.57% 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 nameStatusExplanation
Title check✅ PassedThe title clearly summarizes the main Avatar fallback behavior change.
Description check✅ PassedThe description accurately explains the hidden fallback content, pending pulse behavior, tests, and example changes.
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.

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

@coderabbitaicoderabbitaiBot 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 @.changeset/mosaic-avatar-skeleton-fallback.md:
- Around line 1-2: Add an `@clerk/ui` changeset entry in
mosaic-avatar-skeleton-fallback.md with the appropriate release bump and a
concise release note describing the updated default Avatar.Fallback output; do
not leave the changeset empty.
In `@packages/swingset/src/app/api/pending-image/route.ts`:
- Around line 17-29: Add unit tests covering the GET route’s delayed response
contract: use fake timers to verify normal resolution after HOLD_MS and
request-signal cancellation before the delay completes, and assert both PNG
content type and no-store cache-control headers for the returned Response.
In `@packages/ui/src/mosaic/components/avatar/avatar.styles.ts`:
- Around line 5-7: Reduce comments across the listed sites to only terse
non-obvious rationale: in avatar.styles.ts lines 5-7 and 53-58, replace each
explanation with one concise rationale line, and remove lines 63-64; remove
narration at avatar.test.tsx lines 16-18, 228-230, and 261-262; shorten the
accessibility and avatarName comments in user-button.view.tsx lines 135-136 and
186-188, remove lines 157-158 and 683, and remove narration in
user-button.view.test.tsx lines 653-654; shorten the route-purpose and cache
comments in pending-image/route.ts lines 1-4 and 13-14, remove line 7; and
remove the story narration in avatar.stories.tsx lines 49-50. Preserve code
behavior and leave comments only where they explain non-obvious rationale.
🪄 Autofix

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: ea7ed4b3-ea6a-424f-80b8-5477ae8450cd

📥 Commits

Reviewing files that changed from the base of the PR and between 3132d40 and 07a07ba.

📒 Files selected for processing (9)
  • .changeset/mosaic-avatar-skeleton-fallback.md
  • packages/swingset/src/app/api/pending-image/route.ts
  • packages/swingset/src/stories/avatar.mdx
  • packages/swingset/src/stories/avatar.stories.tsx
  • packages/ui/src/mosaic/components/avatar/avatar.styles.ts
  • packages/ui/src/mosaic/components/avatar/avatar.test.tsx
  • packages/ui/src/mosaic/components/avatar/avatar.tsx
  • packages/ui/src/mosaic/user-button/__tests__/user-button.view.test.tsx
  • packages/ui/src/mosaic/user-button/user-button.view.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)
  • clerk/cli(auto-detected)
  • clerk/clerk-ios(auto-detected)
  • clerk/clerk-android(auto-detected)

Comment thread.changeset/mosaic-avatar-skeleton-fallback.md
Comment threadpackages/swingset/src/app/api/pending-image/route.ts
Comment threadpackages/ui/src/mosaic/components/avatar/avatar.styles.ts Outdated
@alexcarpenter
alexcarpenter merged commit a9099bc into mainAug 11, 2026
52 checks passed
@alexcarpenter
alexcarpenter deleted the carp/avatar-skeleton-fallback branch August 11, 2026 20:32
Sign up for freeto 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.

3 participants

@alexcarpenter@maxyinger@austincalvelage
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(ui): render the Avatar fallback as a blank placeholder - #9398

Merged
alexcarpenter merged 4 commits into
mainfrom
carp/avatar-skeleton-fallback
Aug 11, 2026
Merged

fix(ui): render the Avatar fallback as a blank placeholder#9398
alexcarpenter merged 4 commits into
mainfrom
carp/avatar-skeleton-fallback

Conversation

@alexcarpenter

@alexcarpenteralexcarpenter commented Aug 11, 2026

Copy link
Copy Markdown
Member

Description

Avatar.Fallback keeps its API — it still takes children, initials or an icon — but it no longer paints them. The children move into a new avatar-fallback-content slot held at visibility: hidden, so what's left is the neutral fill on its own. visibility is the one property that takes the content out of the page, the accessibility tree, and the tab order together, and unlike inert it leaves hit-testing alone, so a click still reaches the button row these sit in. A consumer who wants the initials back overrides that one slot.

The fallback also pulses now, but only while an image is genuinely on its way (status === 'loading', reflected as data-pending). Someone who simply has no picture is not waiting on anything, so their mark holds still rather than pulsing for as long as it's on screen. The pulse matches skeleton.tsx's timing and drops to none under prefers-reduced-motion.

user-button.view.tsx is comments only — it still computes and passes initials(name), so the name is there for anyone who styles the slot back into view.

Swingset gets a Pending example. /api/pending-image holds its response for a minute with cache-control: no-store, so the pulse is the component's own status rather than a pinned attribute.

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:

Avatar.Fallback still takes children, but it no longer paints them: an
entity without a picture reads as an absent image rather than as two
letters. The children move into an `avatar-fallback-content` slot held at
`visibility: hidden`, which takes them out of the page, the accessibility
tree, and the tab order at once while leaving hit-testing alone, so a
consumer can style them back into view.
The fallback pulses only while an image is genuinely resolving
(`data-pending`); someone who has no picture is not waiting on anything,
so their mark holds still.
@changeset-bot

changeset-botBot commented Aug 11, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: d013999

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

@vercel

vercelBot commented Aug 11, 2026

Copy link
Copy Markdown

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

ProjectDeploymentActionsUpdated (UTC)
clerk-js-sandboxReadyReadyPreviewAug 11, 2026 6:11pm
swingsetReadyReadyPreviewAug 11, 2026 6:11pm

Request Review

@pkg-pr-new

pkg-pr-newBot commented Aug 11, 2026

Copy link
Copy Markdown

Open in StackBlitz

@clerk/astro

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

@clerk/backend

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

@clerk/chrome-extension

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

@clerk/clerk-js

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

@clerk/electron

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

@clerk/electron-passkeys

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

@clerk/eslint-plugin

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

@clerk/expo

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

@clerk/expo-google-signin

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

@clerk/expo-passkeys

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

@clerk/express

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

@clerk/fastify

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

@clerk/hono

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

@clerk/localizations

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

@clerk/nextjs

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

@clerk/nuxt

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

@clerk/react

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

@clerk/react-router

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

@clerk/shared

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

@clerk/tanstack-react-start

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

@clerk/testing

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

@clerk/ui

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

@clerk/upgrade

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

@clerk/vue

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

commit: d013999

@github-actions

github-actionsBot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

API Changes Report

Generated by Break Check on 2026-08-11T18:13:20.778Z

Summary

MetricCount
Packages analyzed19
Packages with changes0
🔴 Breaking changes0
🟡 Non-breaking changes0
🟢 Additions0

No API Changes Detected

All packages have stable APIs with no detected changes.


Report generated by Break Check

Last ran on d013999.

@coderabbitai

coderabbitaiBot commented Aug 11, 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: 528247a8-b031-4843-a13b-d341dea903a3

📥 Commits

Reviewing files that changed from the base of the PR and between 207eecd and d013999.

📒 Files selected for processing (6)
  • packages/swingset/src/app/api/pending-image/route.ts
  • packages/swingset/src/stories/avatar.stories.tsx
  • packages/ui/src/mosaic/components/avatar/avatar.styles.ts
  • packages/ui/src/mosaic/components/avatar/avatar.test.tsx
  • packages/ui/src/mosaic/user-button/__tests__/user-button.view.test.tsx
  • packages/ui/src/mosaic/user-button/user-button.view.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)
  • clerk/cli(auto-detected)
  • clerk/clerk-ios(auto-detected)
  • clerk/clerk-android(auto-detected)
💤 Files with no reviewable changes (1)
  • packages/swingset/src/stories/avatar.stories.tsx
🚧 Files skipped from review as they are similar to previous changes (5)
  • packages/ui/src/mosaic/user-button/tests/user-button.view.test.tsx
  • packages/ui/src/mosaic/components/avatar/avatar.test.tsx
  • packages/ui/src/mosaic/components/avatar/avatar.styles.ts
  • packages/ui/src/mosaic/user-button/user-button.view.tsx
  • packages/swingset/src/app/api/pending-image/route.ts

📝 Walkthrough

Walkthrough

Avatar fallbacks now hide fallback content by default and wrap it in a dedicated content slot. The fallback exposes pending state while an image request remains unresolved, applies a pulse animation, and stops pulsing after an error. Tests cover pending, error, and no-source states. A pending-image endpoint and story demonstrate the behavior. Avatar documentation and user-button comments were updated. An empty Changeset file was added.

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

Suggested reviewers:maxyinger

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 28.57% 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 nameStatusExplanation
Title check✅ PassedThe title clearly summarizes the main Avatar fallback behavior change.
Description check✅ PassedThe description accurately explains the hidden fallback content, pending pulse behavior, tests, and example changes.
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.

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

@coderabbitaicoderabbitaiBot 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 @.changeset/mosaic-avatar-skeleton-fallback.md:
- Around line 1-2: Add an `@clerk/ui` changeset entry in
mosaic-avatar-skeleton-fallback.md with the appropriate release bump and a
concise release note describing the updated default Avatar.Fallback output; do
not leave the changeset empty.
In `@packages/swingset/src/app/api/pending-image/route.ts`:
- Around line 17-29: Add unit tests covering the GET route’s delayed response
contract: use fake timers to verify normal resolution after HOLD_MS and
request-signal cancellation before the delay completes, and assert both PNG
content type and no-store cache-control headers for the returned Response.
In `@packages/ui/src/mosaic/components/avatar/avatar.styles.ts`:
- Around line 5-7: Reduce comments across the listed sites to only terse
non-obvious rationale: in avatar.styles.ts lines 5-7 and 53-58, replace each
explanation with one concise rationale line, and remove lines 63-64; remove
narration at avatar.test.tsx lines 16-18, 228-230, and 261-262; shorten the
accessibility and avatarName comments in user-button.view.tsx lines 135-136 and
186-188, remove lines 157-158 and 683, and remove narration in
user-button.view.test.tsx lines 653-654; shorten the route-purpose and cache
comments in pending-image/route.ts lines 1-4 and 13-14, remove line 7; and
remove the story narration in avatar.stories.tsx lines 49-50. Preserve code
behavior and leave comments only where they explain non-obvious rationale.
🪄 Autofix

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: ea7ed4b3-ea6a-424f-80b8-5477ae8450cd

📥 Commits

Reviewing files that changed from the base of the PR and between 3132d40 and 07a07ba.

📒 Files selected for processing (9)
  • .changeset/mosaic-avatar-skeleton-fallback.md
  • packages/swingset/src/app/api/pending-image/route.ts
  • packages/swingset/src/stories/avatar.mdx
  • packages/swingset/src/stories/avatar.stories.tsx
  • packages/ui/src/mosaic/components/avatar/avatar.styles.ts
  • packages/ui/src/mosaic/components/avatar/avatar.test.tsx
  • packages/ui/src/mosaic/components/avatar/avatar.tsx
  • packages/ui/src/mosaic/user-button/__tests__/user-button.view.test.tsx
  • packages/ui/src/mosaic/user-button/user-button.view.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)
  • clerk/cli(auto-detected)
  • clerk/clerk-ios(auto-detected)
  • clerk/clerk-android(auto-detected)

Comment thread.changeset/mosaic-avatar-skeleton-fallback.md
Comment threadpackages/swingset/src/app/api/pending-image/route.ts
Comment threadpackages/ui/src/mosaic/components/avatar/avatar.styles.ts Outdated
@alexcarpenter
alexcarpenter merged commit a9099bc into mainAug 11, 2026
52 checks passed
@alexcarpenter
alexcarpenter deleted the carp/avatar-skeleton-fallback branch August 11, 2026 20:32
Sign up for freeto 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.

3 participants

@alexcarpenter@maxyinger@austincalvelage
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

fix(ui): render the Avatar fallback as a blank placeholder - #9398

Merged
alexcarpenter merged 4 commits into
mainfrom
carp/avatar-skeleton-fallback
Aug 11, 2026
Merged

fix(ui): render the Avatar fallback as a blank placeholder#9398
alexcarpenter merged 4 commits into
mainfrom
carp/avatar-skeleton-fallback

Conversation

@alexcarpenter

@alexcarpenteralexcarpenter commented Aug 11, 2026

Copy link
Copy Markdown
Member

Description

Avatar.Fallback keeps its API — it still takes children, initials or an icon — but it no longer paints them. The children move into a new avatar-fallback-content slot held at visibility: hidden, so what's left is the neutral fill on its own. visibility is the one property that takes the content out of the page, the accessibility tree, and the tab order together, and unlike inert it leaves hit-testing alone, so a click still reaches the button row these sit in. A consumer who wants the initials back overrides that one slot.

The fallback also pulses now, but only while an image is genuinely on its way (status === 'loading', reflected as data-pending). Someone who simply has no picture is not waiting on anything, so their mark holds still rather than pulsing for as long as it's on screen. The pulse matches skeleton.tsx's timing and drops to none under prefers-reduced-motion.

user-button.view.tsx is comments only — it still computes and passes initials(name), so the name is there for anyone who styles the slot back into view.

Swingset gets a Pending example. /api/pending-image holds its response for a minute with cache-control: no-store, so the pulse is the component's own status rather than a pinned attribute.

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:

Avatar.Fallback still takes children, but it no longer paints them: an
entity without a picture reads as an absent image rather than as two
letters. The children move into an `avatar-fallback-content` slot held at
`visibility: hidden`, which takes them out of the page, the accessibility
tree, and the tab order at once while leaving hit-testing alone, so a
consumer can style them back into view.
The fallback pulses only while an image is genuinely resolving
(`data-pending`); someone who has no picture is not waiting on anything,
so their mark holds still.
@changeset-bot

changeset-botBot commented Aug 11, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: d013999

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

@vercel

vercelBot commented Aug 11, 2026

Copy link
Copy Markdown

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

ProjectDeploymentActionsUpdated (UTC)
clerk-js-sandboxReadyReadyPreviewAug 11, 2026 6:11pm
swingsetReadyReadyPreviewAug 11, 2026 6:11pm

Request Review

@pkg-pr-new

pkg-pr-newBot commented Aug 11, 2026

Copy link
Copy Markdown

Open in StackBlitz

@clerk/astro

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

@clerk/backend

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

@clerk/chrome-extension

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

@clerk/clerk-js

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

@clerk/electron

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

@clerk/electron-passkeys

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

@clerk/eslint-plugin

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

@clerk/expo

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

@clerk/expo-google-signin

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

@clerk/expo-passkeys

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

@clerk/express

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

@clerk/fastify

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

@clerk/hono

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

@clerk/localizations

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

@clerk/nextjs

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

@clerk/nuxt

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

@clerk/react

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

@clerk/react-router

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

@clerk/shared

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

@clerk/tanstack-react-start

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

@clerk/testing

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

@clerk/ui

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

@clerk/upgrade

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

@clerk/vue

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

commit: d013999

@github-actions

github-actionsBot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

API Changes Report

Generated by Break Check on 2026-08-11T18:13:20.778Z

Summary

MetricCount
Packages analyzed19
Packages with changes0
🔴 Breaking changes0
🟡 Non-breaking changes0
🟢 Additions0

No API Changes Detected

All packages have stable APIs with no detected changes.


Report generated by Break Check

Last ran on d013999.

@coderabbitai

coderabbitaiBot commented Aug 11, 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: 528247a8-b031-4843-a13b-d341dea903a3

📥 Commits

Reviewing files that changed from the base of the PR and between 207eecd and d013999.

📒 Files selected for processing (6)
  • packages/swingset/src/app/api/pending-image/route.ts
  • packages/swingset/src/stories/avatar.stories.tsx
  • packages/ui/src/mosaic/components/avatar/avatar.styles.ts
  • packages/ui/src/mosaic/components/avatar/avatar.test.tsx
  • packages/ui/src/mosaic/user-button/__tests__/user-button.view.test.tsx
  • packages/ui/src/mosaic/user-button/user-button.view.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)
  • clerk/cli(auto-detected)
  • clerk/clerk-ios(auto-detected)
  • clerk/clerk-android(auto-detected)
💤 Files with no reviewable changes (1)
  • packages/swingset/src/stories/avatar.stories.tsx
🚧 Files skipped from review as they are similar to previous changes (5)
  • packages/ui/src/mosaic/user-button/tests/user-button.view.test.tsx
  • packages/ui/src/mosaic/components/avatar/avatar.test.tsx
  • packages/ui/src/mosaic/components/avatar/avatar.styles.ts
  • packages/ui/src/mosaic/user-button/user-button.view.tsx
  • packages/swingset/src/app/api/pending-image/route.ts

📝 Walkthrough

Walkthrough

Avatar fallbacks now hide fallback content by default and wrap it in a dedicated content slot. The fallback exposes pending state while an image request remains unresolved, applies a pulse animation, and stops pulsing after an error. Tests cover pending, error, and no-source states. A pending-image endpoint and story demonstrate the behavior. Avatar documentation and user-button comments were updated. An empty Changeset file was added.

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

Suggested reviewers:maxyinger

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 28.57% 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 nameStatusExplanation
Title check✅ PassedThe title clearly summarizes the main Avatar fallback behavior change.
Description check✅ PassedThe description accurately explains the hidden fallback content, pending pulse behavior, tests, and example changes.
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.

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

@coderabbitaicoderabbitaiBot 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 @.changeset/mosaic-avatar-skeleton-fallback.md:
- Around line 1-2: Add an `@clerk/ui` changeset entry in
mosaic-avatar-skeleton-fallback.md with the appropriate release bump and a
concise release note describing the updated default Avatar.Fallback output; do
not leave the changeset empty.
In `@packages/swingset/src/app/api/pending-image/route.ts`:
- Around line 17-29: Add unit tests covering the GET route’s delayed response
contract: use fake timers to verify normal resolution after HOLD_MS and
request-signal cancellation before the delay completes, and assert both PNG
content type and no-store cache-control headers for the returned Response.
In `@packages/ui/src/mosaic/components/avatar/avatar.styles.ts`:
- Around line 5-7: Reduce comments across the listed sites to only terse
non-obvious rationale: in avatar.styles.ts lines 5-7 and 53-58, replace each
explanation with one concise rationale line, and remove lines 63-64; remove
narration at avatar.test.tsx lines 16-18, 228-230, and 261-262; shorten the
accessibility and avatarName comments in user-button.view.tsx lines 135-136 and
186-188, remove lines 157-158 and 683, and remove narration in
user-button.view.test.tsx lines 653-654; shorten the route-purpose and cache
comments in pending-image/route.ts lines 1-4 and 13-14, remove line 7; and
remove the story narration in avatar.stories.tsx lines 49-50. Preserve code
behavior and leave comments only where they explain non-obvious rationale.
🪄 Autofix

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: ea7ed4b3-ea6a-424f-80b8-5477ae8450cd

📥 Commits

Reviewing files that changed from the base of the PR and between 3132d40 and 07a07ba.

📒 Files selected for processing (9)
  • .changeset/mosaic-avatar-skeleton-fallback.md
  • packages/swingset/src/app/api/pending-image/route.ts
  • packages/swingset/src/stories/avatar.mdx
  • packages/swingset/src/stories/avatar.stories.tsx
  • packages/ui/src/mosaic/components/avatar/avatar.styles.ts
  • packages/ui/src/mosaic/components/avatar/avatar.test.tsx
  • packages/ui/src/mosaic/components/avatar/avatar.tsx
  • packages/ui/src/mosaic/user-button/__tests__/user-button.view.test.tsx
  • packages/ui/src/mosaic/user-button/user-button.view.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)
  • clerk/cli(auto-detected)
  • clerk/clerk-ios(auto-detected)
  • clerk/clerk-android(auto-detected)

Comment thread.changeset/mosaic-avatar-skeleton-fallback.md
Comment threadpackages/swingset/src/app/api/pending-image/route.ts
Comment threadpackages/ui/src/mosaic/components/avatar/avatar.styles.ts Outdated
@alexcarpenter
alexcarpenter merged commit a9099bc into mainAug 11, 2026
52 checks passed
@alexcarpenter
alexcarpenter deleted the carp/avatar-skeleton-fallback branch August 11, 2026 20:32
Sign up for freeto 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.

3 participants

@alexcarpenter@maxyinger@austincalvelage