UNPKG

use-css-dom

Version:

React hook to read and convert CSS variables from the DOM

305 lines (304 loc) 13.3 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.useCSSVariable = useCSSVariable; const react_1 = require("react"); function useCSSVariable(variableName, opacity = 1, returnType = "original") { const [value, setValue] = (0, react_1.useState)(""); // OKLCH to RGB conversion const oklchToRgb = (l, c, h) => { // Convert OKLCH to OKLAB const hRad = (h * Math.PI) / 180; const a = c * Math.cos(hRad); const b = c * Math.sin(hRad); // OKLAB to linear RGB const l_ = l + 0.3963377774 * a + 0.2158037573 * b; const m_ = l - 0.1055613458 * a - 0.0638541728 * b; const s_ = l - 0.0894841775 * a - 1.291485548 * b; const l3 = l_ * l_ * l_; const m3 = m_ * m_ * m_; const s3 = s_ * s_ * s_; // Linear RGB let r = +4.0767416621 * l3 - 3.3077115913 * m3 + 0.2309699292 * s3; let g = -1.2684380046 * l3 + 2.6097574011 * m3 - 0.3413193965 * s3; let b_rgb = -0.0041960863 * l3 - 0.7034186147 * m3 + 1.707614701 * s3; // Gamma correction (linear to sRGB) const gammaCorrect = (c) => { if (c <= 0.0031308) { return 12.92 * c; } else { return 1.055 * Math.pow(c, 1 / 2.4) - 0.055; } }; r = gammaCorrect(Math.max(0, Math.min(1, r))); g = gammaCorrect(Math.max(0, Math.min(1, g))); b_rgb = gammaCorrect(Math.max(0, Math.min(1, b_rgb))); return [ Math.round(r * 255), Math.round(g * 255), Math.round(b_rgb * 255), ]; }; // RGB to OKLCH conversion const rgbToOklch = (r, g, b) => { // Normalize RGB values r = r / 255; g = g / 255; b = b / 255; // sRGB to linear RGB (gamma correction) const linearize = (c) => { if (c <= 0.04045) { return c / 12.92; } else { return Math.pow((c + 0.055) / 1.055, 2.4); } }; r = linearize(r); g = linearize(g); b = linearize(b); // Linear RGB to OKLAB const l = 0.4122214708 * r + 0.5363325363 * g + 0.0514459929 * b; const m = 0.2119034982 * r + 0.6806995451 * g + 0.1073969566 * b; const s = 0.0883024619 * r + 0.2817188376 * g + 0.6299787005 * b; const l_ = Math.cbrt(l); const m_ = Math.cbrt(m); const s_ = Math.cbrt(s); // OKLAB to OKLCH const L = 0.2104542553 * l_ + 0.793617785 * m_ - 0.0040720468 * s_; const a = 1.9779984951 * l_ - 2.428592205 * m_ + 0.4505937099 * s_; const b_lab = 0.0259040371 * l_ + 0.7827717662 * m_ - 0.808675766 * s_; const C = Math.sqrt(a * a + b_lab * b_lab); let H = (Math.atan2(b_lab, a) * 180) / Math.PI; // Normalize hue to 0-360 if (H < 0) H += 360; return [L, C, H]; }; // Helper function to convert colors (0, react_1.useEffect)(() => { const convertColor = (color, targetType, alpha) => { const getRGBFromColor = (colorStr) => { if (colorStr.startsWith("oklch(")) { const oklchMatch = colorStr.match(/oklch\(([\d.]+)\s+([\d.]+)\s+([\d.]+)/); if (oklchMatch) { const l = parseFloat(oklchMatch[1]); const c = parseFloat(oklchMatch[2]); const h = parseFloat(oklchMatch[3]); return oklchToRgb(l, c, h); } } else if (colorStr.startsWith("rgb(")) { const rgbMatch = colorStr.match(/rgb\((\d+),?\s*(\d+),?\s*(\d+)\)/); if (rgbMatch) { return [ parseInt(rgbMatch[1]), parseInt(rgbMatch[2]), parseInt(rgbMatch[3]), ]; } } else if (colorStr.startsWith("#")) { const hex = colorStr.replace("#", ""); const r = parseInt(hex.substring(0, 2), 16); const g = parseInt(hex.substring(2, 4), 16); const b = parseInt(hex.substring(4, 6), 16); return [r, g, b]; } else if (colorStr.startsWith("hsl(")) { const hslMatch = colorStr.match(/hsl\((\d+),?\s*(\d+)%,?\s*(\d+)%\)/); if (hslMatch) { const h = parseInt(hslMatch[1]) / 360; const s = parseInt(hslMatch[2]) / 100; const l = parseInt(hslMatch[3]) / 100; const hue2rgb = (p, q, t) => { if (t < 0) t += 1; if (t > 1) t -= 1; if (t < 1 / 6) return p + (q - p) * 6 * t; if (t < 1 / 2) return q; if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6; return p; }; if (s === 0) { const gray = Math.round(l * 255); return [gray, gray, gray]; } else { const q = l < 0.5 ? l * (1 + s) : l + s - l * s; const p = 2 * l - q; const r = Math.round(hue2rgb(p, q, h + 1 / 3) * 255); const g = Math.round(hue2rgb(p, q, h) * 255); const b = Math.round(hue2rgb(p, q, h - 1 / 3) * 255); return [r, g, b]; } } } return null; }; const rgb = getRGBFromColor(color); if (!rgb) return color; const [r, g, b] = rgb; switch (targetType) { case "hex": { const toHex = (n) => n.toString(16).padStart(2, "0"); if (alpha < 1) { // Return 8-digit hex with alpha when opacity is less than 1 const alphaHex = Math.round(alpha * 255) .toString(16) .padStart(2, "0"); return `#${toHex(r)}${toHex(g)}${toHex(b)}${alphaHex}`; } else { // Standard 6-digit hex when fully opaque return `#${toHex(r)}${toHex(g)}${toHex(b)}`; } } case "rgb": { return `rgb(${r}, ${g}, ${b})`; } case "rgba": { return `rgba(${r}, ${g}, ${b}, ${alpha})`; } case "hsl": { const rNorm = r / 255; const gNorm = g / 255; const bNorm = b / 255; const max = Math.max(rNorm, gNorm, bNorm); const min = Math.min(rNorm, gNorm, bNorm); const diff = max - min; const sum = max + min; const l = sum / 2; if (diff === 0) { return `hsl(0, 0%, ${Math.round(l * 100)}%)`; } const s = l > 0.5 ? diff / (2 - sum) : diff / sum; let h; switch (max) { case rNorm: h = ((gNorm - bNorm) / diff + (gNorm < bNorm ? 6 : 0)) / 6; break; case gNorm: h = ((bNorm - rNorm) / diff + 2) / 6; break; case bNorm: h = ((rNorm - gNorm) / diff + 4) / 6; break; default: h = 0; } return `hsl(${Math.round(h * 360)}, ${Math.round(s * 100)}%, ${Math.round(l * 100)}%)`; } case "hsla": { const rNorm2 = r / 255; const gNorm2 = g / 255; const bNorm2 = b / 255; const max2 = Math.max(rNorm2, gNorm2, bNorm2); const min2 = Math.min(rNorm2, gNorm2, bNorm2); const diff2 = max2 - min2; const sum2 = max2 + min2; const l2 = sum2 / 2; if (diff2 === 0) { return `hsla(0, 0%, ${Math.round(l2 * 100)}%, ${alpha})`; } const s2 = l2 > 0.5 ? diff2 / (2 - sum2) : diff2 / sum2; let h2; switch (max2) { case rNorm2: h2 = ((gNorm2 - bNorm2) / diff2 + (gNorm2 < bNorm2 ? 6 : 0)) / 6; break; case gNorm2: h2 = ((bNorm2 - rNorm2) / diff2 + 2) / 6; break; case bNorm2: h2 = ((rNorm2 - gNorm2) / diff2 + 4) / 6; break; default: h2 = 0; } return `hsla(${Math.round(h2 * 360)}, ${Math.round(s2 * 100)}%, ${Math.round(l2 * 100)}%, ${alpha})`; } case "oklch": { if (color.startsWith("oklch(")) { const oklchMatch = color.match(/oklch\((.*?)\)/); if (oklchMatch) { const values = oklchMatch[1].split(" "); if (alpha < 1) { return `oklch(${values[0]} ${values[1]} ${values[2]} / ${alpha})`; } else { return `oklch(${values[0]} ${values[1]} ${values[2]})`; } } } else { // Convert RGB to OKLCH const [L, C, H] = rgbToOklch(r, g, b); if (alpha < 1) { return `oklch(${L.toFixed(3)} ${C.toFixed(3)} ${H.toFixed(1)} / ${alpha})`; } else { return `oklch(${L.toFixed(3)} ${C.toFixed(3)} ${H.toFixed(1)})`; } } } default: return color; } }; const rootStyles = getComputedStyle(document.documentElement); const cssValue = rootStyles.getPropertyValue(variableName).trim(); if (!cssValue) { setValue(""); return; } if (returnType === "original") { if (opacity === 1) { setValue(cssValue); return; } if (cssValue.startsWith("oklch(")) { const oklchMatch = cssValue.match(/oklch\((.*?)\)/); if (oklchMatch) { const values = oklchMatch[1].split(" "); setValue(`oklch(${values[0]} ${values[1]} ${values[2]} / ${opacity})`); } } else if (cssValue.startsWith("rgb(")) { const rgbMatch = cssValue.match(/rgb\((\d+),?\s*(\d+),?\s*(\d+)\)/); if (rgbMatch) { setValue(`rgba(${rgbMatch[1]}, ${rgbMatch[2]}, ${rgbMatch[3]}, ${opacity})`); } } else if (cssValue.startsWith("#")) { const hex = cssValue.replace("#", ""); const r = parseInt(hex.substring(0, 2), 16); const g = parseInt(hex.substring(2, 4), 16); const b = parseInt(hex.substring(4, 6), 16); setValue(`rgba(${r}, ${g}, ${b}, ${opacity})`); } else if (cssValue.startsWith("hsl(")) { const hslMatch = cssValue.match(/hsl\((\d+),?\s*(\d+)%,?\s*(\d+)%\)/); if (hslMatch) { setValue(`hsla(${hslMatch[1]}, ${hslMatch[2]}%, ${hslMatch[3]}%, ${opacity})`); } } else { setValue(`color-mix(in srgb, ${cssValue} ${opacity * 100}%, transparent)`); } } else { const convertedColor = convertColor(cssValue, returnType, opacity); setValue(convertedColor); } }, [variableName, opacity, returnType]); return value; }