@react-x11/components 0.13.0 → 0.14.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.
Files changed (128) hide show
  1. package/dist/html/controls.d.ts +29 -2
  2. package/dist/html/controls.d.ts.map +1 -1
  3. package/dist/html/controls.js +70 -2
  4. package/dist/html/controls.js.map +1 -1
  5. package/dist/html/css/cascade.d.ts.map +1 -1
  6. package/dist/html/css/cascade.js +26 -4
  7. package/dist/html/css/cascade.js.map +1 -1
  8. package/dist/html/css/color.d.ts +18 -0
  9. package/dist/html/css/color.d.ts.map +1 -1
  10. package/dist/html/css/color.js +27 -0
  11. package/dist/html/css/color.js.map +1 -1
  12. package/dist/html/css/parse.d.ts +18 -4
  13. package/dist/html/css/parse.d.ts.map +1 -1
  14. package/dist/html/css/parse.js +66 -28
  15. package/dist/html/css/parse.js.map +1 -1
  16. package/dist/html/css/style.d.ts +128 -15
  17. package/dist/html/css/style.d.ts.map +1 -1
  18. package/dist/html/css/style.js +561 -144
  19. package/dist/html/css/style.js.map +1 -1
  20. package/dist/html/css/transform.d.ts +85 -0
  21. package/dist/html/css/transform.d.ts.map +1 -0
  22. package/dist/html/css/transform.js +425 -0
  23. package/dist/html/css/transform.js.map +1 -0
  24. package/dist/html/css/ua.d.ts.map +1 -1
  25. package/dist/html/css/ua.js +14 -2
  26. package/dist/html/css/ua.js.map +1 -1
  27. package/dist/html/css/values.d.ts +4 -0
  28. package/dist/html/css/values.d.ts.map +1 -1
  29. package/dist/html/css/values.js.map +1 -1
  30. package/dist/html/fonts.d.ts +55 -5
  31. package/dist/html/fonts.d.ts.map +1 -1
  32. package/dist/html/fonts.js +284 -23
  33. package/dist/html/fonts.js.map +1 -1
  34. package/dist/html/layout/axes.d.ts +14 -0
  35. package/dist/html/layout/axes.d.ts.map +1 -0
  36. package/dist/html/layout/axes.js +28 -0
  37. package/dist/html/layout/axes.js.map +1 -0
  38. package/dist/html/layout/block.d.ts +93 -12
  39. package/dist/html/layout/block.d.ts.map +1 -1
  40. package/dist/html/layout/block.js +389 -39
  41. package/dist/html/layout/block.js.map +1 -1
  42. package/dist/html/layout/boxes.d.ts +59 -1
  43. package/dist/html/layout/boxes.d.ts.map +1 -1
  44. package/dist/html/layout/boxes.js +22 -2
  45. package/dist/html/layout/boxes.js.map +1 -1
  46. package/dist/html/layout/cache.d.ts +1 -1
  47. package/dist/html/layout/cache.d.ts.map +1 -1
  48. package/dist/html/layout/cache.js +6 -0
  49. package/dist/html/layout/cache.js.map +1 -1
  50. package/dist/html/layout/css-grid.d.ts.map +1 -1
  51. package/dist/html/layout/css-grid.js +90 -18
  52. package/dist/html/layout/css-grid.js.map +1 -1
  53. package/dist/html/layout/flex.d.ts.map +1 -1
  54. package/dist/html/layout/flex.js +570 -83
  55. package/dist/html/layout/flex.js.map +1 -1
  56. package/dist/html/layout/inline.d.ts +3 -0
  57. package/dist/html/layout/inline.d.ts.map +1 -1
  58. package/dist/html/layout/inline.js +144 -54
  59. package/dist/html/layout/inline.js.map +1 -1
  60. package/dist/html/layout/multicol.d.ts +42 -0
  61. package/dist/html/layout/multicol.d.ts.map +1 -0
  62. package/dist/html/layout/multicol.js +897 -0
  63. package/dist/html/layout/multicol.js.map +1 -0
  64. package/dist/html/layout/shaping.d.ts +7 -0
  65. package/dist/html/layout/shaping.d.ts.map +1 -1
  66. package/dist/html/layout/shaping.js +25 -20
  67. package/dist/html/layout/shaping.js.map +1 -1
  68. package/dist/html/layout/table.d.ts.map +1 -1
  69. package/dist/html/layout/table.js +62 -18
  70. package/dist/html/layout/table.js.map +1 -1
  71. package/dist/html/node.d.ts +3 -0
  72. package/dist/html/node.d.ts.map +1 -1
  73. package/dist/html/node.js +263 -36
  74. package/dist/html/node.js.map +1 -1
  75. package/dist/html/paint.d.ts +79 -4
  76. package/dist/html/paint.d.ts.map +1 -1
  77. package/dist/html/paint.js +1213 -141
  78. package/dist/html/paint.js.map +1 -1
  79. package/dist/html/svg.d.ts +15 -2
  80. package/dist/html/svg.d.ts.map +1 -1
  81. package/dist/html/svg.js +249 -17
  82. package/dist/html/svg.js.map +1 -1
  83. package/dist/html/widgets.js +87 -7
  84. package/dist/html/widgets.js.map +1 -1
  85. package/dist/html/woff2.d.ts +14 -0
  86. package/dist/html/woff2.d.ts.map +1 -0
  87. package/dist/html/woff2.js +608 -0
  88. package/dist/html/woff2.js.map +1 -0
  89. package/dist/maps/controller.d.ts +17 -1
  90. package/dist/maps/controller.d.ts.map +1 -1
  91. package/dist/maps/controller.js +27 -7
  92. package/dist/maps/controller.js.map +1 -1
  93. package/dist/richtext/node.d.ts +8 -1
  94. package/dist/richtext/node.d.ts.map +1 -1
  95. package/dist/richtext/node.js +5 -1
  96. package/dist/richtext/node.js.map +1 -1
  97. package/dist/richtext/runs.d.ts +25 -6
  98. package/dist/richtext/runs.d.ts.map +1 -1
  99. package/dist/richtext/runs.js +240 -24
  100. package/dist/richtext/runs.js.map +1 -1
  101. package/package.json +1 -1
  102. package/src/html/controls.ts +85 -2
  103. package/src/html/css/cascade.ts +23 -0
  104. package/src/html/css/color.ts +29 -0
  105. package/src/html/css/parse.ts +71 -31
  106. package/src/html/css/style.ts +664 -147
  107. package/src/html/css/transform.ts +459 -0
  108. package/src/html/css/ua.ts +15 -2
  109. package/src/html/css/values.ts +4 -0
  110. package/src/html/fonts.ts +312 -29
  111. package/src/html/layout/axes.ts +62 -0
  112. package/src/html/layout/block.ts +444 -37
  113. package/src/html/layout/boxes.ts +65 -3
  114. package/src/html/layout/cache.ts +6 -0
  115. package/src/html/layout/css-grid.ts +95 -22
  116. package/src/html/layout/flex.ts +671 -77
  117. package/src/html/layout/inline.ts +149 -55
  118. package/src/html/layout/multicol.ts +977 -0
  119. package/src/html/layout/shaping.ts +27 -18
  120. package/src/html/layout/table.ts +62 -19
  121. package/src/html/node.ts +300 -37
  122. package/src/html/paint.ts +1406 -206
  123. package/src/html/svg.ts +277 -19
  124. package/src/html/widgets.ts +95 -9
  125. package/src/html/woff2.ts +612 -0
  126. package/src/maps/controller.ts +28 -7
  127. package/src/richtext/node.ts +13 -2
  128. package/src/richtext/runs.ts +313 -35
@@ -15,6 +15,7 @@
15
15
  import { AUTO, alphaOf, fourSides, inkColor, isTransparent, keywordFontSize, parseAlpha, parseColor, isPct, parseLength, parseNumber, parseWeight, resolve, splitCommas, splitValue, } from './values.js';
16
16
  import { parseUrl, readIdent, startsIdent } from './parse.js';
17
17
  import { DEFAULT_QUOTES, parseContent, parseCounterList, parseListStyleType, parseQuotes, } from './content.js';
18
+ import { parseRotate, parseScale, parseTransform } from './transform.js';
18
19
  import { LIGHT_DARK, lightDark, usedColorScheme } from './color.js';
19
20
  /** Whether a length is one `padding` takes: not `auto`, not negative. A
20
21
  * `calc()` with a percentage in it has no sign until layout, which clamps
@@ -58,6 +59,93 @@ function parseClip(value, ctx) {
58
59
  }
59
60
  return { top: edges[0], right: edges[1], bottom: edges[2], left: edges[3] };
60
61
  }
62
+ /** The boxes `clip-path` names; an SVG one is the CSS box that stands for
63
+ * it on an element with a CSS layout box (5.1). */
64
+ const CLIP_BOXES = {
65
+ 'margin-box': 'margin-box',
66
+ 'border-box': 'border-box',
67
+ 'padding-box': 'padding-box',
68
+ 'content-box': 'content-box',
69
+ 'fill-box': 'content-box',
70
+ 'stroke-box': 'border-box',
71
+ 'view-box': 'border-box',
72
+ };
73
+ /** The shapes that are no rectangle, and a reference to a `<clipPath>`
74
+ * element: a value, which sets the property, and none this draws. */
75
+ const UNDRAWN_CLIPS = /^(circle|ellipse|polygon|path|shape|url)\(/i;
76
+ const NO_LENGTHS = [0, 0, 0, 0];
77
+ /**
78
+ * `none`; a rectangle's function, a box, or the two in either order; or a
79
+ * shape that is no rectangle, which is a value and clips nothing here.
80
+ * Undefined for anything else, which leaves the value as it was.
81
+ */
82
+ function parseClipPath(value, ctx) {
83
+ const v = value.trim();
84
+ if (/^none$/i.test(v))
85
+ return null;
86
+ let box = null;
87
+ let shape;
88
+ for (const part of splitValue(v)) {
89
+ const named = CLIP_BOXES[part.toLowerCase()];
90
+ if (named) {
91
+ if (box)
92
+ return undefined;
93
+ box = named;
94
+ continue;
95
+ }
96
+ if (shape !== undefined)
97
+ return undefined;
98
+ if (UNDRAWN_CLIPS.test(part) && part.endsWith(')'))
99
+ shape = null;
100
+ else
101
+ shape = rectShape(part, ctx);
102
+ if (shape === undefined)
103
+ return undefined;
104
+ }
105
+ if (shape === null)
106
+ return null;
107
+ if (shape)
108
+ return { box: box ?? 'border-box', ...shape };
109
+ if (!box)
110
+ return undefined;
111
+ return { box, shape: null, lengths: NO_LENGTHS, radii: null, radiiY: null };
112
+ }
113
+ /** `inset()`, `rect()` or `xywh()`, with the corners it rounds; undefined
114
+ * for anything else. */
115
+ function rectShape(text, ctx) {
116
+ const m = /^(inset|rect|xywh)\((.*)\)$/is.exec(text);
117
+ if (!m)
118
+ return undefined;
119
+ const shape = m[1].toLowerCase();
120
+ const parts = splitValue(m[2].trim());
121
+ const round = parts.findIndex((p) => p.toLowerCase() === 'round');
122
+ const sides = round < 0 ? parts : parts.slice(0, round);
123
+ let radii = null;
124
+ let radiiY = null;
125
+ if (round >= 0) {
126
+ const corners = parseRadii(parts.slice(round + 1).join(' '), ctx);
127
+ if (!corners)
128
+ return undefined;
129
+ [radii, radiiY] = corners;
130
+ }
131
+ // one to four insets, as a margin's sides are written; the other two
132
+ // take all four
133
+ if (shape === 'inset' ? !sides.length || sides.length > 4 : sides.length !== 4)
134
+ return undefined;
135
+ const lengths = [];
136
+ for (let i = 0; i < sides.length; i += 1) {
137
+ const len = parseLength(sides[i], ctx);
138
+ if (len === null)
139
+ return undefined;
140
+ // only `rect()` has an edge that is `auto`, and a width is not negative
141
+ if (len === AUTO && shape !== 'rect')
142
+ return undefined;
143
+ if (shape === 'xywh' && i > 1 && !notNegative(len))
144
+ return undefined;
145
+ lengths.push(len);
146
+ }
147
+ return { shape, lengths: fourSides(lengths), radii, radiiY };
148
+ }
61
149
  /** The properties that inherit. Named once, so `inherit()` and the `inherit`
62
150
  * keyword cannot disagree about the list. */
63
151
  /** The fields a style takes from its parent's (CSS 2.1's "Inherited:
@@ -92,6 +180,8 @@ export const INHERITED = [
92
180
  'fontFeatureSettings',
93
181
  'tabSize',
94
182
  'tabSizeIsLength',
183
+ 'orphans',
184
+ 'widows',
95
185
  'whiteSpace',
96
186
  'overflowWrap',
97
187
  'wordBreak',
@@ -103,6 +193,9 @@ export const INHERITED = [
103
193
  'listStylePosition',
104
194
  'listStyleImage',
105
195
  'cursor',
196
+ 'pointerEvents',
197
+ 'fill',
198
+ 'stroke',
106
199
  'borderCollapse',
107
200
  'borderSpacing',
108
201
  'borderSpacingY',
@@ -154,6 +247,9 @@ export function initialStyle(look, scale = 1) {
154
247
  listStylePosition: 'outside',
155
248
  listStyleImage: null,
156
249
  cursor: null,
250
+ pointerEvents: 'auto',
251
+ fill: null,
252
+ stroke: null,
157
253
  borderCollapse: 'separate',
158
254
  // CSS's initial value; a `<table>` gets its 2px from the UA sheet, and
159
255
  // an anonymous table, which no sheet names, has none
@@ -188,6 +284,14 @@ export function initialStyle(look, scale = 1) {
188
284
  webkitBoxAlign: null,
189
285
  webkitBoxFlex: 0,
190
286
  columns: 0,
287
+ columnCount: null,
288
+ columnWidth: null,
289
+ columnGapNormal: true,
290
+ orphans: 2,
291
+ widows: 2,
292
+ breakInside: 'auto',
293
+ columnFill: 'balance',
294
+ columnSpan: false,
191
295
  textOverflow: 'clip',
192
296
  aspectRatio: null,
193
297
  objectFit: 'fill',
@@ -197,9 +301,13 @@ export function initialStyle(look, scale = 1) {
197
301
  overflowX: 'visible',
198
302
  overflowY: 'visible',
199
303
  clip: null,
304
+ clipPath: null,
200
305
  opacity: 1,
201
306
  translate: null,
202
- transformTranslate: null,
307
+ rotate: null,
308
+ scale: null,
309
+ transform: null,
310
+ transformOrigin: [{ pct: 50 }, { pct: 50 }],
203
311
  zIndex: AUTO,
204
312
  verticalAlign: 'baseline',
205
313
  width: AUTO,
@@ -283,6 +391,8 @@ export function initialStyle(look, scale = 1) {
283
391
  underlineThickness: null,
284
392
  underlineOffset: null,
285
393
  lineThrough: null,
394
+ lineThroughStyle: 'solid',
395
+ lineThroughThickness: null,
286
396
  flexDirection: 'row',
287
397
  flexWrap: 'nowrap',
288
398
  justifyContent: 'normal',
@@ -381,6 +491,8 @@ export function inherit(parent, initial) {
381
491
  out.fontFeatureSettings = parent.fontFeatureSettings;
382
492
  out.tabSize = parent.tabSize;
383
493
  out.tabSizeIsLength = parent.tabSizeIsLength;
494
+ out.orphans = parent.orphans;
495
+ out.widows = parent.widows;
384
496
  out.whiteSpace = parent.whiteSpace;
385
497
  out.overflowWrap = parent.overflowWrap;
386
498
  out.wordBreak = parent.wordBreak;
@@ -392,6 +504,9 @@ export function inherit(parent, initial) {
392
504
  out.listStylePosition = parent.listStylePosition;
393
505
  out.listStyleImage = parent.listStyleImage;
394
506
  out.cursor = parent.cursor;
507
+ out.pointerEvents = parent.pointerEvents;
508
+ out.fill = parent.fill;
509
+ out.stroke = parent.stroke;
395
510
  out.borderCollapse = parent.borderCollapse;
396
511
  out.borderSpacing = parent.borderSpacing;
397
512
  out.borderSpacingY = parent.borderSpacingY;
@@ -411,6 +526,8 @@ export function inherit(parent, initial) {
411
526
  out.underlineThickness = parent.underlineThickness;
412
527
  out.underlineOffset = parent.underlineOffset;
413
528
  out.lineThrough = parent.lineThrough;
529
+ out.lineThroughStyle = parent.lineThroughStyle;
530
+ out.lineThroughThickness = parent.lineThroughThickness;
414
531
  return out;
415
532
  }
416
533
  /**
@@ -444,6 +561,8 @@ export const FIRST_LINE_INHERITED = [
444
561
  'underlineThickness',
445
562
  'underlineOffset',
446
563
  'lineThrough',
564
+ 'lineThroughStyle',
565
+ 'lineThroughThickness',
447
566
  ];
448
567
  /**
449
568
  * What a box on its block's first line inherits from: its parent's style,
@@ -501,6 +620,14 @@ const BORDER_WIDTH_KEYWORDS = {
501
620
  medium: 3,
502
621
  thick: 5,
503
622
  };
623
+ /** `-webkit-` names that are the multicol properties' own. */
624
+ const MULTICOL_ALIASES = {
625
+ '-webkit-columns': 'columns',
626
+ '-webkit-column-count': 'column-count',
627
+ '-webkit-column-width': 'column-width',
628
+ '-webkit-column-gap': 'column-gap',
629
+ '-webkit-column-break-inside': 'break-inside',
630
+ };
504
631
  /**
505
632
  * Apply one declaration to a style, in place.
506
633
  *
@@ -510,7 +637,12 @@ const BORDER_WIDTH_KEYWORDS = {
510
637
  * correct response to every one of them is to render the rest.
511
638
  */
512
639
  export function applyDeclaration(style, parent, prop, rawValue, ctx) {
513
- const name = prop.toLowerCase();
640
+ const written = prop.toLowerCase();
641
+ // the multicol properties under the names WebKit had them by, which
642
+ // Blink still reads, each an alias of the property (`-webkit-column-
643
+ // count` in its `css_properties.json5`): a page written for it alone
644
+ // names no other
645
+ const name = MULTICOL_ALIASES[written] ?? written;
514
646
  let value = rawValue.trim();
515
647
  if (!value)
516
648
  return;
@@ -645,21 +777,75 @@ export function applyDeclaration(style, parent, prop, rawValue, ctx) {
645
777
  case 'column-count':
646
778
  case 'column-width':
647
779
  case 'columns': {
648
- // `auto`, or a count, a width or both of them
780
+ // `auto`, or a count, a width or both of them, in either order; what
781
+ // the shorthand leaves out is `auto`
649
782
  const bit = name === 'column-count' ? 1 : name === 'column-width' ? 2 : 3;
650
- let set = 0;
783
+ let count = null;
784
+ let width = null;
651
785
  for (const part of splitValue(value)) {
652
786
  const v = part.toLowerCase();
653
787
  if (v === 'auto')
654
788
  continue;
655
- if (/^\d+$/.test(v) && Number(v) >= 1)
656
- set |= 1;
657
- else if (parseLength(part, ctx) !== null)
658
- set |= 2;
659
- else
789
+ if (/^\d+$/.test(v) && Number(v) >= 1) {
790
+ if (count !== null || !(bit & 1))
791
+ return;
792
+ count = Number(v);
793
+ continue;
794
+ }
795
+ const len = parseLength(part, ctx);
796
+ if (typeof len !== 'number' || !(len > 0))
797
+ return;
798
+ if (width !== null || !(bit & 2))
799
+ return;
800
+ width = len;
801
+ }
802
+ if (bit & 1)
803
+ style.columnCount = count;
804
+ if (bit & 2)
805
+ style.columnWidth = width;
806
+ style.columns =
807
+ (style.columnCount !== null ? 1 : 0) |
808
+ (style.columnWidth !== null ? 2 : 0);
809
+ return;
810
+ }
811
+ case 'orphans':
812
+ case 'widows': {
813
+ // a whole number of lines, one or more
814
+ const v = value.trim();
815
+ if (!/^\+?\d+$/.test(v) || Number(v) < 1)
816
+ return;
817
+ style[name] = Number(v);
818
+ return;
819
+ }
820
+ case '-webkit-column-span':
821
+ case 'column-span': {
822
+ const v = value.trim().toLowerCase();
823
+ if (v === 'all')
824
+ style.columnSpan = true;
825
+ else if (v === 'none')
826
+ style.columnSpan = false;
827
+ return;
828
+ }
829
+ case 'column-fill': {
830
+ const v = value.trim().toLowerCase();
831
+ if (v === 'auto')
832
+ style.columnFill = 'auto';
833
+ else if (v === 'balance' || v === 'balance-all') {
834
+ style.columnFill = 'balance';
835
+ }
836
+ return;
837
+ }
838
+ case 'break-inside':
839
+ case 'page-break-inside': {
840
+ const v = value.trim().toLowerCase();
841
+ if (v === 'auto')
842
+ style.breakInside = 'auto';
843
+ else if (/^avoid(?:-page|-column|-region)?$/.test(v)) {
844
+ // the legacy property knows `avoid` alone
845
+ if (name === 'page-break-inside' && v !== 'avoid')
660
846
  return;
847
+ style.breakInside = 'avoid';
661
848
  }
662
- style.columns = (style.columns & ~bit) | (set & bit);
663
849
  return;
664
850
  }
665
851
  case '-webkit-box-flex': {
@@ -927,6 +1113,13 @@ export function applyDeclaration(style, parent, prop, rawValue, ctx) {
927
1113
  style.clip = clip;
928
1114
  return;
929
1115
  }
1116
+ case 'clip-path':
1117
+ case '-webkit-clip-path': {
1118
+ const path = parseClipPath(value, ctx);
1119
+ if (path !== undefined)
1120
+ style.clipPath = path;
1121
+ return;
1122
+ }
930
1123
  case 'opacity': {
931
1124
  const a = parseAlpha(value);
932
1125
  if (a !== null)
@@ -950,10 +1143,38 @@ export function applyDeclaration(style, parent, prop, rawValue, ctx) {
950
1143
  style.translate = [x, y ?? 0];
951
1144
  return;
952
1145
  }
953
- case 'transform': {
954
- const moved = transformTranslation(value, ctx);
955
- if (moved !== undefined)
956
- style.transformTranslate = moved;
1146
+ case 'rotate': {
1147
+ const turn = parseRotate(value);
1148
+ if (turn !== undefined)
1149
+ style.rotate = turn;
1150
+ return;
1151
+ }
1152
+ case 'scale': {
1153
+ const factors = parseScale(value);
1154
+ if (factors !== undefined)
1155
+ style.scale = factors;
1156
+ return;
1157
+ }
1158
+ case 'transform':
1159
+ case '-webkit-transform': {
1160
+ const list = parseTransform(value, ctx);
1161
+ if (list !== undefined)
1162
+ style.transform = list;
1163
+ return;
1164
+ }
1165
+ case 'transform-origin':
1166
+ case '-webkit-transform-origin': {
1167
+ // across and down, as a position is, and a depth nothing here has
1168
+ const parts = splitValue(value);
1169
+ if (parts.length === 3) {
1170
+ if (typeof parseLength(parts.pop(), ctx) !== 'number')
1171
+ return;
1172
+ }
1173
+ else if (parts.length > 2)
1174
+ return;
1175
+ const origin = positionPair(parts, ctx);
1176
+ if (origin)
1177
+ style.transformOrigin = origin;
957
1178
  return;
958
1179
  }
959
1180
  case 'visibility': {
@@ -1242,19 +1463,9 @@ export function applyDeclaration(style, parent, prop, rawValue, ctx) {
1242
1463
  return;
1243
1464
  }
1244
1465
  case 'border-radius': {
1245
- // one to four radii, and after a `/` one to four more for the
1246
- // vertical radii where they differ: elliptical corners
1247
- const halves = value.split('/');
1248
- if (halves.length > 2)
1249
- return;
1250
- const [horizontal, vertical] = halves.map((half) => splitValue(half).map((p) => radiusOf(p, ctx)));
1251
- for (const parts of vertical ? [horizontal, vertical] : [horizontal]) {
1252
- if (!parts.length || parts.length > 4 || parts.includes(null))
1253
- return;
1254
- }
1255
- style.borderRadius = fourSides(horizontal);
1256
- const y = vertical ? fourSides(vertical) : null;
1257
- style.borderRadiusY = y && !sameRadii(style.borderRadius, y) ? y : null;
1466
+ const radii = parseRadii(value, ctx);
1467
+ if (radii)
1468
+ [style.borderRadius, style.borderRadiusY] = radii;
1258
1469
  return;
1259
1470
  }
1260
1471
  // --- background ---------------------------------------------------------
@@ -1509,7 +1720,19 @@ export function applyDeclaration(style, parent, prop, rawValue, ctx) {
1509
1720
  : f.trim().replace(/[ \t\n\r\f]+/g, ' '))
1510
1721
  .filter(Boolean)
1511
1722
  .join(', ');
1512
- style.fontFamily = ctx.families ? ctx.families(list) : list;
1723
+ // A list that ends in no generic family ends in the document's own
1724
+ // font: where no face in the list matches, the text is set in the
1725
+ // user agent's default (CSS Fonts 4, 5.1), which for a browser is its
1726
+ // standard font and here is the one the document is set in. The text
1727
+ // engine's own answer for a name nothing has is its platform's pick,
1728
+ // Verdana under fontconfig: the Zen Garden's 216 sets its summary in
1729
+ // Montserrat alone, which no machine here has, and Chrome sets it in
1730
+ // Times where ours was Verdana, a fifth wider
1731
+ const last = names[names.length - 1].trim();
1732
+ const generic = !/^['"]/.test(last) && GENERIC_FAMILY.has(last.toLowerCase());
1733
+ const fallback = generic ? null : (ctx.fallbackFamily?.() ?? null);
1734
+ const full = fallback ? `${list}, ${fallback}` : list;
1735
+ style.fontFamily = ctx.families ? ctx.families(full) : full;
1513
1736
  return;
1514
1737
  }
1515
1738
  case 'font-size': {
@@ -1933,6 +2156,23 @@ export function applyDeclaration(style, parent, prop, rawValue, ctx) {
1933
2156
  style.cursor = splitCommas(value)[0]?.trim().toLowerCase() || null;
1934
2157
  return;
1935
2158
  }
2159
+ case 'pointer-events': {
2160
+ // `none`, or one of the values SVG gives it, which are `auto` on
2161
+ // anything that is not SVG's to draw
2162
+ const v = value.trim().toLowerCase();
2163
+ if (v === 'none')
2164
+ style.pointerEvents = 'none';
2165
+ else if (POINTER_EVENTS.has(v))
2166
+ style.pointerEvents = 'auto';
2167
+ return;
2168
+ }
2169
+ case 'fill':
2170
+ case 'stroke': {
2171
+ const paint = svgPaint(value);
2172
+ if (paint !== null)
2173
+ style[name] = paint;
2174
+ return;
2175
+ }
1936
2176
  // --- lists --------------------------------------------------------------
1937
2177
  case 'list-style': {
1938
2178
  // a type, a position and an image, each at most once and in any
@@ -2132,8 +2372,11 @@ export function applyDeclaration(style, parent, prop, rawValue, ctx) {
2132
2372
  return;
2133
2373
  if (which !== 'column-gap')
2134
2374
  style.rowGap = row;
2135
- if (which !== 'row-gap')
2375
+ if (which !== 'row-gap') {
2136
2376
  style.columnGap = col;
2377
+ const word = parts[parts.length - 1].toLowerCase();
2378
+ style.columnGapNormal = word === 'normal';
2379
+ }
2137
2380
  return;
2138
2381
  }
2139
2382
  case 'grid-template-columns':
@@ -2605,6 +2848,12 @@ function alignKeyword(value) {
2605
2848
  return 'flex-end';
2606
2849
  case 'first baseline':
2607
2850
  return 'baseline';
2851
+ // By last baselines, which nothing here aligns: set where that falls
2852
+ // back to, the end (CSS Box Alignment 3, 4.2). Dropped, a row that asks
2853
+ // for it stretched its items, and a flex box among them was as tall as
2854
+ // the row, its lines spread down it.
2855
+ case 'last baseline':
2856
+ return 'flex-end';
2608
2857
  case 'center':
2609
2858
  return 'center';
2610
2859
  case 'stretch':
@@ -3226,7 +3475,7 @@ function applyBackgroundShorthand(style, value, ctx) {
3226
3475
  function backgroundImageOf(text, ctx) {
3227
3476
  const v = text.trim();
3228
3477
  if (IMAGE_FUNCTION.test(v.toLowerCase()))
3229
- return parseLinearGradient(v, ctx);
3478
+ return parseGradient(v, ctx);
3230
3479
  return parseUrl(v);
3231
3480
  }
3232
3481
  /** A background property's values, one a comma-separated layer, each read
@@ -3248,7 +3497,7 @@ function layerValues(value, read) {
3248
3497
  * is dropped rather than resetting the background it meant to replace
3249
3498
  * (CSS 2.1 4.2). What CSS3 adds is read too, so that a declaration a
3250
3499
  * browser keeps is kept: `/ cover` after the position, `space` and `round`,
3251
- * a gradient — drawn as nothing, over the layer's colour.
3500
+ * and a gradient, over the layer's colour.
3252
3501
  */
3253
3502
  function readBackgroundLayer(text, ctx, last) {
3254
3503
  const parts = splitValue(text).flatMap(splitSlash);
@@ -3284,7 +3533,7 @@ function readBackgroundLayer(text, ctx, last) {
3284
3533
  layer.image = url;
3285
3534
  }
3286
3535
  else if (v !== 'none')
3287
- layer.gradient = parseLinearGradient(part, ctx);
3536
+ layer.gradient = parseGradient(part, ctx);
3288
3537
  i += 1;
3289
3538
  }
3290
3539
  else if (v === 'repeat-x' || v === 'repeat-y' || REPEATS.has(v)) {
@@ -3363,21 +3612,21 @@ function readBackgroundLayer(text, ctx, last) {
3363
3612
  }
3364
3613
  /**
3365
3614
  * A `linear-gradient()`, or null for any other image function and for one
3366
- * that is no gradient: a direction by angle, by side or by corner, with a
3367
- * colour interpolation method (`in oklab`, which Tailwind 4 writes) read and
3368
- * not honoured, and at least two stops, each with no position, one, or two;
3369
- * a bare percentage between stops, a hint, is passed over.
3615
+ * that is no gradient: a direction by angle, by side or by corner, and its
3616
+ * colour stops.
3370
3617
  */
3371
3618
  function parseLinearGradient(text, ctx) {
3372
3619
  const m = /^linear-gradient\((.*)\)$/is.exec(text.trim());
3373
3620
  if (!m)
3374
3621
  return null;
3375
3622
  const args = splitCommas(m[1]).map((a) => a.trim());
3376
- const out = { angle: Math.PI, corner: null, stops: [] };
3377
- const first = (args[0] ?? '')
3378
- .toLowerCase()
3379
- .replace(/\bin\s+[a-z-]+(?:\s+(?:shorter|longer|increasing|decreasing)\s+hue)?/, '')
3380
- .trim();
3623
+ const out = {
3624
+ kind: 'linear',
3625
+ angle: Math.PI,
3626
+ corner: null,
3627
+ stops: [],
3628
+ };
3629
+ const first = withoutInterpolation(args[0] ?? '');
3381
3630
  let from = 0;
3382
3631
  if (!first || first.startsWith('to ')) {
3383
3632
  from = 1;
@@ -3419,7 +3668,124 @@ function parseLinearGradient(text, ctx) {
3419
3668
  from = 1;
3420
3669
  }
3421
3670
  }
3422
- for (const arg of args.slice(from)) {
3671
+ const stops = parseStops(args.slice(from), ctx);
3672
+ if (!stops)
3673
+ return null;
3674
+ out.stops = stops;
3675
+ return out;
3676
+ }
3677
+ /**
3678
+ * A `radial-gradient()` (CSS Images 3, 3.2.1), or null for anything else:
3679
+ * an ending shape, `circle` or `ellipse`, and its size, in either order —
3680
+ * a keyword for the side or corner of the box it reaches, one length for a
3681
+ * circle's radius, or two lengths or percentages for an ellipse's — then
3682
+ * `at` and its centre, a position as `background-position` writes one;
3683
+ * each of the three may be left out, for an ellipse through the farthest
3684
+ * corner from the middle of the box. A colour interpolation method is read
3685
+ * and not honoured, and the stops are a linear gradient's, spaced along
3686
+ * the ray from the centre to the shape's edge.
3687
+ */
3688
+ function parseRadialGradient(text, ctx) {
3689
+ const m = /^radial-gradient\((.*)\)$/is.exec(text.trim());
3690
+ if (!m)
3691
+ return null;
3692
+ const args = splitCommas(m[1]).map((a) => a.trim());
3693
+ const out = {
3694
+ kind: 'radial',
3695
+ circle: false,
3696
+ extent: null,
3697
+ radii: null,
3698
+ at: [{ pct: 50 }, { pct: 50 }],
3699
+ stops: [],
3700
+ };
3701
+ const first = withoutInterpolation(args[0] ?? '');
3702
+ let from = 0;
3703
+ if (!first)
3704
+ from = 1;
3705
+ else if (parseColor(splitValue(first)[0]) === null) {
3706
+ from = 1;
3707
+ const words = splitValue(first);
3708
+ const at = words.indexOf('at');
3709
+ if (at >= 0) {
3710
+ const position = positionPair(words.slice(at + 1), ctx);
3711
+ if (!position)
3712
+ return null;
3713
+ out.at = position;
3714
+ }
3715
+ let shape = null;
3716
+ const sizes = [];
3717
+ // the size is one run of words, the shape's keyword before or after it
3718
+ let sized = false;
3719
+ for (const word of at >= 0 ? words.slice(0, at) : words) {
3720
+ if (word === 'circle' || word === 'ellipse') {
3721
+ if (shape)
3722
+ return null;
3723
+ shape = word;
3724
+ if (sizes.length || out.extent)
3725
+ sized = true;
3726
+ continue;
3727
+ }
3728
+ if (sized || out.extent)
3729
+ return null;
3730
+ if (RADIAL_EXTENTS.has(word)) {
3731
+ if (sizes.length)
3732
+ return null;
3733
+ out.extent = word;
3734
+ continue;
3735
+ }
3736
+ const len = parseLength(word, ctx);
3737
+ if (len === null || len === AUTO || !notNegative(len))
3738
+ return null;
3739
+ sizes.push(len);
3740
+ }
3741
+ if (sizes.length === 1) {
3742
+ // a circle's radius is a length: a percentage of what, the spec asks
3743
+ const [r] = sizes;
3744
+ if (shape === 'ellipse' || typeof r !== 'number') {
3745
+ return null;
3746
+ }
3747
+ out.circle = true;
3748
+ out.radii = [r, r];
3749
+ }
3750
+ else if (sizes.length === 2) {
3751
+ if (shape === 'circle')
3752
+ return null;
3753
+ out.radii = [sizes[0], sizes[1]];
3754
+ }
3755
+ else if (sizes.length)
3756
+ return null;
3757
+ else {
3758
+ out.circle = shape === 'circle';
3759
+ out.extent ??= 'farthest-corner';
3760
+ }
3761
+ }
3762
+ const stops = parseStops(args.slice(from), ctx);
3763
+ if (!stops)
3764
+ return null;
3765
+ out.stops = stops;
3766
+ return out;
3767
+ }
3768
+ const RADIAL_EXTENTS = new Set([
3769
+ 'closest-side',
3770
+ 'closest-corner',
3771
+ 'farthest-side',
3772
+ 'farthest-corner',
3773
+ ]);
3774
+ /** A gradient's first argument, lower-cased, without the colour
3775
+ * interpolation method CSS Images 4 adds to it (`in oklab`, which
3776
+ * Tailwind 4 writes): read, and not honoured. */
3777
+ function withoutInterpolation(arg) {
3778
+ return arg
3779
+ .toLowerCase()
3780
+ .replace(/\bin\s+[a-z-]+(?:\s+(?:shorter|longer|increasing|decreasing)\s+hue)?/, '')
3781
+ .trim();
3782
+ }
3783
+ /** A gradient's colour stops, at least two, each with no position, one or
3784
+ * two; a bare length between two stops, a hint, is passed over. Null
3785
+ * where an argument is none of those. */
3786
+ function parseStops(args, ctx) {
3787
+ const stops = [];
3788
+ for (const arg of args) {
3423
3789
  const parts = splitValue(arg);
3424
3790
  const color = parseColor(parts[0] ?? '');
3425
3791
  if (color === null) {
@@ -3434,14 +3800,18 @@ function parseLinearGradient(text, ctx) {
3434
3800
  if (at.some((p) => p === null || p === AUTO))
3435
3801
  return null;
3436
3802
  if (!at.length)
3437
- out.stops.push({ color, at: null });
3803
+ stops.push({ color, at: null });
3438
3804
  for (const p of at)
3439
- out.stops.push({ color, at: p });
3805
+ stops.push({ color, at: p });
3440
3806
  }
3441
- return out.stops.length >= 2 ? out : null;
3807
+ return stops.length >= 2 ? stops : null;
3808
+ }
3809
+ /** A gradient function's image, where it is one this draws. */
3810
+ function parseGradient(text, ctx) {
3811
+ return parseLinearGradient(text, ctx) ?? parseRadialGradient(text, ctx);
3442
3812
  }
3443
3813
  /** The images CSS3 has beyond `url()`, which a layer may name: a linear
3444
- * gradient is drawn, and the rest draw as nothing. */
3814
+ * and a radial gradient are drawn, and the rest draw as nothing. */
3445
3815
  const IMAGE_FUNCTION = /^(?:-(?:webkit|moz|o|ms)-)?(?:(?:repeating-)?(?:linear|radial|conic)-gradient|gradient|image-set|cross-fade|element|paint)\(/;
3446
3816
  function isPositionPart(word, ctx) {
3447
3817
  return (word in HORIZONTAL || word in VERTICAL || positionLength(word, ctx) !== null);
@@ -3474,95 +3844,6 @@ function whiteSpaceOf(collapse, wrap) {
3474
3844
  return wrap ? 'pre-wrap' : 'pre';
3475
3845
  return 'pre-line';
3476
3846
  }
3477
- /**
3478
- * The translation a `transform` makes: the sum of its translate functions'
3479
- * and its matrices' — `translate(-50%, -50%)`, Tailwind 3's `translate(
3480
- * var(--tw-translate-x), var(--tw-translate-y)) rotate(…) skewX(…) …` —
3481
- * with the functions that rotate, scale, skew or add perspective read and
3482
- * not drawn. Null for `none`, and undefined for what is no transform list,
3483
- * which drops the declaration.
3484
- */
3485
- function transformTranslation(value, ctx) {
3486
- if (value.trim().toLowerCase() === 'none')
3487
- return null;
3488
- let x = 0;
3489
- let y = 0;
3490
- const parts = splitValue(value);
3491
- if (!parts.length)
3492
- return undefined;
3493
- for (const part of parts) {
3494
- const m = /^([a-z0-9]+)\((.*)\)$/is.exec(part.trim());
3495
- if (!m)
3496
- return undefined;
3497
- const name = m[1].toLowerCase();
3498
- const args = splitCommas(m[2]).map((a) => a.trim());
3499
- let dx = 0;
3500
- let dy = 0;
3501
- if (name === 'translate' || name === 'translate3d') {
3502
- dx = parseLength(args[0] ?? '', ctx);
3503
- dy = args[1] === undefined ? 0 : parseLength(args[1], ctx);
3504
- }
3505
- else if (name === 'translatex')
3506
- dx = parseLength(args[0] ?? '', ctx);
3507
- else if (name === 'translatey')
3508
- dy = parseLength(args[0] ?? '', ctx);
3509
- else if (name === 'matrix' || name === 'matrix3d') {
3510
- // the translation is the last column's
3511
- const n = args.map(Number);
3512
- if (n.length !== (name === 'matrix' ? 6 : 16) || n.some(isNaN)) {
3513
- return undefined;
3514
- }
3515
- [dx, dy] = name === 'matrix' ? [n[4], n[5]] : [n[12], n[13]];
3516
- }
3517
- else if (!TRANSFORMS.has(name))
3518
- return undefined;
3519
- if (dx === null || dx === AUTO || dy === null || dy === AUTO) {
3520
- return undefined;
3521
- }
3522
- const sx = addLen(x, dx);
3523
- const sy = addLen(y, dy);
3524
- if (sx === null || sy === null)
3525
- return undefined;
3526
- x = sx;
3527
- y = sy;
3528
- }
3529
- return [x, y];
3530
- }
3531
- /** The transform functions read and not drawn. */
3532
- const TRANSFORMS = new Set([
3533
- 'translatez',
3534
- 'rotate',
3535
- 'rotatex',
3536
- 'rotatey',
3537
- 'rotatez',
3538
- 'rotate3d',
3539
- 'scale',
3540
- 'scalex',
3541
- 'scaley',
3542
- 'scalez',
3543
- 'scale3d',
3544
- 'skew',
3545
- 'skewx',
3546
- 'skewy',
3547
- 'perspective',
3548
- ]);
3549
- /** Two lengths added, a percentage and a length kept apart; null where
3550
- * one is a `min()` or a `max()`, which a sum cannot be kept of. */
3551
- function addLen(a, b) {
3552
- if (a === AUTO || b === AUTO)
3553
- return null;
3554
- if (typeof a === 'number' && typeof b === 'number')
3555
- return a + b;
3556
- if (a === 0)
3557
- return b;
3558
- if (b === 0)
3559
- return a;
3560
- const pa = typeof a === 'number' ? { pct: 0, px: a } : a;
3561
- const pb = typeof b === 'number' ? { pct: 0, px: b } : b;
3562
- if (pa.of || pb.of)
3563
- return null;
3564
- return { pct: pa.pct + pb.pct, px: (pa.px ?? 0) + (pb.px ?? 0) };
3565
- }
3566
3847
  /** An intrinsic size keyword, with the prefixes browsers still read, or
3567
3848
  * `fit-content()` of a length or a percentage (CSS Sizing 3, 3.1) — a
3568
3849
  * width's, where a height's is its content height as the keyword is;
@@ -4195,6 +4476,34 @@ function splitTopLevelSlash(value) {
4195
4476
  return out;
4196
4477
  }
4197
4478
  // --- logical properties ----------------------------------------------------
4479
+ /** `pointer-events`' values besides `none` (CSS UI 4, SVG 2 15.6). */
4480
+ const POINTER_EVENTS = new Set([
4481
+ 'auto',
4482
+ 'bounding-box',
4483
+ 'visiblepainted',
4484
+ 'visiblefill',
4485
+ 'visiblestroke',
4486
+ 'visible',
4487
+ 'painted',
4488
+ 'fill',
4489
+ 'stroke',
4490
+ 'all',
4491
+ ]);
4492
+ /**
4493
+ * A `fill` or a `stroke` (SVG 2, 13.2): `none`, a colour, or a `url()`
4494
+ * naming a gradient in the drawing, with what follows it, its fallback,
4495
+ * left off. Null for anything else, `context-fill` and `context-stroke`
4496
+ * among it, which only a marker or a `<use>` has a context for.
4497
+ */
4498
+ function svgPaint(value) {
4499
+ const v = value.trim();
4500
+ if (v.toLowerCase() === 'none')
4501
+ return 'none';
4502
+ const url = /^url\(\s*(['"]?)(#[^'")\s]+)\1\s*\)/i.exec(v);
4503
+ if (url)
4504
+ return `url(${url[2]})`;
4505
+ return parseColor(v);
4506
+ }
4198
4507
  const OBJECT_FITS = new Set(['fill', 'contain', 'cover', 'none', 'scale-down']);
4199
4508
  /**
4200
4509
  * `aspect-ratio` (CSS Sizing 4, 5.1): `auto`, a ratio — one number, or
@@ -4285,6 +4594,25 @@ function radiusOf(text, ctx) {
4285
4594
  return len < 0 ? null : len;
4286
4595
  return len.pct < 0 && !len.px && !len.of ? null : len;
4287
4596
  }
4597
+ /**
4598
+ * `border-radius`'s value: one to four radii, and after a `/` one to four
4599
+ * more for the vertical radii where they differ, elliptical corners — the
4600
+ * radii across, and the ones down, null where they are the same values.
4601
+ * Null for anything else.
4602
+ */
4603
+ function parseRadii(value, ctx) {
4604
+ const halves = value.split('/');
4605
+ if (halves.length > 2)
4606
+ return null;
4607
+ const [horizontal, vertical] = halves.map((half) => splitValue(half).map((p) => radiusOf(p, ctx)));
4608
+ for (const parts of vertical ? [horizontal, vertical] : [horizontal]) {
4609
+ if (!parts.length || parts.length > 4 || parts.includes(null))
4610
+ return null;
4611
+ }
4612
+ const across = fourSides(horizontal);
4613
+ const down = vertical ? fourSides(vertical) : null;
4614
+ return [across, down && !sameRadii(across, down) ? down : null];
4615
+ }
4288
4616
  /** Whether two corners' radii are the same values. */
4289
4617
  function sameRadii(a, b) {
4290
4618
  for (let i = 0; i < 4; i += 1) {
@@ -4485,10 +4813,15 @@ const INHERITED_NAMES = new Set([
4485
4813
  'list-style-position',
4486
4814
  'list-style-image',
4487
4815
  'cursor',
4816
+ 'pointer-events',
4817
+ 'fill',
4818
+ 'stroke',
4488
4819
  'border-collapse',
4489
4820
  'border-spacing',
4490
4821
  'empty-cells',
4491
4822
  'quotes',
4823
+ 'orphans',
4824
+ 'widows',
4492
4825
  ]);
4493
4826
  export function isInherited(name) {
4494
4827
  return INHERITED_NAMES.has(name);
@@ -4629,6 +4962,9 @@ const INHERIT_TARGETS = {
4629
4962
  'list-style-position': ['listStylePosition'],
4630
4963
  'list-style-image': ['listStyleImage'],
4631
4964
  cursor: ['cursor'],
4965
+ 'pointer-events': ['pointerEvents'],
4966
+ fill: ['fill'],
4967
+ stroke: ['stroke'],
4632
4968
  'border-collapse': ['borderCollapse'],
4633
4969
  'caption-side': ['captionSide'],
4634
4970
  'empty-cells': ['emptyCells'],
@@ -4695,9 +5031,16 @@ const INHERIT_TARGETS = {
4695
5031
  '-webkit-box-pack': ['webkitBoxPack'],
4696
5032
  '-webkit-box-align': ['webkitBoxAlign'],
4697
5033
  '-webkit-box-flex': ['webkitBoxFlex'],
4698
- columns: ['columns'],
4699
- 'column-count': ['columns'],
4700
- 'column-width': ['columns'],
5034
+ columns: ['columns', 'columnCount', 'columnWidth'],
5035
+ 'column-count': ['columns', 'columnCount'],
5036
+ 'column-width': ['columns', 'columnWidth'],
5037
+ orphans: ['orphans'],
5038
+ widows: ['widows'],
5039
+ 'break-inside': ['breakInside'],
5040
+ 'column-fill': ['columnFill'],
5041
+ 'column-span': ['columnSpan'],
5042
+ '-webkit-column-span': ['columnSpan'],
5043
+ 'page-break-inside': ['breakInside'],
4701
5044
  'text-overflow': ['textOverflow'],
4702
5045
  // a flex box's and its items', and a grid's
4703
5046
  'flex-direction': ['flexDirection'],
@@ -4712,12 +5055,12 @@ const INHERIT_TARGETS = {
4712
5055
  'flex-shrink': ['flexShrink'],
4713
5056
  'flex-basis': ['flexBasis'],
4714
5057
  order: ['order'],
4715
- gap: ['rowGap', 'columnGap'],
5058
+ gap: ['rowGap', 'columnGap', 'columnGapNormal'],
4716
5059
  'row-gap': ['rowGap'],
4717
- 'column-gap': ['columnGap'],
4718
- 'grid-gap': ['rowGap', 'columnGap'],
5060
+ 'column-gap': ['columnGap', 'columnGapNormal'],
5061
+ 'grid-gap': ['rowGap', 'columnGap', 'columnGapNormal'],
4719
5062
  'grid-row-gap': ['rowGap'],
4720
- 'grid-column-gap': ['columnGap'],
5063
+ 'grid-column-gap': ['columnGap', 'columnGapNormal'],
4721
5064
  'grid-template-columns': ['gridColumns'],
4722
5065
  'grid-template-rows': ['gridRows'],
4723
5066
  'grid-auto-rows': ['gridAutoRows'],
@@ -4812,11 +5155,18 @@ const INHERIT_TARGETS = {
4812
5155
  clear: ['clear'],
4813
5156
  overflow: ['overflowX', 'overflowY'],
4814
5157
  clip: ['clip'],
5158
+ 'clip-path': ['clipPath'],
5159
+ '-webkit-clip-path': ['clipPath'],
4815
5160
  'overflow-x': ['overflowX'],
4816
5161
  'overflow-y': ['overflowY'],
4817
5162
  opacity: ['opacity'],
4818
5163
  translate: ['translate'],
4819
- transform: ['transformTranslate'],
5164
+ rotate: ['rotate'],
5165
+ scale: ['scale'],
5166
+ transform: ['transform'],
5167
+ '-webkit-transform': ['transform'],
5168
+ 'transform-origin': ['transformOrigin'],
5169
+ '-webkit-transform-origin': ['transformOrigin'],
4820
5170
  'z-index': ['zIndex'],
4821
5171
  'vertical-align': ['verticalAlign'],
4822
5172
  'text-decoration': [
@@ -4871,13 +5221,30 @@ export function decorate(style) {
4871
5221
  if (lines.includes('underline')) {
4872
5222
  style.underline = inkColor(style.textDecorationColor ?? 'currentColor', style.color);
4873
5223
  style.underlineStyle = style.textDecorationStyle;
4874
- style.underlineThickness = style.textDecorationThickness;
5224
+ style.underlineThickness = usedThickness(style);
4875
5225
  style.underlineOffset = style.textUnderlineOffset;
4876
5226
  }
4877
5227
  if (lines.includes('line-through')) {
4878
5228
  style.lineThrough = inkColor(style.textDecorationColor ?? 'currentColor', style.color);
5229
+ style.lineThroughStyle = style.textDecorationStyle;
5230
+ style.lineThroughThickness = usedThickness(style);
4879
5231
  }
4880
5232
  }
5233
+ /**
5234
+ * How thick the lines a box decorates its text with are. `auto` leaves it
5235
+ * to the user agent, which is to take it from the font where the font says
5236
+ * (CSS Text Decoration 4, 2.4.1); neither text engine here reports a face's
5237
+ * underline, so it is a part of the font size, Blink's tenth — of the size
5238
+ * of the box that set the decoration, which draws one line of one thickness
5239
+ * through everything inside it (2.9). A pixel for every size drew the dots
5240
+ * under the `<abbr>` of a 140px title as a hairline. `from-font` is `auto`
5241
+ * for the same want of a metric, a length is itself on whole pixels, and
5242
+ * no line is thinner than one.
5243
+ */
5244
+ function usedThickness(style) {
5245
+ const length = style.textDecorationThickness;
5246
+ return Math.max(1, length === null ? style.fontSize / 10 : Math.round(length));
5247
+ }
4881
5248
  /**
4882
5249
  * The absolutely positioned styles that were inline-level before they were
4883
5250
  * made blocks: where such a box would have been in flow is on its line,
@@ -4984,4 +5351,54 @@ export function blockify(style, inFlexContainer) {
4984
5351
  return;
4985
5352
  }
4986
5353
  }
5354
+ /**
5355
+ * The `display` a `<button>` is laid out with, which is not always the one
5356
+ * it was given (HTML's rendering section, button layout, 15.5.3): a flex
5357
+ * box or a grid is that, and of the rest, "a value such that the outer
5358
+ * display type is 'inline'" behaves as `inline-block` and anything else as
5359
+ * `flow-root`. Blink computes them so — `inline-block` for `inline` and
5360
+ * `inline-table`, `block` for `table` and `list-item` — and makes the one
5361
+ * block flow of each; the formatting context is the layout's to give every
5362
+ * button that is a block (`establishesBFC`).
5363
+ *
5364
+ * Left an inline box, a button around a block was broken in two around it,
5365
+ * as any inline box is: as wide as the line, its `height` applied to
5366
+ * nothing, and its border and padding drawn above and below the line it
5367
+ * opened. A table's part is left what it is, where the two disagree: a
5368
+ * block to the text, an inline-block in Blink. After `blockify`, since a
5369
+ * float, an absolute box and a flex item are blocks first.
5370
+ */
5371
+ export function settleButton(style) {
5372
+ switch (style.display) {
5373
+ case 'inline':
5374
+ case 'inline-table':
5375
+ style.display = 'inline-block';
5376
+ return;
5377
+ case 'list-item':
5378
+ case 'table':
5379
+ style.display = 'block';
5380
+ return;
5381
+ default:
5382
+ return;
5383
+ }
5384
+ }
5385
+ /** The generic font families, which a list ending in one falls back
5386
+ * through already, in the text engine (CSS Fonts 4, 4.2). */
5387
+ const GENERIC_FAMILY = new Set([
5388
+ 'serif',
5389
+ 'sans-serif',
5390
+ 'monospace',
5391
+ 'cursive',
5392
+ 'fantasy',
5393
+ 'system-ui',
5394
+ 'ui-serif',
5395
+ 'ui-sans-serif',
5396
+ 'ui-monospace',
5397
+ 'ui-rounded',
5398
+ 'math',
5399
+ 'emoji',
5400
+ 'fangsong',
5401
+ '-apple-system',
5402
+ 'blinkmacsystemfont',
5403
+ ]);
4987
5404
  //# sourceMappingURL=style.js.map