diff --git a/README.md b/README.md index dbbd96d..45b6461 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,8 @@ Brings OKLCH to Tailwind and introduces these helpful utilities: - Match utilities that will match the color of a property with another property (e.g. outline color matches the background color), with support for opacity modifiers. - Lightness offset utilities that darken or lighten colors (e.g. on hover). +You don't need to change your theme colors, since this plugin uses color.js to convert all existing colors. + ## Installation To use this package, install it via npm: @@ -54,11 +56,14 @@ The `text-lightness-offset-10` increases the lightness of the text with 10%. Oth If you prefer to change the threshold when the contrast color switches to black or white, you can adjust the value of `contrastThreshold`. This value should be somewhere between `0` and `1`. The higher the number, the more likely white will be chosen as the contrast color. While I was working on this plugin, I noticed that a contrast threshold of `.6` looked better on different screens than `.5`, since dark colors seem to have lesser contrast then lighter colors in reality. +Precision was added since color.js uses floats to calculate the OKLCH values, which can result in long numbers. Defaults to `6`. + ```js /** @type {import('tailwindcss').Config} */ module.exports = { plugins: [require('tailwindcss-oklch')({ contrastThreshold: .5, + precision: 8, })], } ``` diff --git a/examples/dist/main.css b/examples/dist/main.css index 11d1dc9..7879157 100644 --- a/examples/dist/main.css +++ b/examples/dist/main.css @@ -817,9 +817,9 @@ video { } .bg-blue-600 { - --tw-bg-l: 0.5461497233109911; - --tw-bg-c: 0.21520774805039064; - --tw-bg-h: 262.88091714087415; + --tw-bg-l: 0.54615; + --tw-bg-c: 0.215208; + --tw-bg-h: 262.880917; --tw-bg-l-offset: 0; --tw-bg-opacity: 1; background-color: oklch(clamp(0, calc(var(--tw-bg-l) + var(--tw-bg-l-offset)), 1) var(--tw-bg-c) var(--tw-bg-h) / var(--tw-bg-opacity)); @@ -864,9 +864,9 @@ video { } .text-blue-600 { - --tw-text-l: 0.5461497233109911; - --tw-text-c: 0.21520774805039064; - --tw-text-h: 262.88091714087415; + --tw-text-l: 0.54615; + --tw-text-c: 0.215208; + --tw-text-h: 262.880917; --tw-text-l-offset: 0; --tw-text-opacity: 1; color: oklch(clamp(0, calc(var(--tw-text-l) + var(--tw-text-l-offset)), 1) var(--tw-text-c) var(--tw-text-h) / var(--tw-text-opacity)); @@ -902,9 +902,9 @@ video { } .hover\:bg-blue-400:hover { - --tw-bg-l: 0.7137400464544268; - --tw-bg-c: 0.14338050936284802; - --tw-bg-h: 254.62402136543372; + --tw-bg-l: 0.71374; + --tw-bg-c: 0.143381; + --tw-bg-h: 254.624021; --tw-bg-l-offset: 0; --tw-bg-opacity: 1; background-color: oklch(clamp(0, calc(var(--tw-bg-l) + var(--tw-bg-l-offset)), 1) var(--tw-bg-c) var(--tw-bg-h) / var(--tw-bg-opacity)); @@ -919,9 +919,9 @@ video { } .hover\:text-blue-800:hover { - --tw-text-l: 0.42444500037043004; - --tw-text-c: 0.18086883857590272; - --tw-text-h: 265.63771048282496; + --tw-text-l: 0.424445; + --tw-text-c: 0.180869; + --tw-text-h: 265.63771; --tw-text-l-offset: 0; --tw-text-opacity: 1; color: oklch(clamp(0, calc(var(--tw-text-l) + var(--tw-text-l-offset)), 1) var(--tw-text-c) var(--tw-text-h) / var(--tw-text-opacity)); diff --git a/index.js b/index.js index 2382935..5d056ec 100644 --- a/index.js +++ b/index.js @@ -12,7 +12,7 @@ const propertyColors = Object.fromEntries(utilities.map(({ key }) => { ]; }).flat()); -export default plugin.withOptions(({ contrastThreshold = 0.6 } = {}) => { +export default plugin.withOptions(({ contrastThreshold = 0.6, precision = 6 } = {}) => { return ({ matchUtilities, theme, corePlugins, addDefaults, }) => { @@ -34,6 +34,13 @@ export default plugin.withOptions(({ contrastThreshold = 0.6 } = {}) => { }, {}); }; + // Round numbers and turn NaN into 0. + // NaN occurs for the hue gray colors, that also have a chroma of 0, + // so we can safely set the hue to 0 instead of NaN. + const round = (value) => { + return (value || 0).toFixed?.(precision).replace(/\.?0+$/, '') || value; + } + matchUtilities( { [key]: (value) => { @@ -45,7 +52,7 @@ export default plugin.withOptions(({ contrastThreshold = 0.6 } = {}) => { catch (error) { // Some values can be keywords like inherit. - // If the color is a oklch function with custom properties (eg. `oklch(var(--color-primary-l) var(--color-primary-c) var(--color-primary-h))`), parse these custom properties. + // If the color is an oklch function with custom properties (eg. `oklch(var(--color-primary-l) var(--color-primary-c) var(--color-primary-h))`), parse these custom properties. const match = colorValue.match(/^oklch\((var\(--[a-z0-9-]+?\)) (var\(--[a-z0-9-]+?\)) (var\(--[a-z0-9-]+?\))(?: \/ (.+))?\)$/i); if (match) { const [, l, c, h, a] = match; @@ -57,13 +64,12 @@ export default plugin.withOptions(({ contrastThreshold = 0.6 } = {}) => { } const result = {}; - // Exclude `transparent` which has a NaN hue value. - if (color && !Number.isNaN(color.oklch.h)) { + if (color?.oklch && colorValue !== 'transparent') { const { oklch, alpha } = color; Object.assign(result, { - [`--tw-${key}-l`]: oklch.l.toString(), - [`--tw-${key}-c`]: oklch.c.toString(), - [`--tw-${key}-h`]: oklch.h.toString(), + [`--tw-${key}-l`]: round(oklch.l), + [`--tw-${key}-c`]: round(oklch.c), + [`--tw-${key}-h`]: round(oklch.h), [`--tw-${key}-l-offset`]: '0', ...(alpha === 1 && { [`--tw-${base}-opacity`]: alpha.toString() }), ...addProperties(`oklch(clamp(0, calc(var(--tw-${key}-l) + var(--tw-${key}-l-offset)), 1) var(--tw-${key}-c) var(--tw-${key}-h) / ${alpha === 1 ? `var(--tw-${base}-opacity)` : alpha.toString()})`),