Repository navigation
feat: fitted colour model (Tailwind v4 fit) as the new default, plus chroma lab - #6
Closed
nekomeowww wants to merge 5 commits into
Closed
nekomeowww wants to merge 5 commits into
nekomeowww wants to merge 5 commits into
Conversation
Visualizes how chroma and lightness are distributed over hue and shade: chroma / lightness by hue and by shade against the sRGB and P3 gamut, residual heatmaps, a 3D surface, side-by-side palettes, and ΔE against the Tailwind CSS v4 palette with leave-one-family-out cross-validation. Includes the least-squares fit to Tailwind v4 the `fitted` model is generated from, with live controls, and copies the range / field-range components of moeru-ai/airi packages/ui for the controls. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`fitted` varies lightness, chroma and a small hue drift smoothly with hue, fitted to the Tailwind CSS v4 palette. Around yellow it targets a share of the sRGB gamut instead, so light shades do not turn neon. Chroma is fitted as a share of sRGB and then refitted against the exact gamut, which keeps cyan / sky 400–500 vivid. `chromaticColorFrom`, `chromaticPaletteFrom`, `themeFrom`, `colorBy` and `chromaticFrom` accept `model`; `classic` keeps the previous colours. Coefficients are generated with `pnpm generate:fitted` (`--check` to verify they are up to date). BREAKING CHANGE: the default model is now `fitted`. Pass `model: 'classic'` to keep the previous colours. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
With `model: 'fitted'` (now the default) the preflight emits, per colour, the Fourier basis of its hue and the lightness / chroma / hue drift of each shade as custom properties; baked colours use the values of `@proj-airi/chromatic`. A test evaluates the generated CSS and compares it with `fittedOklch`. BREAKING CHANGE: the default model is now `fitted`, which does not emit `--chromatic-chroma` / `--chromatic-chroma-<shade>`. Pass `model: 'classic'` to keep the previous colours and variables. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
✅ Deploy Preview for proj-airi-chromatic ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
Adds vue-router: `/` is the docs page, `/lab` the chroma lab, both lazy loaded so the docs no longer pay for fitting, heatmaps and the 3D surface. The theme hue is shared through `useThemeHue()` and survives route changes. The three.js views only animate while on screen. Adds a Netlify `_redirects` fallback so `/lab` works on reload. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Member
Author
|
Split into a stack for easier review:
The final tree of #9 is identical to this PR plus the route split and moving the fit to |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Blue / cyan shades looked heavy and dull. The cause was in how shades are derived: each shade has a fixed OKLCH lightness, chroma follows
0.18 + 0.04 · cos(hue), and anything past the sRGB gamut is channel-clipped. Cyan / sky only reach high chroma at high lightness, so ~49% of hues at shade 500 were out of sRGB and clipped into darker colours.This PR adds a
fittedcolour model and makes it the default (model: 'classic'keeps the previous colours):share × exact sRGB maxrefitted per shade with 12 harmonics. This keeps cyan / sky 400–500 vivid, e.g. hue 222.69° shade 400 goes from#23c8ecto#00caf0(Tailwind ≈#00c7fb).Packages
@proj-airi/chromatic:fittedOklch,FITTED_MODEL, and amodeloption onchromaticColorFrom/chromaticPaletteFrom/themeFrom/colorBy/chromaticFrom. Coefficients are generated bypnpm generate:fittedfrom the playground fit.--checkverifies they are up to date.@proj-airi/unocss-preset-chromatic: amodeloption. Withfitted, the preflight emits per colour a sharedcos(k·h)/sin(k·h)basis and per-shade lightness / chroma / hue-drift custom properties. Baked colours use the core values. Modifiers (~,*) keep working. The preset now depends on@proj-airi/chromatic./and a "chroma lab" at/lab(lazy loaded; three.js views only animate while on screen). The lab has chroma / lightness by hue and shade, residual heatmaps, a 3D surface, side-by-side palettes, ΔE against Tailwind with cross-validation, a live fit with controls, and TS / CSS export. Sliders are copied frommoeru-ai/airipackages/ui(range / field-range, MIT, attributed in the files).Breaking changes
fitted, so every colour changes slightly. Shades 50 / 100 are no longer mixed with white.fitteddoes not emit--chromatic-chroma/--chromatic-chroma-<shade>. Code that reads them directly should switch to utilities with~/*modifiers, or tochromaticColorFrom(hue, { shade, saturation, brightness }). It can also passmodel: 'classic'.Notes
With
fitted, lightness and chroma are computed from--chromatic-hueon:root. A subtree override of--chromatic-huerotates the hue but keeps the root's lightness and chroma, which is whatclassicalready did for chroma.CSS size per colour: ~17 KB raw / 2.4 KB gzip.
Style recalc per hue change with 5000 elements (median of 5):
Simulators run on the host CPU. Real devices are not measured yet.
Registering the variables with
@propertyhelped on WebKit but not on Blink, so it is not used.Longest substituted utility colour is 2.3 KB, ~900× below Chrome's ~2 MB
var()substitution limit.Test plan
pnpm lint,pnpm typecheck,pnpm buildpnpm test:run: 26 tests pass.model: 'classic'.fittedOklchfor 3 colours × 5 hues × 11 shades.pnpm generate:fitted --check🤖 Generated with Claude Code