rnwind 0.0.10 → 0.0.12
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/lib/cjs/core/normalize-classname.cjs +3 -1
- package/lib/cjs/core/normalize-classname.cjs.map +1 -1
- package/lib/cjs/core/parser/border-dispatcher.cjs +20 -10
- package/lib/cjs/core/parser/border-dispatcher.cjs.map +1 -1
- package/lib/cjs/core/parser/color-properties-dispatcher.cjs +7 -5
- package/lib/cjs/core/parser/color-properties-dispatcher.cjs.map +1 -1
- package/lib/cjs/core/parser/color.cjs +194 -10
- package/lib/cjs/core/parser/color.cjs.map +1 -1
- package/lib/cjs/core/parser/color.d.ts +18 -3
- package/lib/cjs/core/parser/declaration.cjs +62 -4
- package/lib/cjs/core/parser/declaration.cjs.map +1 -1
- package/lib/cjs/core/parser/layout-dispatcher.cjs +32 -2
- package/lib/cjs/core/parser/layout-dispatcher.cjs.map +1 -1
- package/lib/cjs/core/parser/shorthand.cjs +10 -3
- package/lib/cjs/core/parser/shorthand.cjs.map +1 -1
- package/lib/cjs/core/parser/tokens.cjs +9 -0
- package/lib/cjs/core/parser/tokens.cjs.map +1 -1
- package/lib/cjs/core/parser/tw-parser.cjs +89 -2
- package/lib/cjs/core/parser/tw-parser.cjs.map +1 -1
- package/lib/cjs/core/parser/tw-parser.d.ts +2 -0
- package/lib/cjs/core/parser/typography-dispatcher.cjs +15 -8
- package/lib/cjs/core/parser/typography-dispatcher.cjs.map +1 -1
- package/lib/cjs/core/style-builder/union-builder.cjs +81 -2
- package/lib/cjs/core/style-builder/union-builder.cjs.map +1 -1
- package/lib/cjs/core/style-builder/union-builder.d.ts +28 -0
- package/lib/cjs/metro/state.cjs +74 -13
- package/lib/cjs/metro/state.cjs.map +1 -1
- package/lib/cjs/metro/state.d.ts +18 -0
- package/lib/cjs/metro/transformer.cjs +10 -4
- package/lib/cjs/metro/transformer.cjs.map +1 -1
- package/lib/cjs/metro/with-config.cjs +57 -0
- package/lib/cjs/metro/with-config.cjs.map +1 -1
- package/lib/cjs/metro/with-config.d.ts +12 -0
- package/lib/cjs/metro/wrap-imports.cjs +36 -1
- package/lib/cjs/metro/wrap-imports.cjs.map +1 -1
- package/lib/cjs/runtime/hooks/use-scheme.cjs +14 -7
- package/lib/cjs/runtime/hooks/use-scheme.cjs.map +1 -1
- package/lib/cjs/runtime/resolve.cjs +6 -2
- package/lib/cjs/runtime/resolve.cjs.map +1 -1
- package/lib/cjs/runtime/resolve.d.ts +5 -1
- package/lib/esm/core/normalize-classname.mjs +3 -1
- package/lib/esm/core/normalize-classname.mjs.map +1 -1
- package/lib/esm/core/parser/border-dispatcher.mjs +21 -11
- package/lib/esm/core/parser/border-dispatcher.mjs.map +1 -1
- package/lib/esm/core/parser/color-properties-dispatcher.mjs +8 -6
- package/lib/esm/core/parser/color-properties-dispatcher.mjs.map +1 -1
- package/lib/esm/core/parser/color.d.ts +18 -3
- package/lib/esm/core/parser/color.mjs +195 -12
- package/lib/esm/core/parser/color.mjs.map +1 -1
- package/lib/esm/core/parser/declaration.mjs +63 -5
- package/lib/esm/core/parser/declaration.mjs.map +1 -1
- package/lib/esm/core/parser/layout-dispatcher.mjs +32 -2
- package/lib/esm/core/parser/layout-dispatcher.mjs.map +1 -1
- package/lib/esm/core/parser/shorthand.mjs +11 -4
- package/lib/esm/core/parser/shorthand.mjs.map +1 -1
- package/lib/esm/core/parser/tokens.mjs +10 -1
- package/lib/esm/core/parser/tokens.mjs.map +1 -1
- package/lib/esm/core/parser/tw-parser.d.ts +2 -0
- package/lib/esm/core/parser/tw-parser.mjs +69 -0
- package/lib/esm/core/parser/tw-parser.mjs.map +1 -1
- package/lib/esm/core/parser/typography-dispatcher.mjs +15 -8
- package/lib/esm/core/parser/typography-dispatcher.mjs.map +1 -1
- package/lib/esm/core/style-builder/union-builder.d.ts +28 -0
- package/lib/esm/core/style-builder/union-builder.mjs +82 -3
- package/lib/esm/core/style-builder/union-builder.mjs.map +1 -1
- package/lib/esm/metro/state.d.ts +18 -0
- package/lib/esm/metro/state.mjs +75 -14
- package/lib/esm/metro/state.mjs.map +1 -1
- package/lib/esm/metro/transformer.mjs +10 -4
- package/lib/esm/metro/transformer.mjs.map +1 -1
- package/lib/esm/metro/with-config.d.ts +12 -0
- package/lib/esm/metro/with-config.mjs +58 -2
- package/lib/esm/metro/with-config.mjs.map +1 -1
- package/lib/esm/metro/wrap-imports.mjs +36 -1
- package/lib/esm/metro/wrap-imports.mjs.map +1 -1
- package/lib/esm/runtime/hooks/use-scheme.mjs +14 -7
- package/lib/esm/runtime/hooks/use-scheme.mjs.map +1 -1
- package/lib/esm/runtime/resolve.d.ts +5 -1
- package/lib/esm/runtime/resolve.mjs +6 -2
- package/lib/esm/runtime/resolve.mjs.map +1 -1
- package/package.json +1 -1
- package/src/core/normalize-classname.ts +4 -1
- package/src/core/parser/border-dispatcher.ts +22 -11
- package/src/core/parser/color-properties-dispatcher.ts +7 -5
- package/src/core/parser/color.ts +182 -11
- package/src/core/parser/declaration.ts +61 -5
- package/src/core/parser/layout-dispatcher.ts +34 -2
- package/src/core/parser/shorthand.ts +9 -3
- package/src/core/parser/tokens.ts +10 -1
- package/src/core/parser/tw-parser.ts +71 -1
- package/src/core/parser/typography-dispatcher.ts +15 -6
- package/src/core/style-builder/union-builder.ts +83 -3
- package/src/metro/state.ts +117 -12
- package/src/metro/transformer.ts +9 -4
- package/src/metro/with-config.ts +59 -1
- package/src/metro/wrap-imports.ts +36 -1
- package/src/runtime/hooks/use-scheme.ts +13 -6
- package/src/runtime/resolve.ts +6 -2
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { rgb, formatHex } from 'culori';
|
|
1
|
+
import { rgb, interpolate, formatHex } from 'culori';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Clamp a 0-255 float to the integer byte range RN color strings accept.
|
|
@@ -132,35 +132,208 @@ function xyzToHex(color) {
|
|
|
132
132
|
const mode = color.type === 'xyz-d50' ? 'xyz50' : 'xyz65';
|
|
133
133
|
return withAlpha(formatHex({ mode, x: color.x, y: color.y, z: color.z }) ?? null, color.alpha);
|
|
134
134
|
}
|
|
135
|
+
/**
|
|
136
|
+
* Map a CSS `color-mix(in <space>, …)` interpolation space to the culori mode
|
|
137
|
+
* key culori's {@link interpolate} understands. `srgb` is culori's `rgb`;
|
|
138
|
+
* `srgb-linear` is `lrgb`; the lab/lch/oklab/oklch/hsl/hwb spaces share their
|
|
139
|
+
* CSS name. Unknown spaces fall back to `rgb` so a mix still resolves to a
|
|
140
|
+
* concrete color rather than leaking the raw expression.
|
|
141
|
+
* @param space Lowercased interpolation-space token (after `in `).
|
|
142
|
+
* @returns culori interpolation mode key.
|
|
143
|
+
*/
|
|
144
|
+
function colorMixModeFor(space) {
|
|
145
|
+
if (space === 'srgb')
|
|
146
|
+
return 'rgb';
|
|
147
|
+
if (space === 'srgb-linear')
|
|
148
|
+
return 'lrgb';
|
|
149
|
+
const known = new Set(['oklab', 'oklch', 'lab', 'lch', 'hsl', 'hwb', 'xyz', 'xyz-d50', 'xyz-d65']);
|
|
150
|
+
return known.has(space) ? space : 'rgb';
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Split a `color-mix()` argument list at top-level commas (parens-aware) so a
|
|
154
|
+
* nested `rgb(0, 0, 0)` color slot doesn't fragment the split.
|
|
155
|
+
* @param body Text between the outer `color-mix(` parentheses.
|
|
156
|
+
* @returns Comma-separated argument fragments (trimmed).
|
|
157
|
+
*/
|
|
158
|
+
function splitColorMixArgs(body) {
|
|
159
|
+
const parts = [];
|
|
160
|
+
let depth = 0;
|
|
161
|
+
let start = 0;
|
|
162
|
+
for (let index = 0; index < body.length; index += 1) {
|
|
163
|
+
const ch = body[index];
|
|
164
|
+
if (ch === '(')
|
|
165
|
+
depth += 1;
|
|
166
|
+
else if (ch === ')')
|
|
167
|
+
depth -= 1;
|
|
168
|
+
else if (ch === ',' && depth === 0) {
|
|
169
|
+
parts.push(body.slice(start, index).trim());
|
|
170
|
+
start = index + 1;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
parts.push(body.slice(start).trim());
|
|
174
|
+
return parts;
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Peel an optional trailing `<num>%` weight off a `color-mix()` color slot.
|
|
178
|
+
* `#ff0000 50%` → `{ color: '#ff0000', weight: 0.5 }`; a bare color → weight
|
|
179
|
+
* `null` (caller fills the complement / defaults to 50/50).
|
|
180
|
+
* @param slot One color argument (color text, optionally suffixed with a percentage).
|
|
181
|
+
* @returns Color text plus its 0–1 weight, or null weight when unspecified.
|
|
182
|
+
*/
|
|
183
|
+
function parseColorMixSlot(slot) {
|
|
184
|
+
const trimmed = slot.trim();
|
|
185
|
+
if (!trimmed.endsWith('%'))
|
|
186
|
+
return { color: trimmed, weight: null };
|
|
187
|
+
// End-anchored `<num>%` matcher (no leading `.*?` — avoids the super-linear
|
|
188
|
+
// backtracking ESLint flags). Split the color off at the last whitespace
|
|
189
|
+
// before the percentage token.
|
|
190
|
+
const pct = COLOR_MIX_SLOT_PCT.exec(trimmed);
|
|
191
|
+
if (!pct)
|
|
192
|
+
return { color: trimmed, weight: null };
|
|
193
|
+
const color = trimmed.slice(0, pct.index).trim();
|
|
194
|
+
if (color.length === 0)
|
|
195
|
+
return { color: trimmed, weight: null };
|
|
196
|
+
const weight = Number(pct[1]) / 100;
|
|
197
|
+
return { color, weight: Number.isFinite(weight) ? weight : null };
|
|
198
|
+
}
|
|
199
|
+
/** End-anchored `<num>%` matcher for slicing a color-mix slot's weight off its right edge. No backtracking. */
|
|
200
|
+
const COLOR_MIX_SLOT_PCT = /\s(-?\d+(?:\.\d+)?)%$/;
|
|
201
|
+
/**
|
|
202
|
+
* Resolve a two-color CSS `color-mix(in <space>, A [p1%], B [p2%])` to a
|
|
203
|
+
* concrete sRGB color via culori's {@link interpolate}. CSS weight rules:
|
|
204
|
+
* with one percentage the other fills the complement; with none it is 50/50;
|
|
205
|
+
* with both, the interpolation point is `p2 / (p1 + p2)`. RN can't evaluate
|
|
206
|
+
* `color-mix()` at paint time, so this is the only path that lowers it.
|
|
207
|
+
* @param text Trimmed CSS value beginning with `color-mix(`.
|
|
208
|
+
* @returns sRGB hex/rgba string, or null when the shape/colors can't resolve.
|
|
209
|
+
*/
|
|
210
|
+
function resolveColorMix(text) {
|
|
211
|
+
if (!text.endsWith(')'))
|
|
212
|
+
return null;
|
|
213
|
+
const open = text.indexOf('(');
|
|
214
|
+
if (open === -1)
|
|
215
|
+
return null;
|
|
216
|
+
const args = splitColorMixArgs(text.slice(open + 1, -1));
|
|
217
|
+
if (args.length !== 3)
|
|
218
|
+
return null;
|
|
219
|
+
const spaceClause = args[0].toLowerCase();
|
|
220
|
+
if (!spaceClause.startsWith('in '))
|
|
221
|
+
return null;
|
|
222
|
+
const mode = colorMixModeFor(spaceClause.slice(3).trim());
|
|
223
|
+
const first = parseColorMixSlot(args[1]);
|
|
224
|
+
const second = parseColorMixSlot(args[2]);
|
|
225
|
+
if (first.color.length === 0 || second.color.length === 0)
|
|
226
|
+
return null;
|
|
227
|
+
const point = colorMixPoint(first.weight, second.weight);
|
|
228
|
+
if (point === null)
|
|
229
|
+
return null;
|
|
230
|
+
try {
|
|
231
|
+
const mixed = interpolate([first.color, second.color], mode)(point);
|
|
232
|
+
if (!mixed)
|
|
233
|
+
return null;
|
|
234
|
+
const back = rgb(mixed);
|
|
235
|
+
if (!back || ![back.r, back.g, back.b].every((v) => typeof v === 'number' && Number.isFinite(v)))
|
|
236
|
+
return null;
|
|
237
|
+
const alpha = typeof back.alpha === 'number' ? back.alpha : 1;
|
|
238
|
+
return rgbIntsToString(clampByte(back.r * 255), clampByte(back.g * 255), clampByte(back.b * 255), alpha);
|
|
239
|
+
}
|
|
240
|
+
catch {
|
|
241
|
+
// culori threw on an unparseable color slot — drop rather than leak the raw string.
|
|
242
|
+
return null;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Compute the 0–1 interpolation point (weight of the SECOND color) from the
|
|
247
|
+
* two optional `color-mix()` weights, applying CSS normalization.
|
|
248
|
+
* @param firstWeight 0–1 weight of color A, or null when unspecified.
|
|
249
|
+
* @param secondWeight 0–1 weight of color B, or null when unspecified.
|
|
250
|
+
* @returns Interpolation point in `[0, 1]`, or null when both weights are 0.
|
|
251
|
+
*/
|
|
252
|
+
function colorMixPoint(firstWeight, secondWeight) {
|
|
253
|
+
if (firstWeight === null && secondWeight === null)
|
|
254
|
+
return 0.5;
|
|
255
|
+
if (firstWeight !== null && secondWeight === null)
|
|
256
|
+
return 1 - firstWeight;
|
|
257
|
+
if (firstWeight === null && secondWeight !== null)
|
|
258
|
+
return secondWeight;
|
|
259
|
+
const sum = firstWeight + secondWeight;
|
|
260
|
+
if (sum === 0)
|
|
261
|
+
return null;
|
|
262
|
+
return secondWeight / sum;
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* CSS-wide cascade keywords that resolve a property against the inherited /
|
|
266
|
+
* initial / previous-layer value at paint time. React Native has NO color
|
|
267
|
+
* cascade — there is no inherited `color` for an arbitrary style prop and no
|
|
268
|
+
* cascade layers — so as a `color` / `backgroundColor` / `borderColor` value
|
|
269
|
+
* every one of these reaches RN as an invalid color string. `currentColor`
|
|
270
|
+
* belongs here too: it resolves to the element's inherited `color`, which RN
|
|
271
|
+
* never threads into other color props. The color path must DROP these (omit
|
|
272
|
+
* the key) rather than leak the keyword. NOTE: `transparent` is NOT here — it
|
|
273
|
+
* is a real color that {@link cssColorToString} / {@link normalizeColorString}
|
|
274
|
+
* lower to `rgba(0, 0, 0, 0)`, which RN paints correctly.
|
|
275
|
+
*/
|
|
276
|
+
const CSS_WIDE_COLOR_KEYWORDS = new Set([
|
|
277
|
+
'currentcolor',
|
|
278
|
+
'inherit',
|
|
279
|
+
'initial',
|
|
280
|
+
'unset',
|
|
281
|
+
'revert',
|
|
282
|
+
'revert-layer',
|
|
283
|
+
]);
|
|
135
284
|
/**
|
|
136
285
|
* Modern CSS color functions RN's native view manager can't paint —
|
|
137
286
|
* everything else (hex, `rgb()`/`rgba()`, `hsl()`/`hsla()`, named colors,
|
|
138
|
-
* `transparent
|
|
139
|
-
*
|
|
140
|
-
*
|
|
141
|
-
*
|
|
142
|
-
*
|
|
287
|
+
* `transparent`) RN reads directly and must pass through untouched. The
|
|
288
|
+
* CSS-wide cascade keywords (`currentColor`, `inherit`, …) are NOT readable —
|
|
289
|
+
* they have no RN equivalent and are dropped via {@link isCssWideColorKeyword}.
|
|
290
|
+
* Custom `@theme` tokens reach the parser as `var(--color-x)` (only the default
|
|
291
|
+
* palette is `theme(inline)`-d), so they flow through the unparsed-string path
|
|
292
|
+
* where the typed {@link cssColorToString} never runs — this is the one place
|
|
293
|
+
* that lowers their wide-gamut values to sRGB. `color-mix(` is in the list too,
|
|
294
|
+
* but it takes the dedicated {@link resolveColorMix} path — culori's `rgb()`
|
|
295
|
+
* parser can't read it.
|
|
143
296
|
*/
|
|
144
|
-
const RN_UNREADABLE_COLOR_PREFIXES = ['oklch(', 'oklab(', 'lab(', 'lch(', 'color(', 'hwb('];
|
|
297
|
+
const RN_UNREADABLE_COLOR_PREFIXES = ['oklch(', 'oklab(', 'lab(', 'lch(', 'color(', 'hwb(', 'color-mix('];
|
|
145
298
|
/**
|
|
146
299
|
* Lower a wide-gamut / modern CSS color STRING (`oklch(…)`, `lab(…)`,
|
|
147
|
-
* `color(display-p3 …)`, …) to an sRGB hex/rgba string RN can
|
|
148
|
-
* `null` for anything RN already understands (hex, rgb, hsl,
|
|
149
|
-
* caller keeps the original text — only the unrepresentable
|
|
300
|
+
* `color(display-p3 …)`, `color-mix(…)`) to an sRGB hex/rgba string RN can
|
|
301
|
+
* paint. Returns `null` for anything RN already understands (hex, rgb, hsl,
|
|
302
|
+
* named) so the caller keeps the original text — only the unrepresentable
|
|
303
|
+
* forms convert. `color-mix()` is resolved via culori's interpolator; when it
|
|
304
|
+
* (or any other modern form) can't resolve, returns null so the caller DROPS
|
|
305
|
+
* the value rather than leaking the raw, RN-unreadable string.
|
|
150
306
|
* Mirrors {@link cssColorToString}'s culori lowering for the string path.
|
|
151
307
|
* @param text Resolved CSS color text (post theme-var substitution).
|
|
152
308
|
* @returns sRGB color string, or `null` when no conversion is needed/possible.
|
|
153
309
|
*/
|
|
154
310
|
function normalizeColorString(text) {
|
|
155
|
-
const
|
|
311
|
+
const trimmed = text.trim();
|
|
312
|
+
const lower = trimmed.toLowerCase();
|
|
156
313
|
if (!RN_UNREADABLE_COLOR_PREFIXES.some((prefix) => lower.startsWith(prefix)))
|
|
157
314
|
return null;
|
|
315
|
+
if (lower.startsWith('color-mix('))
|
|
316
|
+
return resolveColorMix(trimmed);
|
|
158
317
|
const parsed = rgb(text);
|
|
159
318
|
if (!parsed || ![parsed.r, parsed.g, parsed.b].every((v) => typeof v === 'number' && Number.isFinite(v)))
|
|
160
319
|
return null;
|
|
161
320
|
const alpha = typeof parsed.alpha === 'number' ? parsed.alpha : 1;
|
|
162
321
|
return rgbIntsToString(clampByte(parsed.r * 255), clampByte(parsed.g * 255), clampByte(parsed.b * 255), alpha);
|
|
163
322
|
}
|
|
323
|
+
/**
|
|
324
|
+
* Whether a resolved color STRING is a CSS-wide cascade keyword
|
|
325
|
+
* (`currentColor`, `inherit`, `initial`, `unset`, `revert`, `revert-layer`)
|
|
326
|
+
* with no React Native equivalent. RN has no color cascade, so the color path
|
|
327
|
+
* must DROP (omit the key) when this is true rather than emit the keyword —
|
|
328
|
+
* RN would otherwise receive an invalid color string and render nothing.
|
|
329
|
+
* `transparent` is NOT a cascade keyword: it is a concrete color the converters
|
|
330
|
+
* lower to `rgba(0, 0, 0, 0)`, so it returns false here and resolves normally.
|
|
331
|
+
* @param text Resolved color text (post theme-var substitution / typed-color stringification).
|
|
332
|
+
* @returns True when the value is an RN-unrepresentable CSS-wide keyword.
|
|
333
|
+
*/
|
|
334
|
+
function isCssWideColorKeyword(text) {
|
|
335
|
+
return CSS_WIDE_COLOR_KEYWORDS.has(text.trim().toLowerCase());
|
|
336
|
+
}
|
|
164
337
|
/**
|
|
165
338
|
* Convert a lightningcss `CssColor` to an RN-safe color string. RGB
|
|
166
339
|
* passes through unchanged. LAB / LCH / OKLAB / OKLCH / `color(xyz-…)`
|
|
@@ -210,6 +383,16 @@ function cssColorToString(color) {
|
|
|
210
383
|
return 'currentColor';
|
|
211
384
|
}
|
|
212
385
|
case 'light-dark': {
|
|
386
|
+
// `light-dark(L, D)` is a RUNTIME CSS function — the browser picks the
|
|
387
|
+
// branch from the element's `color-scheme`. rnwind has no runtime CSS
|
|
388
|
+
// evaluation, and the active scheme is NOT threaded into this typed
|
|
389
|
+
// converter (it takes a bare `CssColor`, and every one of its ~15 call
|
|
390
|
+
// sites — border / shorthand / gradient / declaration dispatchers — calls
|
|
391
|
+
// it without a scheme). Scheme resolution instead happens UPSTREAM, at the
|
|
392
|
+
// CSS-block walk (`@custom-variant` + `.dark {}` selectors in
|
|
393
|
+
// theme-vars.ts), which compiles a separate atom + var table per scheme.
|
|
394
|
+
// So the `.light` branch is the correct compile-time default here; the
|
|
395
|
+
// dark value is carried by the scheme-specific atom, not by this function.
|
|
213
396
|
return cssColorToString(color.light);
|
|
214
397
|
}
|
|
215
398
|
default: {
|
|
@@ -218,5 +401,5 @@ function cssColorToString(color) {
|
|
|
218
401
|
}
|
|
219
402
|
}
|
|
220
403
|
|
|
221
|
-
export { cssColorToString, normalizeColorString };
|
|
404
|
+
export { cssColorToString, isCssWideColorKeyword, normalizeColorString };
|
|
222
405
|
//# sourceMappingURL=color.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"color.mjs","sources":["../../../../../src/core/parser/color.ts"],"sourcesContent":["import type { CssColor, LABColor } from 'lightningcss'\nimport { formatHex, rgb as culoriRgb } from 'culori'\n\n/**\n * Clamp a 0-255 float to the integer byte range RN color strings accept.\n * @param value Unclamped float (may be negative or above 255).\n * @returns Integer in `[0, 255]`.\n */\nfunction clampByte(value: number): number {\n if (value < 0) return 0\n if (value > 255) return 255\n return Math.round(value)\n}\n\n/**\n * Two-digit hex encoding of a 0-255 byte.\n * @param byte Byte value (may be out-of-range — clamped).\n * @returns Zero-padded two-char hex string.\n */\nfunction byteToHex(byte: number): string {\n const hex = clampByte(byte).toString(16)\n return hex.length === 1 ? `0${hex}` : hex\n}\n\n/**\n * Format an integer-RGB triple + alpha as `#rrggbb` or `rgba(r, g, b, a)`.\n * @param r 0-255 red.\n * @param g 0-255 green.\n * @param b 0-255 blue.\n * @param alpha 0-1 alpha.\n * @returns Color string.\n */\nfunction rgbIntsToString(r: number, g: number, b: number, alpha: number): string {\n if (alpha >= 1) return `#${byteToHex(r)}${byteToHex(g)}${byteToHex(b)}`\n // Round the alpha to shed f32 noise (`0.2 → 0.20000000298…`) — RN parses\n // either, but the rounded form keeps generated StyleSheets compact.\n return `rgba(${r}, ${g}, ${b}, ${Math.round(alpha * 10_000) / 10_000})`\n}\n\n/**\n * Format a float-RGB triple + alpha (CSS `color(srgb …)` forms) as\n * hex/rgba.\n * @param r 0-1 red.\n * @param g 0-1 green.\n * @param b 0-1 blue.\n * @param alpha 0-1 alpha.\n * @returns Color string.\n */\nfunction floatRgbToString(r: number, g: number, b: number, alpha: number): string {\n return rgbIntsToString(clampByte(r * 255), clampByte(g * 255), clampByte(b * 255), alpha)\n}\n\n/**\n * Dispatch a LAB-family color through culori's hex formatter.\n * @param color Typed LAB-family color.\n * @returns `#rrggbb` string, or `null` when culori couldn't convert.\n */\nfunction culoriHexFor(color: LABColor): string | null {\n switch (color.type) {\n case 'oklch': {\n return formatHex({ mode: 'oklch', l: color.l, c: color.c, h: color.h }) ?? null\n }\n case 'oklab': {\n return formatHex({ mode: 'oklab', l: color.l, a: color.a, b: color.b }) ?? null\n }\n case 'lab': {\n return formatHex({ mode: 'lab', l: color.l, a: color.a, b: color.b }) ?? null\n }\n case 'lch': {\n return formatHex({ mode: 'lch', l: color.l, c: color.c, h: color.h }) ?? null\n }\n default: {\n return null\n }\n }\n}\n\n/**\n * Composite a culori-produced sRGB hex with the source alpha into the RN\n * color string. Shared tail for every culori-backed conversion (lab\n * family, XYZ, wide-gamut RGB): opaque → the hex as-is; translucent →\n * `rgba(...)` rebuilt from the hex channels.\n * @param hex sRGB hex from culori, or `null` when culori rejected the color.\n * @param alpha Source alpha (0–1).\n * @returns RN color string.\n */\nfunction withAlpha(hex: string | null, alpha: number): string {\n if (!hex) return alpha < 1 ? 'rgba(0, 0, 0, 0)' : 'transparent'\n if (alpha >= 1) return hex\n const back = culoriRgb(hex)\n if (!back) return hex\n return rgbIntsToString(clampByte(back.r * 255), clampByte(back.g * 255), clampByte(back.b * 255), alpha)\n}\n\n/**\n * Convert a LAB / LCH / OKLAB / OKLCH color to sRGB hex via culori. RN\n * can't evaluate these modern color spaces at paint time; compile-time\n * lowering to sRGB is the only portable path.\n * @param color Typed lab-family color.\n * @returns Hex or rgba string in sRGB.\n */\nfunction labFamilyToHex(color: LABColor): string {\n return withAlpha(culoriHexFor(color), color.alpha)\n}\n\n/**\n * Convert a wide-gamut `color(<space> r g b)` triple to sRGB hex via\n * culori. The channels are NOT sRGB — each space (display-p3, rec2020,\n * a98-rgb, prophoto-rgb, srgb-linear) carries its own primaries / transfer\n * function, so a bare `channel * 255` would mis-paint. culori does the\n * gamut + gamma conversion to sRGB.\n * @param mode culori mode key for the source space.\n * @param r Source red (0–1).\n * @param g Source green (0–1).\n * @param b Source blue (0–1).\n * @param alpha Alpha channel (0–1).\n * @returns sRGB color string RN accepts.\n */\nfunction wideGamutToHex(mode: 'lrgb' | 'p3' | 'a98' | 'prophoto' | 'rec2020', r: number, g: number, b: number, alpha: number): string {\n return withAlpha(formatHex({ mode, r, g, b }) ?? null, alpha)\n}\n\n/**\n * Convert a CSS `color(xyz-d50 …)` / `color(xyz-d65 …)` value to sRGB hex\n * via culori.\n * @param color Typed XYZ color (discriminated by `type`).\n * @param color.type Whether the color is in the D50 or D65 XYZ space.\n * @param color.x CIE X component (0–1).\n * @param color.y CIE Y component (0–1).\n * @param color.z CIE Z component (0–1).\n * @param color.alpha Alpha channel (0–1).\n * @returns `#rrggbb` string, or `'transparent'` when culori rejects it.\n */\nfunction xyzToHex(color: { type: 'xyz-d50' | 'xyz-d65'; x: number; y: number; z: number; alpha: number }): string {\n const mode = color.type === 'xyz-d50' ? 'xyz50' : 'xyz65'\n return withAlpha(formatHex({ mode, x: color.x, y: color.y, z: color.z }) ?? null, color.alpha)\n}\n\n/**\n * Modern CSS color functions RN's native view manager can't paint —\n * everything else (hex, `rgb()`/`rgba()`, `hsl()`/`hsla()`, named colors,\n * `transparent`, `currentColor`) RN reads directly and must pass through\n * untouched. Custom `@theme` tokens reach the parser as `var(--color-x)`\n * (only the default palette is `theme(inline)`-d), so they flow through the\n * unparsed-string path where the typed {@link cssColorToString} never runs —\n * this is the one place that lowers their wide-gamut values to sRGB.\n */\nconst RN_UNREADABLE_COLOR_PREFIXES: readonly string[] = ['oklch(', 'oklab(', 'lab(', 'lch(', 'color(', 'hwb(']\n\n/**\n * Lower a wide-gamut / modern CSS color STRING (`oklch(…)`, `lab(…)`,\n * `color(display-p3 …)`, …) to an sRGB hex/rgba string RN can paint. Returns\n * `null` for anything RN already understands (hex, rgb, hsl, named) so the\n * caller keeps the original text — only the unrepresentable forms convert.\n * Mirrors {@link cssColorToString}'s culori lowering for the string path.\n * @param text Resolved CSS color text (post theme-var substitution).\n * @returns sRGB color string, or `null` when no conversion is needed/possible.\n */\nexport function normalizeColorString(text: string): string | null {\n const lower = text.trim().toLowerCase()\n if (!RN_UNREADABLE_COLOR_PREFIXES.some((prefix) => lower.startsWith(prefix))) return null\n const parsed = culoriRgb(text)\n if (!parsed || ![parsed.r, parsed.g, parsed.b].every((v) => typeof v === 'number' && Number.isFinite(v))) return null\n const alpha = typeof parsed.alpha === 'number' ? parsed.alpha : 1\n return rgbIntsToString(clampByte(parsed.r * 255), clampByte(parsed.g * 255), clampByte(parsed.b * 255), alpha)\n}\n\n/**\n * Convert a lightningcss `CssColor` to an RN-safe color string. RGB\n * passes through unchanged. LAB / LCH / OKLAB / OKLCH / `color(xyz-…)`\n * forms go through culori to reach sRGB — RN's native view manager only\n * understands sRGB-family strings. SystemColor keywords (`'background'`,\n * `'canvas'`, …) pass through untouched; they have no RN analog and the\n * runtime ignores unknown color strings gracefully.\n * @param color Typed color value.\n * @returns Color string RN accepts.\n */\nexport function cssColorToString(color: CssColor): string {\n if (typeof color === 'string') return color\n switch (color.type) {\n case 'rgb': {\n return rgbIntsToString(color.r, color.g, color.b, color.alpha)\n }\n case 'lab':\n case 'lch':\n case 'oklab':\n case 'oklch': {\n return labFamilyToHex(color)\n }\n case 'srgb': {\n return floatRgbToString(color.r, color.g, color.b, color.alpha)\n }\n case 'srgb-linear': {\n return wideGamutToHex('lrgb', color.r, color.g, color.b, color.alpha)\n }\n case 'display-p3': {\n return wideGamutToHex('p3', color.r, color.g, color.b, color.alpha)\n }\n case 'a98-rgb': {\n return wideGamutToHex('a98', color.r, color.g, color.b, color.alpha)\n }\n case 'prophoto-rgb': {\n return wideGamutToHex('prophoto', color.r, color.g, color.b, color.alpha)\n }\n case 'rec2020': {\n return wideGamutToHex('rec2020', color.r, color.g, color.b, color.alpha)\n }\n case 'xyz-d50':\n case 'xyz-d65': {\n return xyzToHex(color)\n }\n case 'currentcolor': {\n return 'currentColor'\n }\n case 'light-dark': {\n return cssColorToString(color.light)\n }\n default: {\n return 'transparent'\n }\n }\n}\n"],"names":["culoriRgb"],"mappings":";;AAGA;;;;AAIG;AACH,SAAS,SAAS,CAAC,KAAa,EAAA;IAC9B,IAAI,KAAK,GAAG,CAAC;AAAE,QAAA,OAAO,CAAC;IACvB,IAAI,KAAK,GAAG,GAAG;AAAE,QAAA,OAAO,GAAG;AAC3B,IAAA,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC;AAC1B;AAEA;;;;AAIG;AACH,SAAS,SAAS,CAAC,IAAY,EAAA;IAC7B,MAAM,GAAG,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC;AACxC,IAAA,OAAO,GAAG,CAAC,MAAM,KAAK,CAAC,GAAG,CAAA,CAAA,EAAI,GAAG,CAAA,CAAE,GAAG,GAAG;AAC3C;AAEA;;;;;;;AAOG;AACH,SAAS,eAAe,CAAC,CAAS,EAAE,CAAS,EAAE,CAAS,EAAE,KAAa,EAAA;IACrE,IAAI,KAAK,IAAI,CAAC;AAAE,QAAA,OAAO,IAAI,SAAS,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC,CAAC,EAAE;;;AAGvE,IAAA,OAAO,QAAQ,CAAC,CAAA,EAAA,EAAK,CAAC,CAAA,EAAA,EAAK,CAAC,KAAK,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,MAAM,CAAC,GAAG,MAAM,GAAG;AACzE;AAEA;;;;;;;;AAQG;AACH,SAAS,gBAAgB,CAAC,CAAS,EAAE,CAAS,EAAE,CAAS,EAAE,KAAa,EAAA;IACtE,OAAO,eAAe,CAAC,SAAS,CAAC,CAAC,GAAG,GAAG,CAAC,EAAE,SAAS,CAAC,CAAC,GAAG,GAAG,CAAC,EAAE,SAAS,CAAC,CAAC,GAAG,GAAG,CAAC,EAAE,KAAK,CAAC;AAC3F;AAEA;;;;AAIG;AACH,SAAS,YAAY,CAAC,KAAe,EAAA;AACnC,IAAA,QAAQ,KAAK,CAAC,IAAI;QAChB,KAAK,OAAO,EAAE;AACZ,YAAA,OAAO,SAAS,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,EAAE,KAAK,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,CAAC,CAAC,EAAE,CAAC,IAAI,IAAI;QACjF;QACA,KAAK,OAAO,EAAE;AACZ,YAAA,OAAO,SAAS,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,EAAE,KAAK,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,CAAC,CAAC,EAAE,CAAC,IAAI,IAAI;QACjF;QACA,KAAK,KAAK,EAAE;AACV,YAAA,OAAO,SAAS,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,CAAC,CAAC,EAAE,CAAC,IAAI,IAAI;QAC/E;QACA,KAAK,KAAK,EAAE;AACV,YAAA,OAAO,SAAS,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,CAAC,CAAC,EAAE,CAAC,IAAI,IAAI;QAC/E;QACA,SAAS;AACP,YAAA,OAAO,IAAI;QACb;;AAEJ;AAEA;;;;;;;;AAQG;AACH,SAAS,SAAS,CAAC,GAAkB,EAAE,KAAa,EAAA;AAClD,IAAA,IAAI,CAAC,GAAG;QAAE,OAAO,KAAK,GAAG,CAAC,GAAG,kBAAkB,GAAG,aAAa;IAC/D,IAAI,KAAK,IAAI,CAAC;AAAE,QAAA,OAAO,GAAG;AAC1B,IAAA,MAAM,IAAI,GAAGA,GAAS,CAAC,GAAG,CAAC;AAC3B,IAAA,IAAI,CAAC,IAAI;AAAE,QAAA,OAAO,GAAG;AACrB,IAAA,OAAO,eAAe,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,GAAG,GAAG,CAAC,EAAE,SAAS,CAAC,IAAI,CAAC,CAAC,GAAG,GAAG,CAAC,EAAE,SAAS,CAAC,IAAI,CAAC,CAAC,GAAG,GAAG,CAAC,EAAE,KAAK,CAAC;AAC1G;AAEA;;;;;;AAMG;AACH,SAAS,cAAc,CAAC,KAAe,EAAA;IACrC,OAAO,SAAS,CAAC,YAAY,CAAC,KAAK,CAAC,EAAE,KAAK,CAAC,KAAK,CAAC;AACpD;AAEA;;;;;;;;;;;;AAYG;AACH,SAAS,cAAc,CAAC,IAAoD,EAAE,CAAS,EAAE,CAAS,EAAE,CAAS,EAAE,KAAa,EAAA;AAC1H,IAAA,OAAO,SAAS,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,IAAI,IAAI,EAAE,KAAK,CAAC;AAC/D;AAEA;;;;;;;;;;AAUG;AACH,SAAS,QAAQ,CAAC,KAAsF,EAAA;AACtG,IAAA,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,KAAK,SAAS,GAAG,OAAO,GAAG,OAAO;AACzD,IAAA,OAAO,SAAS,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,KAAK,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,CAAC,CAAC,EAAE,CAAC,IAAI,IAAI,EAAE,KAAK,CAAC,KAAK,CAAC;AAChG;AAEA;;;;;;;;AAQG;AACH,MAAM,4BAA4B,GAAsB,CAAC,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,CAAC;AAE9G;;;;;;;;AAQG;AACG,SAAU,oBAAoB,CAAC,IAAY,EAAA;IAC/C,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE;AACvC,IAAA,IAAI,CAAC,4BAA4B,CAAC,IAAI,CAAC,CAAC,MAAM,KAAK,KAAK,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;AAAE,QAAA,OAAO,IAAI;AACzF,IAAA,MAAM,MAAM,GAAGA,GAAS,CAAC,IAAI,CAAC;AAC9B,IAAA,IAAI,CAAC,MAAM,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;AAAE,QAAA,OAAO,IAAI;AACrH,IAAA,MAAM,KAAK,GAAG,OAAO,MAAM,CAAC,KAAK,KAAK,QAAQ,GAAG,MAAM,CAAC,KAAK,GAAG,CAAC;AACjE,IAAA,OAAO,eAAe,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,GAAG,GAAG,CAAC,EAAE,SAAS,CAAC,MAAM,CAAC,CAAC,GAAG,GAAG,CAAC,EAAE,SAAS,CAAC,MAAM,CAAC,CAAC,GAAG,GAAG,CAAC,EAAE,KAAK,CAAC;AAChH;AAEA;;;;;;;;;AASG;AACG,SAAU,gBAAgB,CAAC,KAAe,EAAA;IAC9C,IAAI,OAAO,KAAK,KAAK,QAAQ;AAAE,QAAA,OAAO,KAAK;AAC3C,IAAA,QAAQ,KAAK,CAAC,IAAI;QAChB,KAAK,KAAK,EAAE;AACV,YAAA,OAAO,eAAe,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,KAAK,CAAC;QAChE;AACA,QAAA,KAAK,KAAK;AACV,QAAA,KAAK,KAAK;AACV,QAAA,KAAK,OAAO;QACZ,KAAK,OAAO,EAAE;AACZ,YAAA,OAAO,cAAc,CAAC,KAAK,CAAC;QAC9B;QACA,KAAK,MAAM,EAAE;AACX,YAAA,OAAO,gBAAgB,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,KAAK,CAAC;QACjE;QACA,KAAK,aAAa,EAAE;YAClB,OAAO,cAAc,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,KAAK,CAAC;QACvE;QACA,KAAK,YAAY,EAAE;YACjB,OAAO,cAAc,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,KAAK,CAAC;QACrE;QACA,KAAK,SAAS,EAAE;YACd,OAAO,cAAc,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,KAAK,CAAC;QACtE;QACA,KAAK,cAAc,EAAE;YACnB,OAAO,cAAc,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,KAAK,CAAC;QAC3E;QACA,KAAK,SAAS,EAAE;YACd,OAAO,cAAc,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,KAAK,CAAC;QAC1E;AACA,QAAA,KAAK,SAAS;QACd,KAAK,SAAS,EAAE;AACd,YAAA,OAAO,QAAQ,CAAC,KAAK,CAAC;QACxB;QACA,KAAK,cAAc,EAAE;AACnB,YAAA,OAAO,cAAc;QACvB;QACA,KAAK,YAAY,EAAE;AACjB,YAAA,OAAO,gBAAgB,CAAC,KAAK,CAAC,KAAK,CAAC;QACtC;QACA,SAAS;AACP,YAAA,OAAO,aAAa;QACtB;;AAEJ;;;;"}
|
|
1
|
+
{"version":3,"file":"color.mjs","sources":["../../../../../src/core/parser/color.ts"],"sourcesContent":["import type { CssColor, LABColor } from 'lightningcss'\nimport { formatHex, interpolate, rgb as culoriRgb } from 'culori'\n\n/**\n * Clamp a 0-255 float to the integer byte range RN color strings accept.\n * @param value Unclamped float (may be negative or above 255).\n * @returns Integer in `[0, 255]`.\n */\nfunction clampByte(value: number): number {\n if (value < 0) return 0\n if (value > 255) return 255\n return Math.round(value)\n}\n\n/**\n * Two-digit hex encoding of a 0-255 byte.\n * @param byte Byte value (may be out-of-range — clamped).\n * @returns Zero-padded two-char hex string.\n */\nfunction byteToHex(byte: number): string {\n const hex = clampByte(byte).toString(16)\n return hex.length === 1 ? `0${hex}` : hex\n}\n\n/**\n * Format an integer-RGB triple + alpha as `#rrggbb` or `rgba(r, g, b, a)`.\n * @param r 0-255 red.\n * @param g 0-255 green.\n * @param b 0-255 blue.\n * @param alpha 0-1 alpha.\n * @returns Color string.\n */\nfunction rgbIntsToString(r: number, g: number, b: number, alpha: number): string {\n if (alpha >= 1) return `#${byteToHex(r)}${byteToHex(g)}${byteToHex(b)}`\n // Round the alpha to shed f32 noise (`0.2 → 0.20000000298…`) — RN parses\n // either, but the rounded form keeps generated StyleSheets compact.\n return `rgba(${r}, ${g}, ${b}, ${Math.round(alpha * 10_000) / 10_000})`\n}\n\n/**\n * Format a float-RGB triple + alpha (CSS `color(srgb …)` forms) as\n * hex/rgba.\n * @param r 0-1 red.\n * @param g 0-1 green.\n * @param b 0-1 blue.\n * @param alpha 0-1 alpha.\n * @returns Color string.\n */\nfunction floatRgbToString(r: number, g: number, b: number, alpha: number): string {\n return rgbIntsToString(clampByte(r * 255), clampByte(g * 255), clampByte(b * 255), alpha)\n}\n\n/**\n * Dispatch a LAB-family color through culori's hex formatter.\n * @param color Typed LAB-family color.\n * @returns `#rrggbb` string, or `null` when culori couldn't convert.\n */\nfunction culoriHexFor(color: LABColor): string | null {\n switch (color.type) {\n case 'oklch': {\n return formatHex({ mode: 'oklch', l: color.l, c: color.c, h: color.h }) ?? null\n }\n case 'oklab': {\n return formatHex({ mode: 'oklab', l: color.l, a: color.a, b: color.b }) ?? null\n }\n case 'lab': {\n return formatHex({ mode: 'lab', l: color.l, a: color.a, b: color.b }) ?? null\n }\n case 'lch': {\n return formatHex({ mode: 'lch', l: color.l, c: color.c, h: color.h }) ?? null\n }\n default: {\n return null\n }\n }\n}\n\n/**\n * Composite a culori-produced sRGB hex with the source alpha into the RN\n * color string. Shared tail for every culori-backed conversion (lab\n * family, XYZ, wide-gamut RGB): opaque → the hex as-is; translucent →\n * `rgba(...)` rebuilt from the hex channels.\n * @param hex sRGB hex from culori, or `null` when culori rejected the color.\n * @param alpha Source alpha (0–1).\n * @returns RN color string.\n */\nfunction withAlpha(hex: string | null, alpha: number): string {\n if (!hex) return alpha < 1 ? 'rgba(0, 0, 0, 0)' : 'transparent'\n if (alpha >= 1) return hex\n const back = culoriRgb(hex)\n if (!back) return hex\n return rgbIntsToString(clampByte(back.r * 255), clampByte(back.g * 255), clampByte(back.b * 255), alpha)\n}\n\n/**\n * Convert a LAB / LCH / OKLAB / OKLCH color to sRGB hex via culori. RN\n * can't evaluate these modern color spaces at paint time; compile-time\n * lowering to sRGB is the only portable path.\n * @param color Typed lab-family color.\n * @returns Hex or rgba string in sRGB.\n */\nfunction labFamilyToHex(color: LABColor): string {\n return withAlpha(culoriHexFor(color), color.alpha)\n}\n\n/**\n * Convert a wide-gamut `color(<space> r g b)` triple to sRGB hex via\n * culori. The channels are NOT sRGB — each space (display-p3, rec2020,\n * a98-rgb, prophoto-rgb, srgb-linear) carries its own primaries / transfer\n * function, so a bare `channel * 255` would mis-paint. culori does the\n * gamut + gamma conversion to sRGB.\n * @param mode culori mode key for the source space.\n * @param r Source red (0–1).\n * @param g Source green (0–1).\n * @param b Source blue (0–1).\n * @param alpha Alpha channel (0–1).\n * @returns sRGB color string RN accepts.\n */\nfunction wideGamutToHex(mode: 'lrgb' | 'p3' | 'a98' | 'prophoto' | 'rec2020', r: number, g: number, b: number, alpha: number): string {\n return withAlpha(formatHex({ mode, r, g, b }) ?? null, alpha)\n}\n\n/**\n * Convert a CSS `color(xyz-d50 …)` / `color(xyz-d65 …)` value to sRGB hex\n * via culori.\n * @param color Typed XYZ color (discriminated by `type`).\n * @param color.type Whether the color is in the D50 or D65 XYZ space.\n * @param color.x CIE X component (0–1).\n * @param color.y CIE Y component (0–1).\n * @param color.z CIE Z component (0–1).\n * @param color.alpha Alpha channel (0–1).\n * @returns `#rrggbb` string, or `'transparent'` when culori rejects it.\n */\nfunction xyzToHex(color: { type: 'xyz-d50' | 'xyz-d65'; x: number; y: number; z: number; alpha: number }): string {\n const mode = color.type === 'xyz-d50' ? 'xyz50' : 'xyz65'\n return withAlpha(formatHex({ mode, x: color.x, y: color.y, z: color.z }) ?? null, color.alpha)\n}\n\n/**\n * Map a CSS `color-mix(in <space>, …)` interpolation space to the culori mode\n * key culori's {@link interpolate} understands. `srgb` is culori's `rgb`;\n * `srgb-linear` is `lrgb`; the lab/lch/oklab/oklch/hsl/hwb spaces share their\n * CSS name. Unknown spaces fall back to `rgb` so a mix still resolves to a\n * concrete color rather than leaking the raw expression.\n * @param space Lowercased interpolation-space token (after `in `).\n * @returns culori interpolation mode key.\n */\nfunction colorMixModeFor(space: string): string {\n if (space === 'srgb') return 'rgb'\n if (space === 'srgb-linear') return 'lrgb'\n const known = new Set(['oklab', 'oklch', 'lab', 'lch', 'hsl', 'hwb', 'xyz', 'xyz-d50', 'xyz-d65'])\n return known.has(space) ? space : 'rgb'\n}\n\n/**\n * Split a `color-mix()` argument list at top-level commas (parens-aware) so a\n * nested `rgb(0, 0, 0)` color slot doesn't fragment the split.\n * @param body Text between the outer `color-mix(` parentheses.\n * @returns Comma-separated argument fragments (trimmed).\n */\nfunction splitColorMixArgs(body: string): string[] {\n const parts: string[] = []\n let depth = 0\n let start = 0\n for (let index = 0; index < body.length; index += 1) {\n const ch = body[index]\n if (ch === '(') depth += 1\n else if (ch === ')') depth -= 1\n else if (ch === ',' && depth === 0) {\n parts.push(body.slice(start, index).trim())\n start = index + 1\n }\n }\n parts.push(body.slice(start).trim())\n return parts\n}\n\n/**\n * Peel an optional trailing `<num>%` weight off a `color-mix()` color slot.\n * `#ff0000 50%` → `{ color: '#ff0000', weight: 0.5 }`; a bare color → weight\n * `null` (caller fills the complement / defaults to 50/50).\n * @param slot One color argument (color text, optionally suffixed with a percentage).\n * @returns Color text plus its 0–1 weight, or null weight when unspecified.\n */\nfunction parseColorMixSlot(slot: string): { color: string; weight: number | null } {\n const trimmed = slot.trim()\n if (!trimmed.endsWith('%')) return { color: trimmed, weight: null }\n // End-anchored `<num>%` matcher (no leading `.*?` — avoids the super-linear\n // backtracking ESLint flags). Split the color off at the last whitespace\n // before the percentage token.\n const pct = COLOR_MIX_SLOT_PCT.exec(trimmed)\n if (!pct) return { color: trimmed, weight: null }\n const color = trimmed.slice(0, pct.index).trim()\n if (color.length === 0) return { color: trimmed, weight: null }\n const weight = Number(pct[1]) / 100\n return { color, weight: Number.isFinite(weight) ? weight : null }\n}\n\n/** End-anchored `<num>%` matcher for slicing a color-mix slot's weight off its right edge. No backtracking. */\n \nconst COLOR_MIX_SLOT_PCT = /\\s(-?\\d+(?:\\.\\d+)?)%$/\n\n/**\n * Resolve a two-color CSS `color-mix(in <space>, A [p1%], B [p2%])` to a\n * concrete sRGB color via culori's {@link interpolate}. CSS weight rules:\n * with one percentage the other fills the complement; with none it is 50/50;\n * with both, the interpolation point is `p2 / (p1 + p2)`. RN can't evaluate\n * `color-mix()` at paint time, so this is the only path that lowers it.\n * @param text Trimmed CSS value beginning with `color-mix(`.\n * @returns sRGB hex/rgba string, or null when the shape/colors can't resolve.\n */\nfunction resolveColorMix(text: string): string | null {\n if (!text.endsWith(')')) return null\n const open = text.indexOf('(')\n if (open === -1) return null\n const args = splitColorMixArgs(text.slice(open + 1, -1))\n if (args.length !== 3) return null\n const spaceClause = args[0]!.toLowerCase()\n if (!spaceClause.startsWith('in ')) return null\n const mode = colorMixModeFor(spaceClause.slice(3).trim())\n const first = parseColorMixSlot(args[1]!)\n const second = parseColorMixSlot(args[2]!)\n if (first.color.length === 0 || second.color.length === 0) return null\n const point = colorMixPoint(first.weight, second.weight)\n if (point === null) return null\n try {\n const mixed = interpolate([first.color, second.color], mode as never)(point)\n if (!mixed) return null\n const back = culoriRgb(mixed) as { r?: number; g?: number; b?: number; alpha?: number } | undefined\n if (!back || ![back.r, back.g, back.b].every((v) => typeof v === 'number' && Number.isFinite(v))) return null\n const alpha = typeof back.alpha === 'number' ? back.alpha : 1\n return rgbIntsToString(clampByte(back.r! * 255), clampByte(back.g! * 255), clampByte(back.b! * 255), alpha)\n } catch {\n // culori threw on an unparseable color slot — drop rather than leak the raw string.\n return null\n }\n}\n\n/**\n * Compute the 0–1 interpolation point (weight of the SECOND color) from the\n * two optional `color-mix()` weights, applying CSS normalization.\n * @param firstWeight 0–1 weight of color A, or null when unspecified.\n * @param secondWeight 0–1 weight of color B, or null when unspecified.\n * @returns Interpolation point in `[0, 1]`, or null when both weights are 0.\n */\nfunction colorMixPoint(firstWeight: number | null, secondWeight: number | null): number | null {\n if (firstWeight === null && secondWeight === null) return 0.5\n if (firstWeight !== null && secondWeight === null) return 1 - firstWeight\n if (firstWeight === null && secondWeight !== null) return secondWeight\n const sum = firstWeight! + secondWeight!\n if (sum === 0) return null\n return secondWeight! / sum\n}\n\n/**\n * CSS-wide cascade keywords that resolve a property against the inherited /\n * initial / previous-layer value at paint time. React Native has NO color\n * cascade — there is no inherited `color` for an arbitrary style prop and no\n * cascade layers — so as a `color` / `backgroundColor` / `borderColor` value\n * every one of these reaches RN as an invalid color string. `currentColor`\n * belongs here too: it resolves to the element's inherited `color`, which RN\n * never threads into other color props. The color path must DROP these (omit\n * the key) rather than leak the keyword. NOTE: `transparent` is NOT here — it\n * is a real color that {@link cssColorToString} / {@link normalizeColorString}\n * lower to `rgba(0, 0, 0, 0)`, which RN paints correctly.\n */\nconst CSS_WIDE_COLOR_KEYWORDS: ReadonlySet<string> = new Set([\n 'currentcolor',\n 'inherit',\n 'initial',\n 'unset',\n 'revert',\n 'revert-layer',\n])\n\n/**\n * Modern CSS color functions RN's native view manager can't paint —\n * everything else (hex, `rgb()`/`rgba()`, `hsl()`/`hsla()`, named colors,\n * `transparent`) RN reads directly and must pass through untouched. The\n * CSS-wide cascade keywords (`currentColor`, `inherit`, …) are NOT readable —\n * they have no RN equivalent and are dropped via {@link isCssWideColorKeyword}.\n * Custom `@theme` tokens reach the parser as `var(--color-x)` (only the default\n * palette is `theme(inline)`-d), so they flow through the unparsed-string path\n * where the typed {@link cssColorToString} never runs — this is the one place\n * that lowers their wide-gamut values to sRGB. `color-mix(` is in the list too,\n * but it takes the dedicated {@link resolveColorMix} path — culori's `rgb()`\n * parser can't read it.\n */\nconst RN_UNREADABLE_COLOR_PREFIXES: readonly string[] = ['oklch(', 'oklab(', 'lab(', 'lch(', 'color(', 'hwb(', 'color-mix(']\n\n/**\n * Lower a wide-gamut / modern CSS color STRING (`oklch(…)`, `lab(…)`,\n * `color(display-p3 …)`, `color-mix(…)`) to an sRGB hex/rgba string RN can\n * paint. Returns `null` for anything RN already understands (hex, rgb, hsl,\n * named) so the caller keeps the original text — only the unrepresentable\n * forms convert. `color-mix()` is resolved via culori's interpolator; when it\n * (or any other modern form) can't resolve, returns null so the caller DROPS\n * the value rather than leaking the raw, RN-unreadable string.\n * Mirrors {@link cssColorToString}'s culori lowering for the string path.\n * @param text Resolved CSS color text (post theme-var substitution).\n * @returns sRGB color string, or `null` when no conversion is needed/possible.\n */\nexport function normalizeColorString(text: string): string | null {\n const trimmed = text.trim()\n const lower = trimmed.toLowerCase()\n if (!RN_UNREADABLE_COLOR_PREFIXES.some((prefix) => lower.startsWith(prefix))) return null\n if (lower.startsWith('color-mix(')) return resolveColorMix(trimmed)\n const parsed = culoriRgb(text)\n if (!parsed || ![parsed.r, parsed.g, parsed.b].every((v) => typeof v === 'number' && Number.isFinite(v))) return null\n const alpha = typeof parsed.alpha === 'number' ? parsed.alpha : 1\n return rgbIntsToString(clampByte(parsed.r * 255), clampByte(parsed.g * 255), clampByte(parsed.b * 255), alpha)\n}\n\n/**\n * Whether a resolved color STRING is a CSS-wide cascade keyword\n * (`currentColor`, `inherit`, `initial`, `unset`, `revert`, `revert-layer`)\n * with no React Native equivalent. RN has no color cascade, so the color path\n * must DROP (omit the key) when this is true rather than emit the keyword —\n * RN would otherwise receive an invalid color string and render nothing.\n * `transparent` is NOT a cascade keyword: it is a concrete color the converters\n * lower to `rgba(0, 0, 0, 0)`, so it returns false here and resolves normally.\n * @param text Resolved color text (post theme-var substitution / typed-color stringification).\n * @returns True when the value is an RN-unrepresentable CSS-wide keyword.\n */\nexport function isCssWideColorKeyword(text: string): boolean {\n return CSS_WIDE_COLOR_KEYWORDS.has(text.trim().toLowerCase())\n}\n\n/**\n * Convert a lightningcss `CssColor` to an RN-safe color string. RGB\n * passes through unchanged. LAB / LCH / OKLAB / OKLCH / `color(xyz-…)`\n * forms go through culori to reach sRGB — RN's native view manager only\n * understands sRGB-family strings. SystemColor keywords (`'background'`,\n * `'canvas'`, …) pass through untouched; they have no RN analog and the\n * runtime ignores unknown color strings gracefully.\n * @param color Typed color value.\n * @returns Color string RN accepts.\n */\nexport function cssColorToString(color: CssColor): string {\n if (typeof color === 'string') return color\n switch (color.type) {\n case 'rgb': {\n return rgbIntsToString(color.r, color.g, color.b, color.alpha)\n }\n case 'lab':\n case 'lch':\n case 'oklab':\n case 'oklch': {\n return labFamilyToHex(color)\n }\n case 'srgb': {\n return floatRgbToString(color.r, color.g, color.b, color.alpha)\n }\n case 'srgb-linear': {\n return wideGamutToHex('lrgb', color.r, color.g, color.b, color.alpha)\n }\n case 'display-p3': {\n return wideGamutToHex('p3', color.r, color.g, color.b, color.alpha)\n }\n case 'a98-rgb': {\n return wideGamutToHex('a98', color.r, color.g, color.b, color.alpha)\n }\n case 'prophoto-rgb': {\n return wideGamutToHex('prophoto', color.r, color.g, color.b, color.alpha)\n }\n case 'rec2020': {\n return wideGamutToHex('rec2020', color.r, color.g, color.b, color.alpha)\n }\n case 'xyz-d50':\n case 'xyz-d65': {\n return xyzToHex(color)\n }\n case 'currentcolor': {\n return 'currentColor'\n }\n case 'light-dark': {\n // `light-dark(L, D)` is a RUNTIME CSS function — the browser picks the\n // branch from the element's `color-scheme`. rnwind has no runtime CSS\n // evaluation, and the active scheme is NOT threaded into this typed\n // converter (it takes a bare `CssColor`, and every one of its ~15 call\n // sites — border / shorthand / gradient / declaration dispatchers — calls\n // it without a scheme). Scheme resolution instead happens UPSTREAM, at the\n // CSS-block walk (`@custom-variant` + `.dark {}` selectors in\n // theme-vars.ts), which compiles a separate atom + var table per scheme.\n // So the `.light` branch is the correct compile-time default here; the\n // dark value is carried by the scheme-specific atom, not by this function.\n return cssColorToString(color.light)\n }\n default: {\n return 'transparent'\n }\n }\n}\n"],"names":["culoriRgb"],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { kebabToCamel } from './case-convert.mjs';
|
|
2
|
-
import { cssColorToString, normalizeColorString } from './color.mjs';
|
|
2
|
+
import { cssColorToString, isCssWideColorKeyword, normalizeColorString } from './color.mjs';
|
|
3
3
|
import { lengthPercentageOrAutoToValue, dimensionPercentageToNumber, gapValueToValue, sizeLikeToValue } from './length.mjs';
|
|
4
4
|
import { flexToEntries, expandBorderRadius, expandLogicalBlock, expandLogicalInline, expandFourSided, expandGap, expandBorderColor } from './shorthand.mjs';
|
|
5
5
|
import { serializeTokens, substituteThemeVars, coerceUnparsedValue, coerceFontFamily } from './tokens.mjs';
|
|
@@ -43,6 +43,10 @@ const RN_UNSUPPORTED_PROPERTIES = new Set([
|
|
|
43
43
|
'clear',
|
|
44
44
|
'table-layout',
|
|
45
45
|
'caption-side',
|
|
46
|
+
// Web table-model props with no RN equivalent. `border-spacing` otherwise
|
|
47
|
+
// reaches the generic fallback and leaks an unresolved `calc(0.25rem * N)`.
|
|
48
|
+
'border-spacing',
|
|
49
|
+
'border-collapse',
|
|
46
50
|
'transform-style',
|
|
47
51
|
'background-blend-mode',
|
|
48
52
|
'scroll-behavior',
|
|
@@ -59,6 +63,20 @@ const RN_UNSUPPORTED_PROPERTIES = new Set([
|
|
|
59
63
|
'field-sizing',
|
|
60
64
|
'forced-color-adjust',
|
|
61
65
|
'text-shadow',
|
|
66
|
+
// Web-only KEYS RN has no style prop for. `order` leaks through the negative
|
|
67
|
+
// variant (`-order-1` → `order: calc(1 * -1)` unparsed → resolves to `-1`);
|
|
68
|
+
// the positive `order-*` already drops since no typed branch claims it. Adding
|
|
69
|
+
// it here drops BOTH signs. `isolation` (`isolate` / `isolation-auto`) reaches
|
|
70
|
+
// the `custom` path as `isolation: isolate|auto` — also no RN equivalent.
|
|
71
|
+
'order',
|
|
72
|
+
'isolation',
|
|
73
|
+
// `normal-nums` reaches the `custom` path as `font-variant-numeric: normal`
|
|
74
|
+
// and leaked the non-RN key `fontVariantNumeric`. RN expresses numeric
|
|
75
|
+
// variants via the `fontVariant` array, not this property — drop it. (The
|
|
76
|
+
// `tabular-nums`/`oldstyle-nums`/… utilities carry their token in dropped
|
|
77
|
+
// `--tw-numeric-*` vars and already resolve to {}; mapping those to
|
|
78
|
+
// `fontVariant` is a tracked future enhancement, not a leak.)
|
|
79
|
+
'font-variant-numeric',
|
|
62
80
|
'touch-action',
|
|
63
81
|
'backdrop-filter',
|
|
64
82
|
'-webkit-backdrop-filter',
|
|
@@ -67,6 +85,23 @@ const RN_UNSUPPORTED_PROPERTIES = new Set([
|
|
|
67
85
|
'-webkit-font-smoothing',
|
|
68
86
|
'-moz-osx-font-smoothing',
|
|
69
87
|
]);
|
|
88
|
+
/**
|
|
89
|
+
* Valid value sets for RN enum style props (keyed by the camelCase RN key).
|
|
90
|
+
* A value outside its prop's set is RN-invalid even when the string itself
|
|
91
|
+
* looks clean — RN ignores or warns on it (`position: 'fixed'`, `display:
|
|
92
|
+
* 'contents'`, `justifyContent: 'stretch' | 'baseline'`, `alignContent:
|
|
93
|
+
* 'normal'`). This is the dimension the leak-shape (`var(`/`calc(`/NaN) check
|
|
94
|
+
* misses. Both the typed `display` / `position` branches AND the generic
|
|
95
|
+
* unparsed fallback consult this — Tailwind routes some keyword-only values
|
|
96
|
+
* (`justify-content: baseline`) through the unparsed channel, which would
|
|
97
|
+
* otherwise emit them via `kebabToCamel` with no enum awareness.
|
|
98
|
+
*/
|
|
99
|
+
const RN_ENUM_VALUES = {
|
|
100
|
+
position: new Set(['absolute', 'relative', 'static']),
|
|
101
|
+
display: new Set(['flex', 'none']),
|
|
102
|
+
justifyContent: new Set(['flex-start', 'flex-end', 'center', 'space-between', 'space-around', 'space-evenly']),
|
|
103
|
+
alignContent: new Set(['flex-start', 'flex-end', 'center', 'stretch', 'space-between', 'space-around', 'space-evenly']),
|
|
104
|
+
};
|
|
70
105
|
/** CSS single-sided logical-inline property → RN writing-direction Yoga key. */
|
|
71
106
|
const LOGICAL_INLINE_TO_RN = {
|
|
72
107
|
'margin-inline-start': 'marginStart',
|
|
@@ -239,6 +274,11 @@ function unparsedToEntries(property, tokens, themeVars) {
|
|
|
239
274
|
// multi-token shorthand, not a color: drop it.
|
|
240
275
|
if (hasTopLevelSpace(coerced))
|
|
241
276
|
return [];
|
|
277
|
+
// CSS-wide cascade keywords (`inherit`, `currentColor`, `initial`, `unset`,
|
|
278
|
+
// `revert`, `revert-layer`) have no RN equivalent — RN has no color
|
|
279
|
+
// cascade. Drop rather than leak an invalid color string to RN.
|
|
280
|
+
if (isCssWideColorKeyword(coerced))
|
|
281
|
+
return [];
|
|
242
282
|
// Lower modern color spaces (`oklch(…)`, `lab(…)`, `color(p3 …)`) that
|
|
243
283
|
// RN can't paint to sRGB; hex/rgb/hsl/named pass through unchanged.
|
|
244
284
|
const color = normalizeColorString(coerced) ?? coerced;
|
|
@@ -249,7 +289,15 @@ function unparsedToEntries(property, tokens, themeVars) {
|
|
|
249
289
|
return sides.map((key) => [key, color]);
|
|
250
290
|
return [[kebabToCamel(property), color]];
|
|
251
291
|
}
|
|
252
|
-
|
|
292
|
+
const camelKey = kebabToCamel(property);
|
|
293
|
+
// Enum props whose value Tailwind sometimes routes through the unparsed
|
|
294
|
+
// channel (`justify-content: baseline` → `justifyContent: 'baseline'`),
|
|
295
|
+
// bypassing the typed dispatcher's keyword map. RN rejects values outside
|
|
296
|
+
// the prop's set, so gate them here exactly like the typed branches do.
|
|
297
|
+
const enumValues = RN_ENUM_VALUES[camelKey];
|
|
298
|
+
if (enumValues && typeof coerced === 'string' && !enumValues.has(coerced))
|
|
299
|
+
return [];
|
|
300
|
+
return [[camelKey, coerced]];
|
|
253
301
|
}
|
|
254
302
|
/**
|
|
255
303
|
* Convert one lightningcss `Declaration` into zero-or-more RN style
|
|
@@ -290,7 +338,13 @@ function declarationToRnEntries(decl, themeVars) {
|
|
|
290
338
|
// literal keyword means UA default. Skip the keyword.
|
|
291
339
|
if (typeof decl.value === 'string')
|
|
292
340
|
return [];
|
|
293
|
-
|
|
341
|
+
const colorString = cssColorToString(decl.value);
|
|
342
|
+
// `currentColor` (lightningcss `{type:'currentcolor'}`) and any other
|
|
343
|
+
// CSS-wide cascade keyword have no RN equivalent — drop instead of
|
|
344
|
+
// leaking the keyword string to RN.
|
|
345
|
+
if (isCssWideColorKeyword(colorString))
|
|
346
|
+
return [];
|
|
347
|
+
return [[kebabToCamel(decl.property), colorString]];
|
|
294
348
|
}
|
|
295
349
|
case 'border-color': {
|
|
296
350
|
return expandBorderColor(decl.value);
|
|
@@ -372,10 +426,14 @@ function declarationToRnEntries(decl, themeVars) {
|
|
|
372
426
|
return [['fontStyle', decl.value.type]];
|
|
373
427
|
}
|
|
374
428
|
case 'display': {
|
|
375
|
-
|
|
429
|
+
// `displayToEntries` can still emit `contents` (a CSS value RN rejects —
|
|
430
|
+
// only `flex` / `none` are valid). Gate the result on the RN-valid set.
|
|
431
|
+
return displayToEntries(decl.value).filter(([, value]) => typeof value === 'string' && RN_ENUM_VALUES.display.has(value));
|
|
376
432
|
}
|
|
377
433
|
case 'position': {
|
|
378
|
-
|
|
434
|
+
// RN `position` accepts only `absolute` / `relative` / `static`; CSS
|
|
435
|
+
// `fixed` / `sticky` are invalid for RN, so drop them.
|
|
436
|
+
return RN_ENUM_VALUES.position.has(decl.value.type) ? [['position', decl.value.type]] : [];
|
|
379
437
|
}
|
|
380
438
|
case 'font-size': {
|
|
381
439
|
const px = fontSizeToPx(decl.value);
|