Skip to content

feat: label each poster swatch in its M3 paired color - #217

Draft
abernier wants to merge 10 commits into
mainfrom
ccr-cc0a1c1e-u7azwm
Draft

abernier wants to merge 10 commits into
mainfrom
ccr-cc0a1c1e-u7azwm

Conversation

@abernier

@abernier abernier commented Oct 6, 2026 •

Copy link
Copy Markdown
Owner

What

The poster's labels were white with mix-blend-mode: difference. Each label is now written in the color M3 pairs it with, as Material Theme Builder's own poster does.

All of that logic lives in one file, src/Scheme.ink.ts. The swatches only ask it for a color, in var mode and Tailwind mode alike.

roleInk(role): scheme roles, custom colors included

  • By default, a role's ink is its on- counterpart, and the reverse: primary ↔ on-primary, x-container ↔ on-x-container, background ↔ on-background, my-custom-color-1 ↔ on-my-custom-color-1, and so on.
  • A small table covers the exceptions, where the spec pairs a role with the one it's meant to be read against:
    • *-fixed and *-fixed-dim → on-*-fixed; on-*-fixed-variant → *-fixed
    • the surfaces → on-surface; on-surface-variant and outline → surface; outline-variant → on-surface
    • inverse-surface ↔ inverse-on-surface; inverse-primary → inverse-surface
    • surface-tint → on-primary
    • scrim and shadow (black in both themes) → --md-ref-palette-neutral-100

toneInk(palette, tone): palette tones

Tones up to 50 use palette tone 100, lighter tones use tone 10. These are the tones of on-primary (100 on 40) and on-primary-container (10 on 90). The cut at 50 keeps every pair at about 4.5:1 or above.

Tailwind mode, merged into the components

The Tailwind story used to re-draw the custom colors and shades by hand, because Scheme and Shades couldn't paint them with utilities. Now the components do it themselves, and the story is just <Scheme tw> + <Shades tw>:

  • <Scheme tw> paints the custom colors with their utilities too (bg-myCustomColor1, bg-on-myCustomColor1-container, …).
  • <Shades tw> (new prop) paints the plugin's eleven Tailwind shades (bg-primary-500, …) instead of every tone.
  • Class generation: both sets of names are built at runtime, which no scanner sees, so globals.css (in our marked block) lists them with @source inline(). The tw prop docs give consumers the snippet.
  • <Scheme tw> also paints primary, secondary and background with bg-[var(--md-sys-color-<role>)], the README's escape hatch. These are the three names shadcn claims too:
    • bg-secondary landed on secondary-container;
    • without shadcn.css, all three landed on shadcn's own colors.

I checked this in the Storybook (which has shadcn): all 202 swatches of the Tailwind story have a computed background equal to their --md-sys-color-* / --md-ref-palette-* variable.

Elsewhere

  • Poster no longer sets color or mix-blend-mode on the <p>s it holds.
  • Scheme's custom-color cells are now one loop over their four roles, instead of four copied blocks.

Net effect: about −420 lines.

Checks

pnpm run lgtm passes (build, lint, format, exports, typecheck, tests), with no Tailwind warning. I checked the Custom colors, Dropped roles and Tailwind stories visually in light and dark.

🤖 Generated with Claude Code

https://claude.ai/code/session_01HnjGFvhGSxgzZZiU33Wiko

claude added 4 commits October 6, 2026 08:08
Instead of white blended by difference, each role's label is written in
the role M3 pairs it with (on-primary on primary, primary on on-primary,
on-surface on surfaces...), as Material Theme Builder's poster does.
Custom colors too, including the Tailwind story's hand-written ones.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HnjGFvhGSxgzZZiU33Wiko
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HnjGFvhGSxgzZZiU33Wiko
As M3 inks its roles (on-primary 100 on 40, on-primary-container 10 on
90): tone 100 on tones up to 50, tone 10 above. With every poster label
now inked, Poster drops its white + difference fallback, so the Tailwind
story's custom colors use text-* utilities instead of inline styles.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HnjGFvhGSxgzZZiU33Wiko
Each shade in its palette's 50 on 500 and darker, its 900 above -- as
Shades does with tones 100 and 10, Tailwind having no tone 100. Its
labels were the page's dark text, unreadable on the dark half.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HnjGFvhGSxgzZZiU33Wiko
@abernier abernier changed the title feat: label each Scheme swatch in its M3 paired color feat: label each poster swatch in its M3 paired color Oct 6, 2026
claude and others added 6 commits October 6, 2026 08:18
Next to shadcn, whose @theme inline outranks the plugin on the name,
bg-secondary lands on secondary-container. Use the README's way out,
bg-[var(--md-sys-color-secondary)], which paints the M3 role with or
without shadcn.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HnjGFvhGSxgzZZiU33Wiko
The label colors are dressing: gather them in one module -- roleInk()
for scheme roles (the on- counterpart, or back, plus the spec's
exceptions), toneInk() for palette tones -- so the swatches only ask it.

Custom-color cells, in Scheme and in the Tailwind story, become one loop
over their four roles; the story's 88 hand-written shades a table of
utilities, inked with toneInk() rather than per-cell text-* classes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HnjGFvhGSxgzZZiU33Wiko
primary, secondary and background are the names shadcn's @theme inline
takes over from the plugin: without shadcn.css, all three land on
shadcn's own colors. bg-[var(--md-sys-color-*)] paints the M3 role
whatever else is installed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HnjGFvhGSxgzZZiU33Wiko
Tailwind scans the repo, and a literal bg-[var(--...-*)] in the
changeset became an invalid utility, warned about at build.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HnjGFvhGSxgzZZiU33Wiko
The Tailwind story re-drew the custom colors and the shades by hand,
because Scheme and Shades could not paint them with utilities. Now
Scheme's tw mode covers the custom colors (bg-on-myCustomColor1...) and
Shades gets a tw mode painting the plugin's eleven shades
(bg-primary-500...), both inked by Scheme.ink.ts like any swatch.

Their utilities are named at runtime, so globals.css lists them for
Tailwind with @source inline(). The story is now just <Scheme tw> and
<Shades tw>.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HnjGFvhGSxgzZZiU33Wiko
…tilities

Review of #217.

- `Scheme` names each custom role's ink itself: `roleInk()`'s `on-` rule
  mis-inked a custom color named `onSale`, and read `Object.prototype` for
  one named `constructor`. One `Cell` paints every swatch; `Swatch` types it.
- `Shades tw` paints the six core palettes from a literal `shadeClasses`
  table, which the scanner sees in `dist/react.js`, so the `@source inline()`
  safelist shrinks to the custom colors. It follows a `<Scheme tw>`'s
  context when nested, and its tooltips name the utility too.
- `globals.css`: the safelist sits in the marked block, which names it.
- `Scheme.ink.ts`: the table written out, scrim and shadow inked `white`,
  `sysColorVar()` / `refPaletteVar()` shared with the plugin.
- Tests: every ink names a property `toCss()` declares, and the safelist
  generates exactly the utilities the components name at runtime.
- Changeset `minor`, with the `tw` migration; the docs say Tailwind 4.1+.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

This branch has not been deployed

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants