oklchtohex 0.3.0 → 0.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/package.json +1 -1
- package/src/converter.js +89 -2
package/README.md
CHANGED
|
@@ -34,6 +34,7 @@ const convertedTailwindCss = convertTailwindCssToHex(css);
|
|
|
34
34
|
|
|
35
35
|
- Known Tailwind default variables like `--color-red-500` are replaced with exact Tailwind HEX palette values.
|
|
36
36
|
- Unknown variables and raw `oklch(...)` values use functional conversion.
|
|
37
|
+
- Tailwind opacity utilities like `border-red-500/30` and custom theme colors like `border-aurora/40` are handled when emitted as `color-mix(... var(--color-*) ..., transparent)` and converted to 8-digit HEX when the source variable is resolvable.
|
|
37
38
|
- `gamut` option supports `clip` (default) and `fit`.
|
|
38
39
|
|
|
39
40
|
## Vite plugin (auto on dev + build)
|
package/package.json
CHANGED
package/src/converter.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { getTailwindDefaultHexForVar } from "./tailwind-default-hex.js";
|
|
2
2
|
const OKLCH_REGEX = /oklch\(([^)]*)\)/gi;
|
|
3
|
+
const COLOR_MIX_WITH_TRANSPARENT_REGEX =
|
|
4
|
+
/color-mix\(\s*in\s+oklab\s*,\s*var\(\s*(--[\w-]+)\s*\)\s*([0-9.]+)%\s*,\s*transparent\s*\)/gi;
|
|
3
5
|
|
|
4
6
|
function clamp(value, min, max) {
|
|
5
7
|
return Math.min(max, Math.max(min, value));
|
|
@@ -157,6 +159,45 @@ function channelToHex(channel) {
|
|
|
157
159
|
return byte.toString(16).padStart(2, "0");
|
|
158
160
|
}
|
|
159
161
|
|
|
162
|
+
function normalizeHexColor(hex) {
|
|
163
|
+
const trimmed = hex.trim().toLowerCase();
|
|
164
|
+
if (!trimmed.startsWith("#")) {
|
|
165
|
+
return null;
|
|
166
|
+
}
|
|
167
|
+
const raw = trimmed.slice(1);
|
|
168
|
+
if (raw.length === 3) {
|
|
169
|
+
return `#${raw[0]}${raw[0]}${raw[1]}${raw[1]}${raw[2]}${raw[2]}`;
|
|
170
|
+
}
|
|
171
|
+
if (raw.length === 4) {
|
|
172
|
+
return `#${raw[0]}${raw[0]}${raw[1]}${raw[1]}${raw[2]}${raw[2]}${raw[3]}${raw[3]}`;
|
|
173
|
+
}
|
|
174
|
+
if (raw.length === 6 || raw.length === 8) {
|
|
175
|
+
return `#${raw}`;
|
|
176
|
+
}
|
|
177
|
+
return null;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function splitHexChannels(hex) {
|
|
181
|
+
const normalized = normalizeHexColor(hex);
|
|
182
|
+
if (!normalized) {
|
|
183
|
+
return null;
|
|
184
|
+
}
|
|
185
|
+
const raw = normalized.slice(1);
|
|
186
|
+
const rgbHex = raw.slice(0, 6);
|
|
187
|
+
const alphaHex = raw.length === 8 ? raw.slice(6, 8) : "ff";
|
|
188
|
+
return { rgbHex, alphaHex };
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function alphaToHex(alpha) {
|
|
192
|
+
return channelToHex(clamp(alpha, 0, 1));
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function combineAlpha(existingAlphaHex, mixPercent) {
|
|
196
|
+
const baseAlpha = Number.parseInt(existingAlphaHex, 16) / 255;
|
|
197
|
+
const mixedAlpha = baseAlpha * clamp(mixPercent / 100, 0, 1);
|
|
198
|
+
return alphaToHex(mixedAlpha);
|
|
199
|
+
}
|
|
200
|
+
|
|
160
201
|
function normalizeInput(input) {
|
|
161
202
|
if (typeof input === "string") {
|
|
162
203
|
return parseOklch(input);
|
|
@@ -219,14 +260,57 @@ function replaceTailwindDefaultColorVariables(text) {
|
|
|
219
260
|
);
|
|
220
261
|
}
|
|
221
262
|
|
|
263
|
+
function collectHexVariableDeclarations(text) {
|
|
264
|
+
const declarationRegex = /(--[\w-]+)\s*:\s*(#[0-9a-fA-F]{3,8})\s*;/g;
|
|
265
|
+
const map = new Map();
|
|
266
|
+
let match = declarationRegex.exec(text);
|
|
267
|
+
while (match) {
|
|
268
|
+
const variableName = match[1].toLowerCase();
|
|
269
|
+
const normalized = normalizeHexColor(match[2]);
|
|
270
|
+
if (normalized) {
|
|
271
|
+
map.set(variableName, normalized);
|
|
272
|
+
}
|
|
273
|
+
match = declarationRegex.exec(text);
|
|
274
|
+
}
|
|
275
|
+
return map;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
function replaceTailwindColorMixWithHex(text, variableHexMap) {
|
|
279
|
+
return text.replace(
|
|
280
|
+
COLOR_MIX_WITH_TRANSPARENT_REGEX,
|
|
281
|
+
(fullMatch, variableNameRaw, percentRaw) => {
|
|
282
|
+
const variableName = String(variableNameRaw).toLowerCase();
|
|
283
|
+
const percent = Number.parseFloat(percentRaw);
|
|
284
|
+
if (Number.isNaN(percent)) {
|
|
285
|
+
return fullMatch;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
const sourceHex =
|
|
289
|
+
variableHexMap.get(variableName) ??
|
|
290
|
+
getTailwindDefaultHexForVar(variableName);
|
|
291
|
+
|
|
292
|
+
if (!sourceHex) {
|
|
293
|
+
return fullMatch;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
const channels = splitHexChannels(sourceHex);
|
|
297
|
+
if (!channels) {
|
|
298
|
+
return fullMatch;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
const alphaHex = combineAlpha(channels.alphaHex, percent);
|
|
302
|
+
return `#${channels.rgbHex}${alphaHex}`;
|
|
303
|
+
}
|
|
304
|
+
);
|
|
305
|
+
}
|
|
306
|
+
|
|
222
307
|
export function replaceOklchInText(text, options = {}) {
|
|
223
308
|
if (typeof text !== "string") {
|
|
224
309
|
throw new Error("replaceOklchInText expects a string");
|
|
225
310
|
}
|
|
226
311
|
const { onError = "preserve", ...convertOptions } = options;
|
|
227
312
|
const withMappedTailwindDefaults = replaceTailwindDefaultColorVariables(text);
|
|
228
|
-
|
|
229
|
-
return withMappedTailwindDefaults.replace(OKLCH_REGEX, (match) => {
|
|
313
|
+
const withConvertedOklch = withMappedTailwindDefaults.replace(OKLCH_REGEX, (match) => {
|
|
230
314
|
try {
|
|
231
315
|
return oklchToHex(match, convertOptions);
|
|
232
316
|
} catch (error) {
|
|
@@ -236,6 +320,9 @@ export function replaceOklchInText(text, options = {}) {
|
|
|
236
320
|
return match;
|
|
237
321
|
}
|
|
238
322
|
});
|
|
323
|
+
|
|
324
|
+
const variableHexMap = collectHexVariableDeclarations(withConvertedOklch);
|
|
325
|
+
return replaceTailwindColorMixWithHex(withConvertedOklch, variableHexMap);
|
|
239
326
|
}
|
|
240
327
|
|
|
241
328
|
export function convertTailwindCssToHex(css, options = {}) {
|