What the numbers mean.
How each tool works, what its numbers mean, and where your data goes. There is no account to make.
01
Where your data goes
The tools differ, so this is worth stating plainly rather than in a single reassuring sentence.
The forge is entirely local. Ramp generation, contrast, format conversion and export serialization all run in the page. Your working color is kept in your browser's own storage so it survives a reload and follows you between tools. It never reaches a server.
Extract, Images and Speed cannot be local. A browser is not allowed to fetch another site's HTML — the same-origin policy exists precisely to stop that. So the URL you enter is sent to this app's server, which fetches the page on your behalf and returns what it found. Speed additionally passes the URL to Google's PageSpeed Insights API, which loads the page in its own infrastructure.
Nothing is stored server-side, there is no account and there is no analytics. What the server does keep, in memory only, is a per-IP request count for rate limiting, which resets every minute.
Those endpoints refuse private and loopback addresses, cloud metadata endpoints and non-HTTP schemes, and they re-check the destination on every redirect hop. A URL-fetching endpoint that skips those checks is a way to read the inside of the network it runs on.
02
How a ramp is built
Your color is converted to HSL, and its hue and saturation are held constant while lightness is distributed evenly from 97 down to 6. Each of those lightness values becomes one stop.
Saturation is then pulled back at the two ends. Without that, tints read chalky and shades read muddy — an artifact of HSL, where a fully saturated color at 95% lightness is nearly white but still carries all its chroma. The attenuation is proportional, so a desaturated input stays desaturated.
Finally, the stop whose lightness sits closest to your color's own is replaced by your color exactly and marked BASE. The ramp runs through the color you picked rather than near it, so the hex you brought in is one of the hexes you take out.
03
Step names
At eleven stops the names are the familiar ladder: 50 100 200 … 900 950. That is the convention Tailwind and most design systems use, so the output drops in without renaming.
At any other count the names are interpolated onto a 25-grid and forced strictly increasing, so no two stops can collide. Eleven is the default because it is what most systems expect.
04
OKLCH is computed, not approximated
The oklch() values here come from the real transform: sRGB is linearized, mapped to LMS cone response, cube-rooted, and converted to Oklab before going polar. Round-tripping a color through OKLCH and back returns the original 8-bit value exactly.
This matters because OKLCH output gets pasted into production stylesheets. A close-enough approximation shifts hue in ways that are invisible on one swatch and obvious across a ramp.
OKLCH is worth using because its lightness axis is perceptually uniform. Two colors with the same L look equally light, which is not true of HSL.
05
Reading the contrast figures
Every stop is measured against both white and black using WCAG 2.1 relative luminance. The Ratio column reports the better of the two, and Text on tells you which foreground that was — so the row reads directly as an instruction.
AA— 4.5:1. Body text. The threshold most audits check.AA+— 3:1. Text at 18pt and above, or 14pt bold. Also the floor for interface components and graphical objects.AAA— 7:1. Enhanced. Required for some public-sector work.
These use relative luminance, not perceptual lightness. The two disagree, sometimes sharply, and only relative luminance is normative for a conformance claim. A ramp that looks evenly spaced in OKLCH will still have uneven contrast ratios, which is exactly why both are shown.
One consequence is worth knowing: if you are free to choose black or white text, AA is unfailable. The worst case anywhere in sRGB is 4.583:1, still above the 4.5 threshold. So an “stops meeting AA” count is always the whole ramp and tells you nothing — which is why the summary readouts here report AAA instead. The per-stop AA column stays, because a reviewer will ask for it.
What that floor does not promise is a specific foreground. Black text and white text pass independently, and the Text on column names the one that works. If your design system is committed to a single text color, read that column rather than the ratio.
06
Formats
CSS variables writes custom properties on :root with each stop's contrast in a trailing comment. Tailwind v4 writes an @theme block, which generates the whole utility set for the ramp. SCSS gives flat variables plus a map. JSON carries every format and every contrast figure, for token pipelines.
All four are previewable and copyable without downloading. Filenames include the base color, so exporting two ramps never overwrites the first.
07
Keyboard
Ctrl K (or Cmd K) opens the switchboard from anywhere in the workbench: it lists all four tools and the reading pages, arrow keys move, Enter opens, Escape closes. The rail down the left does the same thing with the mouse.
Every control is reachable by tab. The channel faders take arrow keys for single steps and Page Up / Page Down for larger jumps. Focusing a ramp stop reports it in the readout above the bar, and Enter or Space copies it — the focus ring on a stop is drawn in that stop's own readable text color, so it is visible on any color the ramp lands on.
Typing a hex value commits on Enter or on blur. An invalid entry stays in the field with an explanation rather than being silently discarded.
08
Reading a site from its CSS
Extract fetches the URL you give it, then up to eight of the stylesheets that page links, and harvests every declaration it can find: color literals with the properties they were declared for, font families, font sizes, spacing, radii, shadows, media-query widths and CSS custom properties.
Colors run through the same engine as the forge, so the contrast figures mean exactly what they mean everywhere else, and any swatch can be sent straight to the forge to become a ramp.
The limit that matters: it does not run JavaScript. A site that ships real CSS comes back rich. A site that styles itself at runtime comes back thin, and no amount of parsing fixes that — the styles do not exist until the script runs. That is what the browser extension is for.
Counts are declaration frequency in the stylesheets, not visual prominence. A utility framework inflates the count of colors that may never appear on screen, so read the ranking as a hint rather than a verdict.
09
Auditing a page’s assets
The image tool pulls sources from img src, srcset, picture and source, video posters, icon links, Open Graph and Twitter card images, and url() references in inline styles — resolves them all to absolute URLs, then asks each server how big the file is.
Up to 28 files are weighed, six at a time. That ceiling is deliberate: the tool is a guest on someone else's server and should not behave like a load test. Files past the limit are listed without a size.
A missing alt attribute and an empty alt="" are reported differently, because they mean different things. Empty is a valid, deliberate choice for decorative images. Missing is not a choice.
Some servers answer without a content-length, usually because the response is compressed or streamed. Those show no size rather than a guess.
10
PageSpeed Insights
This is a real PageSpeed Insights run, not an approximation. Google loads the page in Lighthouse on its own infrastructure and returns the four category scores, the lab metrics, and Chrome UX Report field data when it has enough real-user traffic for that origin.
Lab metrics are one synthetic load on Google's hardware. Field metrics are what actual visitors experienced over 28 days. When they disagree, the field data is the truth about your users and the lab data is the truth about your page — both are worth having, which is why both are shown side by side.
Treat PAGESPEED_API_KEY as required rather than optional. Without it, requests go against a single global quota that Google shares between every keyless caller in the world, and in practice that quota is usually already exhausted — you will see HTTP 429 most of the time. A key is free from the Google Cloud console, stays on the server, and is never sent to the browser.
The page has to be reachable from the public internet. Google cannot load your localhost.
11
Reading the rendered page
The extension exists for the one thing the server-side reader cannot do: run the page. It injects a collector into the tab you are looking at and reads getComputedStyle on the rendered tree, which is the ground truth — whatever the framework did, this sees the result.
It measures visible, non-zero-sized elements only, skips fully transparent color values as browser defaults rather than decisions, and resolves each custom property to the value the root element actually gives it.
It asks for activeTab and scripting and nothing else, so it can read one tab, only when you click the icon, only until you navigate away. Nothing is transmitted.
What it reads is this route, at this viewport, in this theme. A different breakpoint or a dark-mode toggle produces a different document. That is correct behaviour, and worth remembering before treating one read as a whole design system.