@symbiote-native/css-parser 0.5.0 → 0.5.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 CHANGED
@@ -19,7 +19,8 @@ CSS Modules all work identically regardless of source language.
19
19
  Not installed directly by an app — every adapter package (`@symbiote-native/react`,
20
20
  `@symbiote-native/vue`, `@symbiote-native/svelte`, `@symbiote-native/solid`,
21
21
  `@symbiote-native/angular`) already depends on it and re-exports it via its own
22
- `./metro-css-parser` subpath. Writing a Metro transformer for a new adapter yourself:
22
+ `./metro-css-parser` subpath, so any app scaffolded by `npx @symbiote-native/cli new` already has
23
+ it wired, no separate flag needed. Writing a Metro transformer for a new adapter yourself:
23
24
 
24
25
  ```bash
25
26
  npm install @symbiote-native/css-parser
@@ -52,7 +53,7 @@ import './theme.css'; // plain CSS — registers classes globally, no export
52
53
  ```
53
54
 
54
55
  ```tsx
55
- <View className="card" style={styles.highlight} /> // React
56
+ <view className="card" style={styles.highlight} /> // React
56
57
  ```
57
58
 
58
59
  ```html
@@ -135,6 +136,10 @@ import {
135
136
  - It does not implement Tailwind CSS — that needs whole-project class scanning and JIT utility
136
137
  generation, a fundamentally different shape than "one source file reduces to CSS text", and is
137
138
  being designed as a separate, future package.
139
+ - It drops a `:active`/`:hover`/`:focus` rule the same as any other pseudo-class — there is no
140
+ CSS-class route to a pressed look. `Pressable`'s functional `style={({ pressed }) => ...}` prop
141
+ is the supported way to style a pressed state; the engine resolves it into a separate style slot
142
+ outside this pipeline.
138
143
  - It supports `scoped` / `:global()` / CSS Modules and SCSS/Sass/Less/Stylus preprocessing —
139
144
  including Svelte's own `<style>` block (its preprocessor calls this package's
140
145
  `compileScopedCss`, the same compile every Vue `<style scoped>` block goes through) — and it does
@@ -756,13 +756,9 @@ function declarationToStyleAt(declaration, context, depth) {
756
756
  }
757
757
  case 'text-shadow':
758
758
  return textShadowToStyle(value, context);
759
- // A CSS-Modules DIRECTIVE, not a style property — and lightningcss has ALREADY acted on it:
760
- // the composed names come back through `exports[...].composes`, which
761
- // `metro-css-module/index.ts` walks to flatten a chain. Measured 2026-08-20: it arrives as a
762
- // first-class `property: 'composes'`, NOT under `custom` where an unknown name goes, so it fell
763
- // through to the PROPERTY_TABLE miss and every author of a WORKING `.module.*` file was told
764
- // "unsupported CSS property" on every build. The drop warnings are the only signal that a real
765
- // rule died; a channel that cries wolf on working code is the one nobody reads when it matters.
759
+ // A CSS-Modules DIRECTIVE, not a style property: the composed names come back through
760
+ // `exports[...].composes`, which `metro-css-module/index.ts` flattens. It arrives as a
761
+ // first-class `property: 'composes'`, NOT under `custom`, so it must be matched here.
766
762
  case 'composes':
767
763
  return {};
768
764
  default:
@@ -12,8 +12,8 @@
12
12
  // Class names are emitted AS AUTHORED (`card-title` stays `card-title`, plus whatever the scoping
13
13
  // rename appended). No camelCase anywhere in this file: normalization is what the trap was made of.
14
14
  //
15
- // Measured 2026-08-20 and the reason this is ONE pass, not two: with `cssModules` on, the visitor
16
- // still sees the ORIGINAL class names — lightningcss renames AFTER the visitor walk. So the AST
15
+ // The reason this is ONE pass, not two: with `cssModules` on, the visitor still sees the
16
+ // ORIGINAL class names — lightningcss renames AFTER the visitor walk. So the AST
17
17
  // gives authored tokens and `exports` gives their renamed spelling, and the renamed CSS text never
18
18
  // has to be parsed a second time (which is exactly what `../metro-css-module/index.ts` had to do,
19
19
  // and what mangled a scope tail whose base36 hash began with a letter).
@@ -28,10 +28,9 @@ import { selectorsToMatches } from "./selectors.js";
28
28
  import { warnOnce } from "../values.js";
29
29
  // A conditional at-rule is DROPPED WHOLE, its nested rules with it. There is no media-query engine
30
30
  // in React Native, so applying `.responsive` from `@media (min-width: 900px)` would paint it on
31
- // every phone — worse than not supporting the rule, because it looks supported. The retired text
32
- // pass dropped these too, but silently: it simply never walked into an at-rule. Measured
33
- // 2026-08-20: returning `[]` from the at-rule visitor removes it BEFORE the walk descends, so a
34
- // nested style rule never reaches the collector; without it lightningcss hoists it out.
31
+ // every phone — worse than not supporting the rule, because it looks supported. Returning `[]`
32
+ // from the at-rule visitor removes it BEFORE the walk descends, so a nested style rule never
33
+ // reaches the collector; without it lightningcss hoists it out.
35
34
  //
36
35
  // `@keyframes` and `@font-face` need no entry here — neither emits a style rule to begin with.
37
36
  const CONDITIONAL_AT_RULES = ['media', 'supports', 'container'];
@@ -11,7 +11,7 @@
11
11
  // .card[data-x]{…} -> key `card[dataX]` a key no element can ever carry
12
12
  // .a.b / .a .b / .a>.b / .a+.b -> all key `aB` five selectors, one key, merged
13
13
  //
14
- // (Traps six and seven in `.claude/rules/style-registry-collisions.md`, measured 2026-08-20.)
14
+ // (Traps six and seven in `.claude/rules/style-registry-collisions.md`.)
15
15
  //
16
16
  // So this module reports what the selector ACTUALLY says and refuses to guess:
17
17
  // - tokens stay AS AUTHORED — `card-title` is `card-title`. Casing is the caller's problem, and
@@ -32,8 +32,7 @@
32
32
  // a raw token stream — structurally the same thing `:global(X)` is under that same mode. All three
33
33
  // are folded into `deep` here.
34
34
  //
35
- // `:global(X)` arrives in TWO DIFFERENT SHAPES and BOTH are live, so both are handled (measured
36
- // 2026-08-20, lightningcss 1.32, same CSS through both modes):
35
+ // `:global(X)` arrives in TWO DIFFERENT SHAPES and BOTH are live, so both are handled:
37
36
  //
38
37
  // cssModules OFF {kind:'custom-function', name:'global', arguments:[…raw token stream…]}
39
38
  // cssModules ON {kind:'global', selector:[…parsed SelectorComponent[]…]}
@@ -60,11 +59,9 @@ const DROP_EXPLANATION = {
60
59
  // `:active` support is OFF, and the selector machinery below is kept intact so one line turns it
61
60
  // back on.
62
61
  //
63
- // WHY. The pressed look has a second, better route that did not exist when `:active` landed: a
64
- // functional `style={({pressed}) => …}`, which every lowering transform specialises into
65
- // `style` + `activeStyle` at build time (2026-08-23). It reaches the same slot with no pseudo-class
66
- // machinery, it is what the ecosystem already writes, and it lowers — so the reason `:active`
67
- // existed, keeping a Pressable lowerable without a state-reading callback, is gone.
62
+ // WHY. The pressed look has a second, better route: a functional `style={({pressed}) => …}`,
63
+ // which the engine resolves into `style` + `activeStyle` (`routeProp`). It reaches the same slot
64
+ // with no pseudo-class machinery and it is what the ecosystem already writes.
68
65
  //
69
66
  // Keeping BOTH live is what argues against it: they occupy different cascade slots (`activeStyle`
70
67
  // replaces the authored style, an `:active` class rule replaces the class style), so an adapter has
@@ -371,13 +368,9 @@ function consumeComponent(builder, component) {
371
368
  // depends on for every node at once.
372
369
  if (component.kind === STATE_PSEUDO_CLASS) {
373
370
  builder.specificity[1]++;
374
- // ...but NOT through a scope boundary. `:deep(.b:active)` already dropped, because a
375
- // custom-function payload is a raw token stream this walk re-parses; `.a >>> .b:active`
376
- // did NOT, because `>>>` is a real combinator and the walk reaches the pseudo-class
377
- // normally. Two spellings of one relation behaving differently is the bug, and the
378
- // decision (2026-08-23) is to refuse BOTH: a deep selector reaches into another
379
- // component's internals, and the state token is only meaningful on the node whose press
380
- // machine owns it — which is exactly the node a deep rule cannot predict.
371
+ // ...but NOT through a scope boundary. Both `:deep(.b:active)` and `.a >>> .b:active`
372
+ // refuse: a deep selector reaches into another component's internals, and the state
373
+ // token is only meaningful on the node whose press machine owns it.
381
374
  if (builder.combinators.includes('deep') ||
382
375
  builder.pending === 'deep') {
383
376
  drop(builder, 'pseudo-class', 'active through a deep combinator');
@@ -452,7 +445,7 @@ export function selectorsToMatches(selectors, filename) {
452
445
  // and that is FALSE here — the pressed look is fully supported, by a different route. A
453
446
  // warning that misdescribes the cause is worse than none: it sends the reader to the engine.
454
447
  if (problem.reason === 'state-pseudo-class') {
455
- console.warn(`[@symbiote-native/css-parser] ${filename}: dropped \`:${problem.detail}\` — pseudo-class state is currently disabled in this parser. Use a functional style instead: style={({ pressed }) => ({ opacity: pressed ? 0.6 : 1 })}, which every lowering transform compiles to style + activeStyle.`);
448
+ console.warn(`[@symbiote-native/css-parser] ${filename}: dropped \`:${problem.detail}\` — pseudo-class state is currently disabled in this parser. Use a functional style instead: style={({ pressed }) => ({ opacity: pressed ? 0.6 : 1 })}, which the engine resolves to style + activeStyle.`);
456
449
  continue;
457
450
  }
458
451
  console.warn(`[@symbiote-native/css-parser] ${filename}: dropped a rule on \`${problem.detail}\` — ${DROP_EXPLANATION[problem.reason]}, so it can never match in React Native.`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@symbiote-native/css-parser",
3
- "version": "0.5.0",
3
+ "version": "0.5.2",
4
4
  "description": "Build-time CSS/SCSS/Less/Stylus compiler for SymbioteNative — compiles stylesheets to React Native style objects, resolved at runtime via a cross-adapter class-name registry.",
5
5
  "license": "MIT",
6
6
  "repository": {