lightningcss
lightningcss copied to clipboard
lowering for `oklch` does not work when using numeric or mixed values
Currently, lightningcss lowers the following correctly (albeit with an unnecessary conversion from oklch to lab):
--color-1: oklch(100% none none);
--color-2: oklch(100% 0 0);
Whereas the following are ignored completely:
--color-3: oklch(1 0 0);
--color-4: oklch(100% 0% 0deg);
See playground.
I came across a similar issue (#445) which was closed by https://github.com/parcel-bundler/lightningcss/commit/835e5bc68da7ecc7c2e59b20617eed56465f9045, but it doesn't work in the latest version.
Aside: should the extra conversion from oklch to lab be considered a bug?
There could be some slight differences between the color formats. I noticed a difference in precision too. For example, oklch(99.06% 0.002 247.84) becomes lab(98.9092% -.247002 -.70672).
Id also like to understand the conversion from oklch to lab, was expecting to still see oklch in the final css 🤓
That commit was reverted in 81cd955 . At some point the spec changed to accept numbers or percentages for all components instead of one or the other. Unfortunately this is a breaking change for Lightning CSS because it changes the way calcs work. There is a PR #465 to update to the latest spec, but it is now waiting for a major version release unfortunately. In the meantime, you can use percentages.
As for the conversion to lab, that's because you have specified browser targets that do not support oklch but do support lab. According to MDN, oklch is only supported in Chrome 111 and Firefox 113, but the targets in the playground are set to 110 for both.
Thanks for the response. That's unfortunate about the breaking change, but using percentages is not a big deal I suppose.
I'm not sure that the reasoning for the lab issue is correct. lab and oklch have equal browser support, afaict (see caniuse). Even if I lower the targets some more, lab is still used (see playground). The fallback for older browsers is not the lab but rather the hex colors. So the correct combination would be hex (fallback) + oklch (untransformed).
That's true. I guess Safari was the difference. 15 supported lab and 15.4 added support for oklab. In this case you aren't targeting any Safari browsers so it could probably be avoided.
Tailwind CSS v4 colors now use oklch colors by default and they are not being lowered because they use the decimal syntax and not the percentage syntax.
Hi, I'm using TailwindCSS v4 too and like @nicksrandall said, it doesn't work because TailwindCSS use decimal syntax but I also discovered that lightningcss is taking the oklch color as a function instead when it use decimals. So, I found a temporary solution by creating a custom visitor to transform the function into a color as it should be.
// tailwind.polyfill.ts
import {
type CustomAtRules, type Function, type ReturnedDeclaration, type TokenOrValue, type Visitor,
} from "lightningcss";
function transformFunctionIntoColor(tokenOrValue: TokenOrValue & { type: "function"; value: Function }): TokenOrValue {
let [l, c, h, alpha] = tokenOrValue.value.arguments
.filter((arg): arg is TokenOrValue & { value: { type: "number"; value: number } } => arg.type === "token" && arg.value.type === "number")
.map(arg => arg.value.value);
l ??= 0;
c ??= 0;
h ??= 0;
alpha ??= 1;
const oklchColor: TokenOrValue = {
type: "color",
value: {
type: "oklch",
l, c, h, alpha,
},
};
return oklchColor;
}
/**
* Fix oklch colors which are detected as functions instead of colors.
*/
export const FixOklchColorsVisitor: Visitor<CustomAtRules> = {
Declaration(declaration): ReturnedDeclaration | ReturnedDeclaration[] | void {
let needsUpdate = false;
if (declaration.property === "custom") {
for (let index = 0; index < declaration.value.value.length; index++) {
const tokenOrValue = declaration.value.value[index];
if (tokenOrValue?.type === "function" && tokenOrValue.value.name === "oklch") {
declaration.value.value[index] = transformFunctionIntoColor(tokenOrValue);
needsUpdate = true;
}
}
}
if (needsUpdate) {
return declaration;
}
},
};
Now, the visitor have to be added in the lightningcss options along other visitors. In my case, I'm using Astro, so I added into the vite css options.
import tailwindcss from "@tailwindcss/vite";
import { defineConfig } from "astro/config";
import { FixOklchColorsVisitor } from "./tailwind.polyfill";
import { composeVisitors } from "lightningcss";
export default defineConfig({
// ...
vite: {
css: {
transformer: "lightningcss",
lightningcss: {
// ...
visitor: composeVisitors([
// other visitors
FixOklchColorsVisitor, // Here with the others visitors
]),
},
},
plugins: [tailwindcss()],
},
});