Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .changeset/mosaic-provider-logo.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
---
---
12 changes: 7 additions & 5 deletions .claude/skills/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,10 @@ relevant.

## Skills in this repo

| Skill | Use it for |
| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
| `clerk-monorepo` | Day-to-day work in the monorepo: setup, build/test loops, the package map, changesets, commits, PRs, breaking-change checks. |
| `mosaic` | Mosaic flow UI: authoring machines, controllers, and views, and migrating a legacy component into the split (with parity verification). |
| `verify-clerk-expo` | Proving a `@clerk/expo` change on an iOS simulator or Android emulator, local or borrowed on a CI runner, with video and screenshots as evidence. |
| Skill | Use it for |
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
| `clerk-monorepo` | Day-to-day work in the monorepo: setup, build/test loops, the package map, changesets, commits, PRs, breaking-change checks. |
| `mosaic` | Mosaic flow UI: authoring machines, controllers, and views, and migrating a legacy component into the split (with parity verification). |
| `verify-clerk-expo` | Proving a `@clerk/expo` change on an iOS simulator or Android emulator, local or borrowed on a CI runner, with video and screenshots as evidence. |
| `provider-logo-add` | Adding a logo for a new provider to Mosaic's `ProviderLogo`. |
| `provider-logo-update` | Checking provider logos against each brand's official assets and refreshing outdated ones. |
35 changes: 35 additions & 0 deletions .claude/skills/provider-logo-add/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
---
name: provider-logo-add
description: >-
Add a logo for a new OAuth, Web3, or phone-code provider to Mosaic's
`ProviderLogo` (the bundled provider logo set). Use when a provider is
added to Clerk, or when asked to add a logo for a provider that
`ProviderLogo` does not support yet. To refresh an existing logo, use
`provider-logo-update`.
---

# Adding a provider logo

Read `../provider-logo-update/references/logo-pipeline.md` first. It covers where the files
live, the manifest, the three treatments, sourcing rules, SVG cleanup, verification, and
changesets.

## Workflow

1. **Confirm the id.** It must match Clerk's provider id: `provider` in
`packages/shared/src/oauth.ts` or `packages/shared/src/web3.ts`, or `channel` in
`packages/shared/src/alternativePhoneCode.ts`. If the provider is not there yet, say so: adding
it to `@clerk/shared` is a separate change with its own changeset, and the backend has to
support it. Don't add it to shared as part of this skill unless asked.
2. **Find the brand page** and the official symbol SVG, per the sourcing rules. Read its guidance
for dark backgrounds and sign-in buttons.
3. **Pick a treatment** and explain the choice in one line to the user, citing the brand guidance.
4. **Add the files:**
- `packages/mosaic/src/components/provider-logo/logos/<id>.svg`, cleaned per the reference.
- A manifest entry with `id`, `name`, `treatment`, `groups` (every group whose lookup should
resolve it, keyed by that group's provider ids), `colors` if adaptive or recolored, `brand`,
`source` if anything is non-obvious, and `checked` set to today. Place it next to providers
from the same shared list, in that list's order.
5. **Generate and verify** per the reference. Report the gzip size change, since every bundle that
imports one of the logo's groups carries it.
6. **Changeset** per the reference.
40 changes: 40 additions & 0 deletions .claude/skills/provider-logo-update/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
---
name: provider-logo-update
description: >-
Check Mosaic's provider logos (Google, GitHub, Apple, Discord, etc., used by
`ProviderLogo`) against each brand's official assets and update any that are
outdated, broken, or handled wrong in dark mode. Use when asked to refresh,
audit, or check for updated provider/OAuth/social logos, or to fix how one
provider's logo looks. For a provider that has no logo yet, use
`provider-logo-add`.
---

# Updating provider logos

Read `references/logo-pipeline.md` first. It covers where the files live, the manifest, the
three treatments, sourcing rules, SVG cleanup, verification, and changesets.

## Workflow

1. **Scope.** One provider, a list, or all of them. For "all", sort the manifest by `checked`
(oldest first) and fan out the brand checks to parallel subagents, about 10 providers each.
Give each subagent the reference file and the current SVGs to compare against.
2. **Check each provider** against its `brand` URL and report, per provider:
- whether the current mark still matches the official one (shape and colors),
- what the brand says to do on dark backgrounds,
- the recommended treatment, and whether it differs from the manifest,
- the official SVG URL, or why one could not be fetched.
3. **Stop and report before changing anything.** Present a short table of proposed changes and
the providers that could not be verified. Logo choices are brand and product calls; wait for
the user to pick.
4. **Apply** the approved changes: replace `logos/<id>.svg`, update `treatment`, `colors`, and
`source`, and set `checked` to today. Set `checked` on providers you verified as unchanged too.
5. **Verify** per the reference: generate, test, and look at both color schemes.
6. **Changeset** per the reference.

## Judgment calls to surface, not decide

- A brand has moved to a mark that is hard to reproduce as clean SVG (Google's 2025 gradient G).
- A brand rename (Twitter → X, Base App → Coinbase Wallet) where the old id stays.
- A color the brand's site uses but its asset files do not confirm (HubSpot).
- Spotify-style rules that turn a colored mark white on our dark background.
117 changes: 117 additions & 0 deletions .claude/skills/provider-logo-update/references/logo-pipeline.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
# Provider logo pipeline

Shared by `provider-logo-update` and `provider-logo-add`.

## Where things live

All paths are under `packages/mosaic/`.

| Path | What |
| -------------------------------------------------------- | --------------------------------------------------------------------------------- |
| `src/components/provider-logo/logos/<id>.svg` | Source SVG per provider, as downloaded from the brand (hand-trimmed if needed) |
| `src/components/provider-logo/logos/manifest.json` | Source of truth: order, treatment, color swaps, brand URL, provenance, last check |
| `scripts/generate-provider-logos.mjs` | svgo + treatment + JSX codegen |
| `src/components/provider-logo/glyphs/<id>.generated.tsx` | Generated glyph module per logo |
| `src/components/provider-logo/<group>.generated.ts` | Generated map and lookup per group (`oauth`, `enterprise`, `web3`, `phone`) |

Never hand-edit a `*.generated.*` file. Edit the SVG or manifest, then run
`pnpm --filter @clerk/mosaic generate:provider-logos`.

## Manifest entry

```json
{
"id": "atlassian",
"name": "Atlassian",
"treatment": "adaptive",
"groups": { "oauth": ["atlassian"] },
"colors": { "#1868db": "light-dark(#1868DB, #357DE8)" },
"brand": "https://atlassian.design/foundations/logos",
"source": "Paths and colors from @atlaskit/logo.",
"checked": "2026-10-02"
}
```

- `id` names the logo: the SVG filename and the glyph module. Use the Clerk provider id when the
logo belongs to one (`provider` in `packages/shared/src/oauth.ts` or `web3.ts`, `channel` in
`alternativePhoneCode.ts`).
- `groups` lists, per group, the ids a lookup in that group resolves to this logo. Keys are the ids
Clerk uses in that context: OAuth and Web3 providers, phone code channels, and full enterprise
provider ids (`saml_okta`, `oauth_google`, `oidc_gitlab`). A logo in several groups (Google is
`oauth` and `enterprise`) is one module that each group map references.
- `colors` maps a fill **as svgo writes it** (lowercase, shortened hex: `#000`, `#00f`) to its
replacement. The generator throws if a key is not found; its error prints the optimized markup.
- `brand` is the official brand or sign-in-button guideline page. Omit it only when none exists.
- `source` records anything non-obvious about where the asset came from or why it deviates.
- `checked` is the date the entry was last compared against the brand (ISO `YYYY-MM-DD`).

## Choosing a treatment

| Treatment | Use when | Effect |
| ---------- | ----------------------------------------------------------------------------------- | -------------------------------------- |
| `color` | A full-color mark the brand uses on both light and dark backgrounds | Rendered as-is |
| `mono` | The brand's mark is black/white only, or its login button uses a one-color mark | Every fill becomes `currentColor` |
| `adaptive` | The brand names different colors for dark backgrounds, or forbids its color on dark | Listed fills become `light-dark(a, b)` |

Precedence: the brand's sign-in-button guideline beats its general logo page, which beats the
press kit. Apple must resolve to pure black or white (App Review checks custom buttons), so it is
`adaptive` with `light-dark(#000, #fff)`, not `mono`.

The dark Mosaic background is `oklch(0.2046 0 0)`. If a `color` mark is near-invisible there and
the brand allows a white version, make it `adaptive`.

## Sourcing an asset

1. Open the manifest `brand` URL (WebFetch, or a browser for JS-rendered pages). Look for the
**symbol/icon mark**, not the wordmark, unless the brand has no symbol (Box).
2. Prefer a direct official SVG. Zips are common: download and unzip in the scratchpad.
3. Fallbacks, in order: the brand's own npm package (`@atlaskit/logo`), its developer docs' login
button kit, then [simple-icons](https://simpleicons.org) (CC0, one-color marks only). Record
the fallback in `source`.
4. Never redraw, recolor beyond the brand's own palette, or trace a raster. If only PNG exists,
stop and report it.

Pages that commonly block fetching: X (402), Coinbase (403), Meta brand zips, Okta (partner
login), HubSpot (Brandfolder form), Notion (redirects to app login). Report these rather than
guessing.

## Cleaning an SVG

The generator throws on `<image>`, `<style>`, `<script>`, `<foreignObject>`, and text content.

- **Figma exports:** check fills are real colors. A `fill="{&quot;type&quot;:&quot;GRADIENT_…`
value renders as nothing (this is how the old Instagram asset broke).
- **Background tiles:** remove full-bleed background rects the brand does not include (the old
Microsoft asset had a `#F3F3F3` square that showed as a gray box in dark mode).
- **viewBox:** keep it. Non-square boxes are centered into a square automatically. Trim excess
padding so the mark's optical size matches its neighbors.
- **Gradients and clip paths** are fine; ids are scoped per instance by the generator.

## Verifying

1. `pnpm --filter @clerk/mosaic generate:provider-logos`. It prints the gzip size of all logos;
note the before/after. A single logo over ~1.5KB gzip deserves a second look, since every bundle
that imports its group carries it.
2. `pnpm --filter @clerk/mosaic exec vitest run --project mosaic src/components/provider-logo`.
3. Look at swingset `/components/provider-logo`: the Color schemes example shows every logo on
light and dark. Check the changed logo at `sm`, `md`, and `lg` against its neighbors. Swingset
runs on 6006 (`pnpm run dev:swingset`); use another port if a second worktree holds it. Ask the
user before driving a browser for screenshots.

## Bundle size

`ProviderLogo` renders whatever glyph it is handed, so it pulls in no logos itself. A feature pays
only for the group maps it imports. Minified and gzipped: `oauth` ~6.3KB, `enterprise` ~1.6KB,
`web3` ~1.5KB, `phone` ~0.6KB. `enterprise` shares Google, Microsoft, GitHub and GitLab with
`oauth`, so a bundle with both adds only Okta.

Loading only the logo a screen needs after discovering which one it is was rejected: the extra round
trip costs more than the bytes it saves. The group modules are the unit for the planned
per-feature code splitting, so a split chunk carries only the groups its feature imports.

## Changeset

Follow `AGENTS.md`. If `ProviderLogo` is reachable from a published entry point
(`packages/mosaic/src/index.ts`, directly or through an exported feature), write a
`@clerk/mosaic` changeset describing the visible change ("Updated the Linear logo to the current
mark"). Otherwise use `pnpm changeset:empty`.
2 changes: 2 additions & 0 deletions packages/mosaic/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
"dev": "tsdown --config tsdown.config.mts --watch",
"format": "node ../../scripts/format-package.mjs",
"format:check": "node ../../scripts/format-package.mjs --check",
"generate:provider-logos": "node ./scripts/generate-provider-logos.mjs",
"lint": "eslint src",
"lint:attw": "attw --pack . --exclude-entrypoints styles.css --profile esm-only",
"lint:publint": "publint",
Expand Down Expand Up @@ -88,6 +89,7 @@
"qrcode.react": "4.2.0",
"react": "catalog:react",
"react-dom": "catalog:react",
"svgo": "^3.3.5",
"tsdown": "catalog:repo",
"vitest-axe": "^0.1.0"
},
Expand Down
Loading
Loading