@ng-native/fabric 0.14.0 → 0.16.0

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/dist/css.d.ts CHANGED
@@ -667,6 +667,8 @@ export declare const styleStats: {
667
667
  nodesResolved: number;
668
668
  /** Rules filed by key selector: each sheet's once, however many sheets come after it. */
669
669
  rulesFiled: number;
670
+ /** Nodes asked whether a rule `:has()` was asked for could be for them: see `isFor`. */
671
+ subjectTests: number;
670
672
  };
671
673
  export declare function resetStyleStats(): void;
672
674
  /**
@@ -721,6 +723,11 @@ export interface PlaceReader {
721
723
  * from (`.a:first-child + .b`), is that element's own: the rules it matches say.
722
724
  */
723
725
  export declare function placeReadElsewhere(sheet: StyleSheet): readonly PlaceReader[];
726
+ /**
727
+ * The props a sheet's rules ask of an element before another in a child list, `[data-on] ~ .x`:
728
+ * those whose change can restyle the elements after it. An id is asked as `nativeID`.
729
+ */
730
+ export declare function namedBeside(sheet: StyleSheet): ReadonlySet<string>;
724
731
  /** One rule in a cascade: the sheet it came from, which `:host` needs, and its weight here. */
725
732
  interface RuleEntry {
726
733
  readonly rule: StyleRule;
@@ -789,6 +796,16 @@ export interface Subjects {
789
796
  * matched again itself. Unset where the rules are only for elements under it.
790
797
  */
791
798
  own?: boolean;
799
+ /**
800
+ * In what a class reaches: a rule names it in `:has()`, `.field:has(.on)`, which the boxes
801
+ * above the element that has it are then asked again.
802
+ */
803
+ beneath?: boolean;
804
+ /**
805
+ * In what a class reaches: a rule names it in `:has(> )` alone, `.field:has(> .on)`, which only
806
+ * the box right above the element that has it is then asked again.
807
+ */
808
+ parentHas?: boolean;
792
809
  }
793
810
  /**
794
811
  * A box a rule asks `:has()` of that is not the node it styles, `.card:has(.x) .title`, by the
@@ -801,6 +818,8 @@ export interface HasAbove {
801
818
  }
802
819
  /** Each box a sheet's rules ask `:has()` of above the node they style. */
803
820
  export declare function hasAbove(sheet: StyleSheet): readonly HasAbove[];
821
+ /** What all of `each` are for, as one. */
822
+ export declare function joinSubjects(each: readonly Subjects[]): Subjects;
804
823
  /** Whether an inline style sets a property children inherit. Allocates nothing: it runs per set. */
805
824
  export declare function setsInherited(style: unknown): boolean;
806
825
  /**
@@ -928,8 +947,11 @@ export declare class StyleResolver {
928
947
  */
929
948
  private readonly deepSheets;
930
949
  private readonly deepIndexes;
931
- /** Read the classes a sheet names, once, as soon as any node is given it. */
932
- noteSheet(sheet: StyleSheet | null | undefined): void;
950
+ /**
951
+ * Read the classes a sheet names, once, as soon as any node is given it. One `scoped` to a
952
+ * component or a host asks of its elements for good, global or not.
953
+ */
954
+ noteSheet(sheet: StyleSheet | null | undefined, scoped?: boolean): void;
933
955
  /** The props a selector of some sheet asks about: an id, and each attribute by its name. */
934
956
  private readonly named;
935
957
  /**
@@ -937,6 +959,18 @@ export declare class StyleResolver {
937
959
  * sheet that was ever loaded names it. A sheet loaded later has every element matched again.
938
960
  */
939
961
  reads(prop: string): boolean;
962
+ /**
963
+ * By prop, how many sheets in play ask it of an element before another: see `namedBeside`. A
964
+ * global sheet taken away, or replaced, asks no more.
965
+ */
966
+ private readonly besideNamed;
967
+ private readonly besideNoted;
968
+ /** The sheets a component or a host was given: see `noteSheet`. */
969
+ private readonly scoped;
970
+ private noteBeside;
971
+ private forgetBeside;
972
+ /** Whether a prop changing can change what the elements after its element match. */
973
+ readsBeside(prop: string): boolean;
940
974
  private noteReach;
941
975
  /** Whether a class's reach names any element under the one that has it. */
942
976
  reachesUnder(subjects: Subjects): boolean;
@@ -947,6 +981,8 @@ export declare class StyleResolver {
947
981
  * of them and nothing is styled again.
948
982
  */
949
983
  reachOf(classes: Iterable<string>): true | Subjects | null;
984
+ /** How far a class reaches by its name, and by each test of the class attribute it answers. */
985
+ private reachesOf;
950
986
  /** Whether one of the rules `subjects` stands for could be for a node. */
951
987
  isFor(subjects: Subjects, node: StyleTarget): boolean;
952
988
  /**
@@ -972,6 +1008,9 @@ export declare class StyleResolver {
972
1008
  * change it was marked on the element, its parent's `scope` is the one it was styled under,
973
1009
  * and no sheet that could match it has come since. It is being styled again all the same, for
974
1010
  * a change of its own that alters nothing under it, or for what it inherits.
1011
+ *
1012
+ * A change beneath it is not one: what it answers `:has()` is asked again, and the elements a
1013
+ * rule asking that of it is for are marked on their own (`markUnderHas`), not all under it.
975
1014
  */
976
1015
  private matchesStand;
977
1016
  /**
@@ -1179,9 +1218,12 @@ export declare class StyleResolver {
1179
1218
  * the compiler writes for the same value in a rule. `token` is the value as a token, which is an
1180
1219
  * alias when it is a `var()`; null for any other.
1181
1220
  *
1182
- * ponytail: the whole value has to be the `var()`. One inside a longer value, `calc()` included,
1183
- * is what the compiler parses at build time and nothing here does; write the sum in a custom
1184
- * property, `[style.--gap]`, and read that in the stylesheet.
1221
+ * A colour made of tokens, `color-mix(in srgb, var(--brand), white 20%)`, is worked out from the
1222
+ * tokens where it is used, as a fallback of the same shape is.
1223
+ *
1224
+ * ponytail: any other value has to be the whole `var()`. One inside a longer value, `calc()`
1225
+ * included, is what the compiler parses at build time and nothing here does; write the sum in a
1226
+ * custom property, `[style.--gap]`, and read that in the stylesheet.
1185
1227
  */
1186
1228
  export declare function boundDeclaration(prop: string, token: TokenValue | undefined, on?: string): DeferredDeclaration | null;
1187
1229
  /** Whether `text` is a `line-height` below zero, which a browser drops: `-1.5`, `-1em`, `-2px`. */
package/dist/css.js CHANGED
@@ -208,12 +208,15 @@ export const styleStats = {
208
208
  nodesResolved: 0,
209
209
  /** Rules filed by key selector: each sheet's once, however many sheets come after it. */
210
210
  rulesFiled: 0,
211
+ /** Nodes asked whether a rule `:has()` was asked for could be for them: see `isFor`. */
212
+ subjectTests: 0,
211
213
  };
212
214
  export function resetStyleStats() {
213
215
  styleStats.compoundTests = 0;
214
216
  styleStats.ruleTests = 0;
215
217
  styleStats.nodesResolved = 0;
216
218
  styleStats.rulesFiled = 0;
219
+ styleStats.subjectTests = 0;
217
220
  }
218
221
  /** A prop's value as an attribute test compares it: in lower case, under the i flag. */
219
222
  const attributeText = (actual, test) => test.insensitive ? String(actual).toLowerCase() : String(actual);
@@ -640,6 +643,36 @@ export function placeReadElsewhere(sheet) {
640
643
  }
641
644
  return known;
642
645
  }
646
+ const NAMED_BESIDE = new WeakMap();
647
+ /**
648
+ * The props a sheet's rules ask of an element before another in a child list, `[data-on] ~ .x`:
649
+ * those whose change can restyle the elements after it. An id is asked as `nativeID`.
650
+ */
651
+ export function namedBeside(sheet) {
652
+ let named = NAMED_BESIDE.get(sheet);
653
+ if (named === undefined) {
654
+ named = new Set();
655
+ for (const rule of sheet.rules) {
656
+ rule.combinators.forEach((joined, at) => {
657
+ if (joined === 'next-sibling' || joined === 'later-sibling') {
658
+ propsNamed(rule.compounds[at], named);
659
+ }
660
+ });
661
+ }
662
+ NAMED_BESIDE.set(sheet, named);
663
+ }
664
+ return named;
665
+ }
666
+ /** Add each prop a compound, or one nested in it at any depth, asks about: an attribute or an id. */
667
+ function propsNamed(compound, into) {
668
+ if (compound.id !== undefined)
669
+ into.add('nativeID');
670
+ for (const test of compound.attributes ?? [])
671
+ into.add(test.name);
672
+ const within = (compound.has ?? []).flatMap((test) => test.any);
673
+ for (const inner of [...nestedCompounds(compound), ...within])
674
+ propsNamed(inner, into);
675
+ }
643
676
  /** Whether a compound, or one nested in it at any depth, asks about a place in a child list. */
644
677
  function readsPlace(compound) {
645
678
  if (compound.nth?.length)
@@ -1187,6 +1220,13 @@ function importantNames(result, twin) {
1187
1220
  }
1188
1221
  /** What `em` falls back to when nothing in scope has set a font size. The web's default. */
1189
1222
  const DEFAULT_FONT_SIZE = 16;
1223
+ /**
1224
+ * The rules a node matched, to style it from again: for a change above it, and not for one of
1225
+ * its own or beneath it, which can change what it matches.
1226
+ */
1227
+ function rulesKept(cached, node) {
1228
+ return node.ownDirty || node.hasDirty ? undefined : cached.rules;
1229
+ }
1190
1230
  /** Shared empty result. Never handed out anywhere that mutates it. */
1191
1231
  const EMPTY = {};
1192
1232
  /** Stands in for the parent of a root node, so the comparison needs no null case. */
@@ -1287,14 +1327,19 @@ function classReach(sheet) {
1287
1327
  for (const rule of sheet.rules) {
1288
1328
  const every = new Set();
1289
1329
  collectClasses(rule, every, reads, named);
1290
- const { own, inside } = classPlaces(rule);
1330
+ const { own, inside, beneath, parentHas } = classPlaces(rule);
1291
1331
  for (const name of every) {
1292
- if (!own.has(name) && !inside.has(name))
1332
+ const placed = own.has(name) || inside.has(name) || beneath.has(name) || parentHas.has(name);
1333
+ if (!placed)
1293
1334
  classes.set(name, true);
1294
1335
  if (own.has(name))
1295
1336
  noteOwn(classes, name);
1296
1337
  if (inside.has(name))
1297
1338
  noteInside(classes, name, rule);
1339
+ if (beneath.has(name))
1340
+ noteBeneath(classes, name, 'beneath');
1341
+ if (parentHas.has(name))
1342
+ noteBeneath(classes, name, 'parentHas');
1298
1343
  }
1299
1344
  }
1300
1345
  reaches.set(sheet, (found = { classes, reads, named }));
@@ -1310,6 +1355,16 @@ function noteOwn(found, name) {
1310
1355
  else
1311
1356
  found.set(name, { ...noSubjects(), own: true });
1312
1357
  }
1358
+ /** Note a rule as one that names a class in `:has()`, or in `:has(> )` alone. */
1359
+ function noteBeneath(found, name, how) {
1360
+ const subjects = found.get(name);
1361
+ if (subjects === true)
1362
+ return;
1363
+ if (subjects)
1364
+ subjects[how] = true;
1365
+ else
1366
+ found.set(name, { ...noSubjects(), [how]: true });
1367
+ }
1313
1368
  /** Note a rule as one that names a class as what its element is inside. */
1314
1369
  function noteInside(found, name, rule) {
1315
1370
  let subjects = found.get(name);
@@ -1333,8 +1388,6 @@ const noSubjects = () => ({
1333
1388
  inside: new Set(),
1334
1389
  any: false,
1335
1390
  });
1336
- /** The reach of classes that rules are for the element of, and for nothing under it. */
1337
- const OWN_ALONE = { ...noSubjects(), own: true };
1338
1391
  /** Add to `into` what `from` is for. */
1339
1392
  function addSubjects(into, from) {
1340
1393
  for (const name of from.classes)
@@ -1346,6 +1399,17 @@ function addSubjects(into, from) {
1346
1399
  into.any ||= from.any;
1347
1400
  if (from.own)
1348
1401
  into.own = true;
1402
+ if (from.beneath)
1403
+ into.beneath = true;
1404
+ if (from.parentHas)
1405
+ into.parentHas = true;
1406
+ }
1407
+ /** What all of `each` are for, as one. */
1408
+ export function joinSubjects(each) {
1409
+ const all = noSubjects();
1410
+ for (const one of each)
1411
+ addSubjects(all, one);
1412
+ return all;
1349
1413
  }
1350
1414
  /**
1351
1415
  * Where a rule names each class it names, for the classes it names in no other place than these:
@@ -1354,21 +1418,29 @@ function addSubjects(into, from) {
1354
1418
  * it, which are asked of that element;
1355
1419
  * - `inside`, as what that element is inside: the classes of a compound before the last, with
1356
1420
  * nothing but `>` and a space between there and the element, and those of what `:is(.box *)`
1357
- * and `:is(.box > *)` put over it.
1421
+ * and `:is(.box > *)` put over it;
1422
+ * - `beneath`, inside `:has()`, wherever in the rule that is: what it asks of is under the box
1423
+ * that answers it;
1424
+ * - `parentHas`, of what `:has(> )` asks of the box's children themselves, and nowhere else in
1425
+ * `:has()`: the element is right under that box.
1358
1426
  *
1359
- * A class named anywhere else in the rule is in neither, whatever else names it: beside the
1360
- * element, inside `:has()` or `:host-context()`, or in `:not()` of a box the element is in.
1427
+ * A class named anywhere else in the rule is in none, whatever else names it: beside the element,
1428
+ * inside `:host-context()`, or in `:not()` of a box the element is in. A test of the class
1429
+ * attribute is placed as a class is, by `classTestKey`.
1361
1430
  */
1362
1431
  function classPlaces(rule) {
1363
1432
  const own = new Set();
1364
1433
  const inside = new Set();
1434
+ const beneath = new Set();
1435
+ const parentHas = new Set();
1365
1436
  const elsewhere = new Set();
1437
+ const below = { beneath, parentHas, elsewhere };
1366
1438
  const last = rule.compounds.length - 1;
1367
1439
  let under = true;
1368
1440
  for (let index = last; index >= 0; index--) {
1369
1441
  const compound = rule.compounds[index];
1370
1442
  if (index === last) {
1371
- placeClasses(compound, own, inside, elsewhere);
1443
+ placeClasses(compound, own, inside, below);
1372
1444
  continue;
1373
1445
  }
1374
1446
  const joined = rule.combinators[index];
@@ -1376,36 +1448,75 @@ function classPlaces(rule) {
1376
1448
  // A box the element is under, by its classes and by those it is asked to have or not to,
1377
1449
  // `.box:not(.busy) .row`: each is asked of that box, as its own are.
1378
1450
  const over = under ? inside : elsewhere;
1379
- placeClasses(compound, over, over, elsewhere);
1451
+ placeClasses(compound, over, over, below);
1380
1452
  }
1381
1453
  for (const name of elsewhere) {
1382
1454
  own.delete(name);
1383
1455
  inside.delete(name);
1456
+ beneath.delete(name);
1457
+ parentHas.delete(name);
1384
1458
  }
1385
- return { own, inside };
1459
+ return { own, inside, beneath, parentHas };
1386
1460
  }
1387
1461
  /**
1388
1462
  * The classes of a compound asked of the element a rule is for, into `at`, and of the compounds
1389
1463
  * it puts over that element, into `inside`. What any other part of it names goes `elsewhere`:
1390
1464
  * a part read here by name is one this knows the reach of, and one added later is not.
1391
1465
  */
1392
- function placeClasses(compound, at, inside, elsewhere) {
1393
- const { classes, is, not, ancestors, parents, type: _t, id: _i, root: _r, pseudo: _p, ...rest } = compound;
1394
- for (const name of classes)
1466
+ function placeClasses(compound, at, inside, below) {
1467
+ const { classes, is, not, ancestors, parents, attributes, has, type: _t, id: _i, root: _r, pseudo: _p, ...rest } = compound;
1468
+ for (const name of namesOf(classes, attributes))
1395
1469
  at.add(name);
1396
1470
  for (const alternatives of is ?? []) {
1397
1471
  for (const option of alternatives)
1398
- placeClasses(option, at, inside, elsewhere);
1472
+ placeClasses(option, at, inside, below);
1399
1473
  }
1400
1474
  for (const excluded of not ?? [])
1401
- placeClasses(excluded, at, inside, elsewhere);
1475
+ placeClasses(excluded, at, inside, below);
1402
1476
  // What is over the element is over it whichever of these said so, and what is over that is too.
1403
1477
  for (const above of [...(ancestors ?? []), ...(parents ?? [])]) {
1404
- placeClasses(above, inside, inside, elsewhere);
1478
+ placeClasses(above, inside, inside, below);
1405
1479
  }
1406
- // `attributes` among them: a test of the `class` attribute is answered through `classTests`.
1407
- collectClasses(rest, elsewhere, []);
1480
+ placeHas(has, below);
1481
+ collectClasses(rest, below.elsewhere, []);
1408
1482
  }
1483
+ /**
1484
+ * The classes `:has()` asks of what is under the box that answers it, into `beneath`, wherever
1485
+ * in the rule it is written, or into `parentHas` for what `:has(> )` asks of the box's children.
1486
+ * One it can ask of what is above the box goes `elsewhere`: `:is()` in `:has()` is not relative,
1487
+ * so the `.night` of `:has(:is(.night .cell))` can be any element over the cell.
1488
+ */
1489
+ function placeHas(tests, below) {
1490
+ for (const test of tests ?? []) {
1491
+ const into = test.child ? below.parentHas : below.beneath;
1492
+ for (const compound of test.any)
1493
+ placeBeneath(compound, into, below);
1494
+ }
1495
+ }
1496
+ /** `placeHas` for one compound of a `:has()` argument, whose own classes go `into`. */
1497
+ function placeBeneath(compound, into, below) {
1498
+ const { classes, attributes, is, not, has, type: _t, id: _i, root: _r, pseudo: _p, ...rest } = compound;
1499
+ for (const name of namesOf(classes, attributes))
1500
+ into.add(name);
1501
+ for (const alternatives of is ?? []) {
1502
+ for (const option of alternatives)
1503
+ placeBeneath(option, into, below);
1504
+ }
1505
+ for (const excluded of not ?? [])
1506
+ placeBeneath(excluded, into, below);
1507
+ placeHas(has, below);
1508
+ collectClasses(rest, below.elsewhere, []);
1509
+ }
1510
+ /** A compound's classes, and its tests of the class attribute, which are asked as its classes are. */
1511
+ function namesOf(classes, attributes) {
1512
+ const tests = attributes?.filter((test) => test.name === 'class');
1513
+ return tests?.length ? [...classes, ...tests.map(classTestKey)] : classes;
1514
+ }
1515
+ /**
1516
+ * A test of the class attribute, `[class^="p-"]`, by a name no class can have: it has a reach
1517
+ * as a class does, for what any class it answers can restyle.
1518
+ */
1519
+ const classTestKey = (test) => `\0${test.operator ?? ''}${test.insensitive ? 'i' : ''}\0${test.value ?? ''}`;
1409
1520
  /** What a rule declares, which names no element. */
1410
1521
  const DECLARED = new Set(['declarations', 'important', 'tokens']);
1411
1522
  /**
@@ -1425,8 +1536,10 @@ function collectClasses(value, into, reads, named) {
1425
1536
  if (Array.isArray(part.attributes)) {
1426
1537
  for (const test of part.attributes) {
1427
1538
  named?.add(test.name);
1428
- if (test.name === 'class')
1429
- reads.push(test);
1539
+ if (test.name !== 'class')
1540
+ continue;
1541
+ reads.push(test);
1542
+ into.add(classTestKey(test));
1430
1543
  }
1431
1544
  }
1432
1545
  for (const [key, inner] of Object.entries(value)) {
@@ -1725,8 +1838,11 @@ export class StyleResolver {
1725
1838
  const at = replacing ? this.addedSheets.indexOf(replacing) : -1;
1726
1839
  if (at === -1)
1727
1840
  this.addedSheets.push(sheet);
1728
- else
1841
+ else {
1729
1842
  this.addedSheets[at] = sheet;
1843
+ this.forgetBeside(replacing);
1844
+ }
1845
+ this.noteBeside(sheet);
1730
1846
  // One added at the end is filed after what is there. One in another's place files them all again.
1731
1847
  if (at !== -1)
1732
1848
  this.filed = null;
@@ -1753,7 +1869,7 @@ export class StyleResolver {
1753
1869
  /** How far each class named by the sheets met so far reaches: see `classReach`. */
1754
1870
  reach = new Map();
1755
1871
  /** The tests of the `class` attribute itself in those sheets, which any class may answer. */
1756
- classTests = [];
1872
+ classTests = new Map();
1757
1873
  noted = new WeakSet();
1758
1874
  /**
1759
1875
  * The sheets with a rule written with `::ng-deep`, which is for what is under the component
@@ -1761,9 +1877,17 @@ export class StyleResolver {
1761
1877
  */
1762
1878
  deepSheets = new Set();
1763
1879
  deepIndexes = new WeakMap();
1764
- /** Read the classes a sheet names, once, as soon as any node is given it. */
1765
- noteSheet(sheet) {
1766
- if (!sheet || this.noted.has(sheet))
1880
+ /**
1881
+ * Read the classes a sheet names, once, as soon as any node is given it. One `scoped` to a
1882
+ * component or a host asks of its elements for good, global or not.
1883
+ */
1884
+ noteSheet(sheet, scoped = false) {
1885
+ if (!sheet)
1886
+ return;
1887
+ if (scoped)
1888
+ this.scoped.add(sheet);
1889
+ this.noteBeside(sheet);
1890
+ if (this.noted.has(sheet))
1767
1891
  return;
1768
1892
  this.noted.add(sheet);
1769
1893
  if (sheet.rules.some((rule) => rule.deep))
@@ -1771,7 +1895,8 @@ export class StyleResolver {
1771
1895
  const { classes, reads, named } = classReach(sheet);
1772
1896
  for (const [name, how] of classes)
1773
1897
  this.noteReach(name, how);
1774
- this.classTests.push(...reads);
1898
+ for (const test of reads)
1899
+ this.classTests.set(classTestKey(test), test);
1775
1900
  for (const name of named)
1776
1901
  this.named.add(name);
1777
1902
  }
@@ -1784,6 +1909,37 @@ export class StyleResolver {
1784
1909
  reads(prop) {
1785
1910
  return this.named.has(prop);
1786
1911
  }
1912
+ /**
1913
+ * By prop, how many sheets in play ask it of an element before another: see `namedBeside`. A
1914
+ * global sheet taken away, or replaced, asks no more.
1915
+ */
1916
+ besideNamed = new Map();
1917
+ besideNoted = new WeakSet();
1918
+ /** The sheets a component or a host was given: see `noteSheet`. */
1919
+ scoped = new WeakSet();
1920
+ noteBeside(sheet) {
1921
+ if (this.besideNoted.has(sheet))
1922
+ return;
1923
+ this.besideNoted.add(sheet);
1924
+ for (const name of namedBeside(sheet)) {
1925
+ this.besideNamed.set(name, (this.besideNamed.get(name) ?? 0) + 1);
1926
+ }
1927
+ }
1928
+ forgetBeside(sheet) {
1929
+ if (this.scoped.has(sheet) || !this.besideNoted.delete(sheet))
1930
+ return;
1931
+ for (const name of namedBeside(sheet)) {
1932
+ const left = this.besideNamed.get(name) - 1;
1933
+ if (left)
1934
+ this.besideNamed.set(name, left);
1935
+ else
1936
+ this.besideNamed.delete(name);
1937
+ }
1938
+ }
1939
+ /** Whether a prop changing can change what the elements after its element match. */
1940
+ readsBeside(prop) {
1941
+ return this.besideNamed.has(prop);
1942
+ }
1787
1943
  noteReach(name, how) {
1788
1944
  const had = this.reach.get(name);
1789
1945
  if (had === true)
@@ -1807,28 +1963,29 @@ export class StyleResolver {
1807
1963
  * of them and nothing is styled again.
1808
1964
  */
1809
1965
  reachOf(classes) {
1810
- let under = null;
1811
- let own = false;
1966
+ let found = null;
1812
1967
  for (const name of classes) {
1813
- const how = this.reach.get(name);
1814
- if (how === true || this.classTests.some((test) => answers(test, name)))
1815
- return true;
1816
- if (!how)
1817
- continue;
1818
- own ||= how.own === true;
1819
- // Made only for a class that reaches under: most are a rule's own, and this is asked for
1820
- // every element as it is given its classes.
1821
- if (this.reachesUnder(how))
1822
- addSubjects((under ??= noSubjects()), how);
1968
+ for (const how of this.reachesOf(name)) {
1969
+ if (how === true)
1970
+ return true;
1971
+ addSubjects((found ??= noSubjects()), how);
1972
+ }
1973
+ }
1974
+ return found;
1975
+ }
1976
+ /** How far a class reaches by its name, and by each test of the class attribute it answers. */
1977
+ *reachesOf(name) {
1978
+ const how = this.reach.get(name);
1979
+ if (how)
1980
+ yield how;
1981
+ for (const [key, test] of this.classTests) {
1982
+ if (answers(test, name))
1983
+ yield this.reach.get(key) ?? true;
1823
1984
  }
1824
- if (!under)
1825
- return own ? OWN_ALONE : null;
1826
- if (own)
1827
- under.own = true;
1828
- return under;
1829
1985
  }
1830
1986
  /** Whether one of the rules `subjects` stands for could be for a node. */
1831
1987
  isFor(subjects, node) {
1988
+ styleStats.subjectTests++;
1832
1989
  return writtenFor(subjects, node);
1833
1990
  }
1834
1991
  /**
@@ -1860,6 +2017,7 @@ export class StyleResolver {
1860
2017
  if (at === -1)
1861
2018
  return false;
1862
2019
  this.addedSheets.splice(at, 1);
2020
+ this.forgetBeside(sheet);
1863
2021
  this.filed = null;
1864
2022
  this.additions.length = 0;
1865
2023
  this.generation = ++generations;
@@ -1919,8 +2077,7 @@ export class StyleResolver {
1919
2077
  return this.unstyled(node, epoch, parent, parentInherited, scope, parentScope);
1920
2078
  }
1921
2079
  styleStats.nodesResolved++;
1922
- // Its own rules are found again for a change of its own, and not for one above it.
1923
- const rules = stands && !node.ownDirty ? cached.rules : undefined;
2080
+ const rules = stands ? rulesKept(cached, node) : undefined;
1924
2081
  const matched = found ?? rules ?? this.matchedBy(node);
1925
2082
  const parentTokens = parent ? parent.tokens : this.tokensOnRoot;
1926
2083
  this.heir(node, parent, matched);
@@ -1938,11 +2095,13 @@ export class StyleResolver {
1938
2095
  * change it was marked on the element, its parent's `scope` is the one it was styled under,
1939
2096
  * and no sheet that could match it has come since. It is being styled again all the same, for
1940
2097
  * a change of its own that alters nothing under it, or for what it inherits.
2098
+ *
2099
+ * A change beneath it is not one: what it answers `:has()` is asked again, and the elements a
2100
+ * rule asking that of it is for are marked on their own (`markUnderHas`), not all under it.
1941
2101
  */
1942
2102
  matchesStand(cached, node, parentScope) {
1943
2103
  return (cached !== null &&
1944
2104
  !node.styleDirty &&
1945
- !node.hasDirty &&
1946
2105
  !node.stateDirty &&
1947
2106
  cached.scope !== SHARED_SCOPE &&
1948
2107
  parentScope !== SHARED_SCOPE &&
@@ -3298,14 +3457,17 @@ function boundKind(prop) {
3298
3457
  * the compiler writes for the same value in a rule. `token` is the value as a token, which is an
3299
3458
  * alias when it is a `var()`; null for any other.
3300
3459
  *
3301
- * ponytail: the whole value has to be the `var()`. One inside a longer value, `calc()` included,
3302
- * is what the compiler parses at build time and nothing here does; write the sum in a custom
3303
- * property, `[style.--gap]`, and read that in the stylesheet.
3460
+ * A colour made of tokens, `color-mix(in srgb, var(--brand), white 20%)`, is worked out from the
3461
+ * tokens where it is used, as a fallback of the same shape is.
3462
+ *
3463
+ * ponytail: any other value has to be the whole `var()`. One inside a longer value, `calc()`
3464
+ * included, is what the compiler parses at build time and nothing here does; write the sum in a
3465
+ * custom property, `[style.--gap]`, and read that in the stylesheet.
3304
3466
  */
3305
3467
  export function boundDeclaration(prop, token, on) {
3306
- if (!token?.alias)
3307
- return null;
3308
3468
  const kind = boundKind(prop);
3469
+ if (!token?.alias)
3470
+ return derivedColour(prop, token, kind, on);
3309
3471
  const [reference, ...alternatives] = aliasesOf(token);
3310
3472
  for (; token?.alias; token = token.fallback)
3311
3473
  ;
@@ -3362,6 +3524,12 @@ export function boundRelativeLength(prop, text, on) {
3362
3524
  compute: { unit: 'em', factor: Number(unitless[1]), multiple: true },
3363
3525
  };
3364
3526
  }
3527
+ /** A bound colour made of tokens, worked out where it is used; null for any other value. */
3528
+ function derivedColour(prop, token, kind, on) {
3529
+ if (!token || kind !== 'color' || !isDerived(token))
3530
+ return null;
3531
+ return { props: [prop], ...(on === undefined ? {} : { on }), kind, fallbackToken: token };
3532
+ }
3365
3533
  /** The names a `var()` tries in turn: its own, then each `var()` nested as its fallback. */
3366
3534
  function aliasesOf(token) {
3367
3535
  const names = [];