@react-x11/components 0.13.0 → 0.14.1

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 (134) 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 +42 -1
  6. package/dist/html/css/cascade.d.ts.map +1 -1
  7. package/dist/html/css/cascade.js +336 -16
  8. package/dist/html/css/cascade.js.map +1 -1
  9. package/dist/html/css/color.d.ts +18 -0
  10. package/dist/html/css/color.d.ts.map +1 -1
  11. package/dist/html/css/color.js +27 -0
  12. package/dist/html/css/color.js.map +1 -1
  13. package/dist/html/css/parse.d.ts +18 -4
  14. package/dist/html/css/parse.d.ts.map +1 -1
  15. package/dist/html/css/parse.js +66 -28
  16. package/dist/html/css/parse.js.map +1 -1
  17. package/dist/html/css/shapes.d.ts +44 -0
  18. package/dist/html/css/shapes.d.ts.map +1 -0
  19. package/dist/html/css/shapes.js +194 -0
  20. package/dist/html/css/shapes.js.map +1 -0
  21. package/dist/html/css/style.d.ts +128 -15
  22. package/dist/html/css/style.d.ts.map +1 -1
  23. package/dist/html/css/style.js +547 -144
  24. package/dist/html/css/style.js.map +1 -1
  25. package/dist/html/css/transform.d.ts +85 -0
  26. package/dist/html/css/transform.d.ts.map +1 -0
  27. package/dist/html/css/transform.js +425 -0
  28. package/dist/html/css/transform.js.map +1 -0
  29. package/dist/html/css/ua.d.ts.map +1 -1
  30. package/dist/html/css/ua.js +14 -2
  31. package/dist/html/css/ua.js.map +1 -1
  32. package/dist/html/css/values.d.ts +4 -0
  33. package/dist/html/css/values.d.ts.map +1 -1
  34. package/dist/html/css/values.js.map +1 -1
  35. package/dist/html/fonts.d.ts +55 -5
  36. package/dist/html/fonts.d.ts.map +1 -1
  37. package/dist/html/fonts.js +284 -23
  38. package/dist/html/fonts.js.map +1 -1
  39. package/dist/html/layout/axes.d.ts +14 -0
  40. package/dist/html/layout/axes.d.ts.map +1 -0
  41. package/dist/html/layout/axes.js +28 -0
  42. package/dist/html/layout/axes.js.map +1 -0
  43. package/dist/html/layout/block.d.ts +104 -12
  44. package/dist/html/layout/block.d.ts.map +1 -1
  45. package/dist/html/layout/block.js +410 -39
  46. package/dist/html/layout/block.js.map +1 -1
  47. package/dist/html/layout/boxes.d.ts +72 -1
  48. package/dist/html/layout/boxes.d.ts.map +1 -1
  49. package/dist/html/layout/boxes.js +40 -2
  50. package/dist/html/layout/boxes.js.map +1 -1
  51. package/dist/html/layout/cache.d.ts +1 -1
  52. package/dist/html/layout/cache.d.ts.map +1 -1
  53. package/dist/html/layout/cache.js +6 -0
  54. package/dist/html/layout/cache.js.map +1 -1
  55. package/dist/html/layout/css-grid.d.ts.map +1 -1
  56. package/dist/html/layout/css-grid.js +144 -24
  57. package/dist/html/layout/css-grid.js.map +1 -1
  58. package/dist/html/layout/flex.d.ts.map +1 -1
  59. package/dist/html/layout/flex.js +585 -85
  60. package/dist/html/layout/flex.js.map +1 -1
  61. package/dist/html/layout/inline.d.ts +3 -0
  62. package/dist/html/layout/inline.d.ts.map +1 -1
  63. package/dist/html/layout/inline.js +144 -54
  64. package/dist/html/layout/inline.js.map +1 -1
  65. package/dist/html/layout/multicol.d.ts +42 -0
  66. package/dist/html/layout/multicol.d.ts.map +1 -0
  67. package/dist/html/layout/multicol.js +897 -0
  68. package/dist/html/layout/multicol.js.map +1 -0
  69. package/dist/html/layout/shaping.d.ts +7 -0
  70. package/dist/html/layout/shaping.d.ts.map +1 -1
  71. package/dist/html/layout/shaping.js +25 -20
  72. package/dist/html/layout/shaping.js.map +1 -1
  73. package/dist/html/layout/table.d.ts.map +1 -1
  74. package/dist/html/layout/table.js +97 -31
  75. package/dist/html/layout/table.js.map +1 -1
  76. package/dist/html/node.d.ts +4 -0
  77. package/dist/html/node.d.ts.map +1 -1
  78. package/dist/html/node.js +319 -47
  79. package/dist/html/node.js.map +1 -1
  80. package/dist/html/paint.d.ts +83 -5
  81. package/dist/html/paint.d.ts.map +1 -1
  82. package/dist/html/paint.js +1474 -217
  83. package/dist/html/paint.js.map +1 -1
  84. package/dist/html/svg.d.ts +39 -9
  85. package/dist/html/svg.d.ts.map +1 -1
  86. package/dist/html/svg.js +482 -37
  87. package/dist/html/svg.js.map +1 -1
  88. package/dist/html/widgets.js +87 -7
  89. package/dist/html/widgets.js.map +1 -1
  90. package/dist/html/woff2.d.ts +14 -0
  91. package/dist/html/woff2.d.ts.map +1 -0
  92. package/dist/html/woff2.js +608 -0
  93. package/dist/html/woff2.js.map +1 -0
  94. package/dist/maps/controller.d.ts +17 -1
  95. package/dist/maps/controller.d.ts.map +1 -1
  96. package/dist/maps/controller.js +27 -7
  97. package/dist/maps/controller.js.map +1 -1
  98. package/dist/richtext/node.d.ts +8 -1
  99. package/dist/richtext/node.d.ts.map +1 -1
  100. package/dist/richtext/node.js +5 -1
  101. package/dist/richtext/node.js.map +1 -1
  102. package/dist/richtext/runs.d.ts +25 -6
  103. package/dist/richtext/runs.d.ts.map +1 -1
  104. package/dist/richtext/runs.js +240 -24
  105. package/dist/richtext/runs.js.map +1 -1
  106. package/package.json +1 -1
  107. package/src/html/controls.ts +85 -2
  108. package/src/html/css/cascade.ts +366 -8
  109. package/src/html/css/color.ts +29 -0
  110. package/src/html/css/parse.ts +71 -31
  111. package/src/html/css/shapes.ts +239 -0
  112. package/src/html/css/style.ts +652 -147
  113. package/src/html/css/transform.ts +459 -0
  114. package/src/html/css/ua.ts +15 -2
  115. package/src/html/css/values.ts +4 -0
  116. package/src/html/fonts.ts +312 -29
  117. package/src/html/layout/axes.ts +62 -0
  118. package/src/html/layout/block.ts +466 -37
  119. package/src/html/layout/boxes.ts +95 -3
  120. package/src/html/layout/cache.ts +6 -0
  121. package/src/html/layout/css-grid.ts +169 -32
  122. package/src/html/layout/flex.ts +686 -79
  123. package/src/html/layout/inline.ts +149 -55
  124. package/src/html/layout/multicol.ts +977 -0
  125. package/src/html/layout/shaping.ts +27 -18
  126. package/src/html/layout/table.ts +105 -35
  127. package/src/html/node.ts +354 -48
  128. package/src/html/paint.ts +1804 -315
  129. package/src/html/svg.ts +573 -36
  130. package/src/html/widgets.ts +95 -9
  131. package/src/html/woff2.ts +612 -0
  132. package/src/maps/controller.ts +28 -7
  133. package/src/richtext/node.ts +13 -2
  134. package/src/richtext/runs.ts +313 -35
@@ -15,7 +15,9 @@
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';
20
+ import { svgPaint } from './shapes.js';
19
21
  /** Whether a length is one `padding` takes: not `auto`, not negative. A
20
22
  * `calc()` with a percentage in it has no sign until layout, which clamps
21
23
  * it at zero (CSS Values 4 10.12). */
@@ -58,6 +60,93 @@ function parseClip(value, ctx) {
58
60
  }
59
61
  return { top: edges[0], right: edges[1], bottom: edges[2], left: edges[3] };
60
62
  }
63
+ /** The boxes `clip-path` names; an SVG one is the CSS box that stands for
64
+ * it on an element with a CSS layout box (5.1). */
65
+ const CLIP_BOXES = {
66
+ 'margin-box': 'margin-box',
67
+ 'border-box': 'border-box',
68
+ 'padding-box': 'padding-box',
69
+ 'content-box': 'content-box',
70
+ 'fill-box': 'content-box',
71
+ 'stroke-box': 'border-box',
72
+ 'view-box': 'border-box',
73
+ };
74
+ /** The shapes that are no rectangle, and a reference to a `<clipPath>`
75
+ * element: a value, which sets the property, and none this draws. */
76
+ const UNDRAWN_CLIPS = /^(circle|ellipse|polygon|path|shape|url)\(/i;
77
+ const NO_LENGTHS = [0, 0, 0, 0];
78
+ /**
79
+ * `none`; a rectangle's function, a box, or the two in either order; or a
80
+ * shape that is no rectangle, which is a value and clips nothing here.
81
+ * Undefined for anything else, which leaves the value as it was.
82
+ */
83
+ function parseClipPath(value, ctx) {
84
+ const v = value.trim();
85
+ if (/^none$/i.test(v))
86
+ return null;
87
+ let box = null;
88
+ let shape;
89
+ for (const part of splitValue(v)) {
90
+ const named = CLIP_BOXES[part.toLowerCase()];
91
+ if (named) {
92
+ if (box)
93
+ return undefined;
94
+ box = named;
95
+ continue;
96
+ }
97
+ if (shape !== undefined)
98
+ return undefined;
99
+ if (UNDRAWN_CLIPS.test(part) && part.endsWith(')'))
100
+ shape = null;
101
+ else
102
+ shape = rectShape(part, ctx);
103
+ if (shape === undefined)
104
+ return undefined;
105
+ }
106
+ if (shape === null)
107
+ return null;
108
+ if (shape)
109
+ return { box: box ?? 'border-box', ...shape };
110
+ if (!box)
111
+ return undefined;
112
+ return { box, shape: null, lengths: NO_LENGTHS, radii: null, radiiY: null };
113
+ }
114
+ /** `inset()`, `rect()` or `xywh()`, with the corners it rounds; undefined
115
+ * for anything else. */
116
+ function rectShape(text, ctx) {
117
+ const m = /^(inset|rect|xywh)\((.*)\)$/is.exec(text);
118
+ if (!m)
119
+ return undefined;
120
+ const shape = m[1].toLowerCase();
121
+ const parts = splitValue(m[2].trim());
122
+ const round = parts.findIndex((p) => p.toLowerCase() === 'round');
123
+ const sides = round < 0 ? parts : parts.slice(0, round);
124
+ let radii = null;
125
+ let radiiY = null;
126
+ if (round >= 0) {
127
+ const corners = parseRadii(parts.slice(round + 1).join(' '), ctx);
128
+ if (!corners)
129
+ return undefined;
130
+ [radii, radiiY] = corners;
131
+ }
132
+ // one to four insets, as a margin's sides are written; the other two
133
+ // take all four
134
+ if (shape === 'inset' ? !sides.length || sides.length > 4 : sides.length !== 4)
135
+ return undefined;
136
+ const lengths = [];
137
+ for (let i = 0; i < sides.length; i += 1) {
138
+ const len = parseLength(sides[i], ctx);
139
+ if (len === null)
140
+ return undefined;
141
+ // only `rect()` has an edge that is `auto`, and a width is not negative
142
+ if (len === AUTO && shape !== 'rect')
143
+ return undefined;
144
+ if (shape === 'xywh' && i > 1 && !notNegative(len))
145
+ return undefined;
146
+ lengths.push(len);
147
+ }
148
+ return { shape, lengths: fourSides(lengths), radii, radiiY };
149
+ }
61
150
  /** The properties that inherit. Named once, so `inherit()` and the `inherit`
62
151
  * keyword cannot disagree about the list. */
63
152
  /** The fields a style takes from its parent's (CSS 2.1's "Inherited:
@@ -92,6 +181,8 @@ export const INHERITED = [
92
181
  'fontFeatureSettings',
93
182
  'tabSize',
94
183
  'tabSizeIsLength',
184
+ 'orphans',
185
+ 'widows',
95
186
  'whiteSpace',
96
187
  'overflowWrap',
97
188
  'wordBreak',
@@ -103,6 +194,9 @@ export const INHERITED = [
103
194
  'listStylePosition',
104
195
  'listStyleImage',
105
196
  'cursor',
197
+ 'pointerEvents',
198
+ 'fill',
199
+ 'stroke',
106
200
  'borderCollapse',
107
201
  'borderSpacing',
108
202
  'borderSpacingY',
@@ -154,6 +248,9 @@ export function initialStyle(look, scale = 1) {
154
248
  listStylePosition: 'outside',
155
249
  listStyleImage: null,
156
250
  cursor: null,
251
+ pointerEvents: 'auto',
252
+ fill: null,
253
+ stroke: null,
157
254
  borderCollapse: 'separate',
158
255
  // CSS's initial value; a `<table>` gets its 2px from the UA sheet, and
159
256
  // an anonymous table, which no sheet names, has none
@@ -188,6 +285,14 @@ export function initialStyle(look, scale = 1) {
188
285
  webkitBoxAlign: null,
189
286
  webkitBoxFlex: 0,
190
287
  columns: 0,
288
+ columnCount: null,
289
+ columnWidth: null,
290
+ columnGapNormal: true,
291
+ orphans: 2,
292
+ widows: 2,
293
+ breakInside: 'auto',
294
+ columnFill: 'balance',
295
+ columnSpan: false,
191
296
  textOverflow: 'clip',
192
297
  aspectRatio: null,
193
298
  objectFit: 'fill',
@@ -197,9 +302,13 @@ export function initialStyle(look, scale = 1) {
197
302
  overflowX: 'visible',
198
303
  overflowY: 'visible',
199
304
  clip: null,
305
+ clipPath: null,
200
306
  opacity: 1,
201
307
  translate: null,
202
- transformTranslate: null,
308
+ rotate: null,
309
+ scale: null,
310
+ transform: null,
311
+ transformOrigin: [{ pct: 50 }, { pct: 50 }],
203
312
  zIndex: AUTO,
204
313
  verticalAlign: 'baseline',
205
314
  width: AUTO,
@@ -283,6 +392,8 @@ export function initialStyle(look, scale = 1) {
283
392
  underlineThickness: null,
284
393
  underlineOffset: null,
285
394
  lineThrough: null,
395
+ lineThroughStyle: 'solid',
396
+ lineThroughThickness: null,
286
397
  flexDirection: 'row',
287
398
  flexWrap: 'nowrap',
288
399
  justifyContent: 'normal',
@@ -381,6 +492,8 @@ export function inherit(parent, initial) {
381
492
  out.fontFeatureSettings = parent.fontFeatureSettings;
382
493
  out.tabSize = parent.tabSize;
383
494
  out.tabSizeIsLength = parent.tabSizeIsLength;
495
+ out.orphans = parent.orphans;
496
+ out.widows = parent.widows;
384
497
  out.whiteSpace = parent.whiteSpace;
385
498
  out.overflowWrap = parent.overflowWrap;
386
499
  out.wordBreak = parent.wordBreak;
@@ -392,6 +505,9 @@ export function inherit(parent, initial) {
392
505
  out.listStylePosition = parent.listStylePosition;
393
506
  out.listStyleImage = parent.listStyleImage;
394
507
  out.cursor = parent.cursor;
508
+ out.pointerEvents = parent.pointerEvents;
509
+ out.fill = parent.fill;
510
+ out.stroke = parent.stroke;
395
511
  out.borderCollapse = parent.borderCollapse;
396
512
  out.borderSpacing = parent.borderSpacing;
397
513
  out.borderSpacingY = parent.borderSpacingY;
@@ -411,6 +527,8 @@ export function inherit(parent, initial) {
411
527
  out.underlineThickness = parent.underlineThickness;
412
528
  out.underlineOffset = parent.underlineOffset;
413
529
  out.lineThrough = parent.lineThrough;
530
+ out.lineThroughStyle = parent.lineThroughStyle;
531
+ out.lineThroughThickness = parent.lineThroughThickness;
414
532
  return out;
415
533
  }
416
534
  /**
@@ -444,6 +562,8 @@ export const FIRST_LINE_INHERITED = [
444
562
  'underlineThickness',
445
563
  'underlineOffset',
446
564
  'lineThrough',
565
+ 'lineThroughStyle',
566
+ 'lineThroughThickness',
447
567
  ];
448
568
  /**
449
569
  * What a box on its block's first line inherits from: its parent's style,
@@ -501,6 +621,14 @@ const BORDER_WIDTH_KEYWORDS = {
501
621
  medium: 3,
502
622
  thick: 5,
503
623
  };
624
+ /** `-webkit-` names that are the multicol properties' own. */
625
+ const MULTICOL_ALIASES = {
626
+ '-webkit-columns': 'columns',
627
+ '-webkit-column-count': 'column-count',
628
+ '-webkit-column-width': 'column-width',
629
+ '-webkit-column-gap': 'column-gap',
630
+ '-webkit-column-break-inside': 'break-inside',
631
+ };
504
632
  /**
505
633
  * Apply one declaration to a style, in place.
506
634
  *
@@ -510,7 +638,12 @@ const BORDER_WIDTH_KEYWORDS = {
510
638
  * correct response to every one of them is to render the rest.
511
639
  */
512
640
  export function applyDeclaration(style, parent, prop, rawValue, ctx) {
513
- const name = prop.toLowerCase();
641
+ const written = prop.toLowerCase();
642
+ // the multicol properties under the names WebKit had them by, which
643
+ // Blink still reads, each an alias of the property (`-webkit-column-
644
+ // count` in its `css_properties.json5`): a page written for it alone
645
+ // names no other
646
+ const name = MULTICOL_ALIASES[written] ?? written;
514
647
  let value = rawValue.trim();
515
648
  if (!value)
516
649
  return;
@@ -645,21 +778,75 @@ export function applyDeclaration(style, parent, prop, rawValue, ctx) {
645
778
  case 'column-count':
646
779
  case 'column-width':
647
780
  case 'columns': {
648
- // `auto`, or a count, a width or both of them
781
+ // `auto`, or a count, a width or both of them, in either order; what
782
+ // the shorthand leaves out is `auto`
649
783
  const bit = name === 'column-count' ? 1 : name === 'column-width' ? 2 : 3;
650
- let set = 0;
784
+ let count = null;
785
+ let width = null;
651
786
  for (const part of splitValue(value)) {
652
787
  const v = part.toLowerCase();
653
788
  if (v === 'auto')
654
789
  continue;
655
- if (/^\d+$/.test(v) && Number(v) >= 1)
656
- set |= 1;
657
- else if (parseLength(part, ctx) !== null)
658
- set |= 2;
659
- else
790
+ if (/^\d+$/.test(v) && Number(v) >= 1) {
791
+ if (count !== null || !(bit & 1))
792
+ return;
793
+ count = Number(v);
794
+ continue;
795
+ }
796
+ const len = parseLength(part, ctx);
797
+ if (typeof len !== 'number' || !(len > 0))
798
+ return;
799
+ if (width !== null || !(bit & 2))
800
+ return;
801
+ width = len;
802
+ }
803
+ if (bit & 1)
804
+ style.columnCount = count;
805
+ if (bit & 2)
806
+ style.columnWidth = width;
807
+ style.columns =
808
+ (style.columnCount !== null ? 1 : 0) |
809
+ (style.columnWidth !== null ? 2 : 0);
810
+ return;
811
+ }
812
+ case 'orphans':
813
+ case 'widows': {
814
+ // a whole number of lines, one or more
815
+ const v = value.trim();
816
+ if (!/^\+?\d+$/.test(v) || Number(v) < 1)
817
+ return;
818
+ style[name] = Number(v);
819
+ return;
820
+ }
821
+ case '-webkit-column-span':
822
+ case 'column-span': {
823
+ const v = value.trim().toLowerCase();
824
+ if (v === 'all')
825
+ style.columnSpan = true;
826
+ else if (v === 'none')
827
+ style.columnSpan = false;
828
+ return;
829
+ }
830
+ case 'column-fill': {
831
+ const v = value.trim().toLowerCase();
832
+ if (v === 'auto')
833
+ style.columnFill = 'auto';
834
+ else if (v === 'balance' || v === 'balance-all') {
835
+ style.columnFill = 'balance';
836
+ }
837
+ return;
838
+ }
839
+ case 'break-inside':
840
+ case 'page-break-inside': {
841
+ const v = value.trim().toLowerCase();
842
+ if (v === 'auto')
843
+ style.breakInside = 'auto';
844
+ else if (/^avoid(?:-page|-column|-region)?$/.test(v)) {
845
+ // the legacy property knows `avoid` alone
846
+ if (name === 'page-break-inside' && v !== 'avoid')
660
847
  return;
848
+ style.breakInside = 'avoid';
661
849
  }
662
- style.columns = (style.columns & ~bit) | (set & bit);
663
850
  return;
664
851
  }
665
852
  case '-webkit-box-flex': {
@@ -927,6 +1114,13 @@ export function applyDeclaration(style, parent, prop, rawValue, ctx) {
927
1114
  style.clip = clip;
928
1115
  return;
929
1116
  }
1117
+ case 'clip-path':
1118
+ case '-webkit-clip-path': {
1119
+ const path = parseClipPath(value, ctx);
1120
+ if (path !== undefined)
1121
+ style.clipPath = path;
1122
+ return;
1123
+ }
930
1124
  case 'opacity': {
931
1125
  const a = parseAlpha(value);
932
1126
  if (a !== null)
@@ -950,10 +1144,38 @@ export function applyDeclaration(style, parent, prop, rawValue, ctx) {
950
1144
  style.translate = [x, y ?? 0];
951
1145
  return;
952
1146
  }
953
- case 'transform': {
954
- const moved = transformTranslation(value, ctx);
955
- if (moved !== undefined)
956
- style.transformTranslate = moved;
1147
+ case 'rotate': {
1148
+ const turn = parseRotate(value);
1149
+ if (turn !== undefined)
1150
+ style.rotate = turn;
1151
+ return;
1152
+ }
1153
+ case 'scale': {
1154
+ const factors = parseScale(value);
1155
+ if (factors !== undefined)
1156
+ style.scale = factors;
1157
+ return;
1158
+ }
1159
+ case 'transform':
1160
+ case '-webkit-transform': {
1161
+ const list = parseTransform(value, ctx);
1162
+ if (list !== undefined)
1163
+ style.transform = list;
1164
+ return;
1165
+ }
1166
+ case 'transform-origin':
1167
+ case '-webkit-transform-origin': {
1168
+ // across and down, as a position is, and a depth nothing here has
1169
+ const parts = splitValue(value);
1170
+ if (parts.length === 3) {
1171
+ if (typeof parseLength(parts.pop(), ctx) !== 'number')
1172
+ return;
1173
+ }
1174
+ else if (parts.length > 2)
1175
+ return;
1176
+ const origin = positionPair(parts, ctx);
1177
+ if (origin)
1178
+ style.transformOrigin = origin;
957
1179
  return;
958
1180
  }
959
1181
  case 'visibility': {
@@ -1242,19 +1464,9 @@ export function applyDeclaration(style, parent, prop, rawValue, ctx) {
1242
1464
  return;
1243
1465
  }
1244
1466
  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;
1467
+ const radii = parseRadii(value, ctx);
1468
+ if (radii)
1469
+ [style.borderRadius, style.borderRadiusY] = radii;
1258
1470
  return;
1259
1471
  }
1260
1472
  // --- background ---------------------------------------------------------
@@ -1509,7 +1721,19 @@ export function applyDeclaration(style, parent, prop, rawValue, ctx) {
1509
1721
  : f.trim().replace(/[ \t\n\r\f]+/g, ' '))
1510
1722
  .filter(Boolean)
1511
1723
  .join(', ');
1512
- style.fontFamily = ctx.families ? ctx.families(list) : list;
1724
+ // A list that ends in no generic family ends in the document's own
1725
+ // font: where no face in the list matches, the text is set in the
1726
+ // user agent's default (CSS Fonts 4, 5.1), which for a browser is its
1727
+ // standard font and here is the one the document is set in. The text
1728
+ // engine's own answer for a name nothing has is its platform's pick,
1729
+ // Verdana under fontconfig: the Zen Garden's 216 sets its summary in
1730
+ // Montserrat alone, which no machine here has, and Chrome sets it in
1731
+ // Times where ours was Verdana, a fifth wider
1732
+ const last = names[names.length - 1].trim();
1733
+ const generic = !/^['"]/.test(last) && GENERIC_FAMILY.has(last.toLowerCase());
1734
+ const fallback = generic ? null : (ctx.fallbackFamily?.() ?? null);
1735
+ const full = fallback ? `${list}, ${fallback}` : list;
1736
+ style.fontFamily = ctx.families ? ctx.families(full) : full;
1513
1737
  return;
1514
1738
  }
1515
1739
  case 'font-size': {
@@ -1933,6 +2157,23 @@ export function applyDeclaration(style, parent, prop, rawValue, ctx) {
1933
2157
  style.cursor = splitCommas(value)[0]?.trim().toLowerCase() || null;
1934
2158
  return;
1935
2159
  }
2160
+ case 'pointer-events': {
2161
+ // `none`, or one of the values SVG gives it, which are `auto` on
2162
+ // anything that is not SVG's to draw
2163
+ const v = value.trim().toLowerCase();
2164
+ if (v === 'none')
2165
+ style.pointerEvents = 'none';
2166
+ else if (POINTER_EVENTS.has(v))
2167
+ style.pointerEvents = 'auto';
2168
+ return;
2169
+ }
2170
+ case 'fill':
2171
+ case 'stroke': {
2172
+ const paint = svgPaint(value);
2173
+ if (paint !== null)
2174
+ style[name] = paint;
2175
+ return;
2176
+ }
1936
2177
  // --- lists --------------------------------------------------------------
1937
2178
  case 'list-style': {
1938
2179
  // a type, a position and an image, each at most once and in any
@@ -2132,8 +2373,11 @@ export function applyDeclaration(style, parent, prop, rawValue, ctx) {
2132
2373
  return;
2133
2374
  if (which !== 'column-gap')
2134
2375
  style.rowGap = row;
2135
- if (which !== 'row-gap')
2376
+ if (which !== 'row-gap') {
2136
2377
  style.columnGap = col;
2378
+ const word = parts[parts.length - 1].toLowerCase();
2379
+ style.columnGapNormal = word === 'normal';
2380
+ }
2137
2381
  return;
2138
2382
  }
2139
2383
  case 'grid-template-columns':
@@ -2605,6 +2849,12 @@ function alignKeyword(value) {
2605
2849
  return 'flex-end';
2606
2850
  case 'first baseline':
2607
2851
  return 'baseline';
2852
+ // By last baselines, which nothing here aligns: set where that falls
2853
+ // back to, the end (CSS Box Alignment 3, 4.2). Dropped, a row that asks
2854
+ // for it stretched its items, and a flex box among them was as tall as
2855
+ // the row, its lines spread down it.
2856
+ case 'last baseline':
2857
+ return 'flex-end';
2608
2858
  case 'center':
2609
2859
  return 'center';
2610
2860
  case 'stretch':
@@ -3226,7 +3476,7 @@ function applyBackgroundShorthand(style, value, ctx) {
3226
3476
  function backgroundImageOf(text, ctx) {
3227
3477
  const v = text.trim();
3228
3478
  if (IMAGE_FUNCTION.test(v.toLowerCase()))
3229
- return parseLinearGradient(v, ctx);
3479
+ return parseGradient(v, ctx);
3230
3480
  return parseUrl(v);
3231
3481
  }
3232
3482
  /** A background property's values, one a comma-separated layer, each read
@@ -3248,7 +3498,7 @@ function layerValues(value, read) {
3248
3498
  * is dropped rather than resetting the background it meant to replace
3249
3499
  * (CSS 2.1 4.2). What CSS3 adds is read too, so that a declaration a
3250
3500
  * browser keeps is kept: `/ cover` after the position, `space` and `round`,
3251
- * a gradient — drawn as nothing, over the layer's colour.
3501
+ * and a gradient, over the layer's colour.
3252
3502
  */
3253
3503
  function readBackgroundLayer(text, ctx, last) {
3254
3504
  const parts = splitValue(text).flatMap(splitSlash);
@@ -3284,7 +3534,7 @@ function readBackgroundLayer(text, ctx, last) {
3284
3534
  layer.image = url;
3285
3535
  }
3286
3536
  else if (v !== 'none')
3287
- layer.gradient = parseLinearGradient(part, ctx);
3537
+ layer.gradient = parseGradient(part, ctx);
3288
3538
  i += 1;
3289
3539
  }
3290
3540
  else if (v === 'repeat-x' || v === 'repeat-y' || REPEATS.has(v)) {
@@ -3363,21 +3613,21 @@ function readBackgroundLayer(text, ctx, last) {
3363
3613
  }
3364
3614
  /**
3365
3615
  * 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.
3616
+ * that is no gradient: a direction by angle, by side or by corner, and its
3617
+ * colour stops.
3370
3618
  */
3371
3619
  function parseLinearGradient(text, ctx) {
3372
3620
  const m = /^linear-gradient\((.*)\)$/is.exec(text.trim());
3373
3621
  if (!m)
3374
3622
  return null;
3375
3623
  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();
3624
+ const out = {
3625
+ kind: 'linear',
3626
+ angle: Math.PI,
3627
+ corner: null,
3628
+ stops: [],
3629
+ };
3630
+ const first = withoutInterpolation(args[0] ?? '');
3381
3631
  let from = 0;
3382
3632
  if (!first || first.startsWith('to ')) {
3383
3633
  from = 1;
@@ -3419,7 +3669,124 @@ function parseLinearGradient(text, ctx) {
3419
3669
  from = 1;
3420
3670
  }
3421
3671
  }
3422
- for (const arg of args.slice(from)) {
3672
+ const stops = parseStops(args.slice(from), ctx);
3673
+ if (!stops)
3674
+ return null;
3675
+ out.stops = stops;
3676
+ return out;
3677
+ }
3678
+ /**
3679
+ * A `radial-gradient()` (CSS Images 3, 3.2.1), or null for anything else:
3680
+ * an ending shape, `circle` or `ellipse`, and its size, in either order —
3681
+ * a keyword for the side or corner of the box it reaches, one length for a
3682
+ * circle's radius, or two lengths or percentages for an ellipse's — then
3683
+ * `at` and its centre, a position as `background-position` writes one;
3684
+ * each of the three may be left out, for an ellipse through the farthest
3685
+ * corner from the middle of the box. A colour interpolation method is read
3686
+ * and not honoured, and the stops are a linear gradient's, spaced along
3687
+ * the ray from the centre to the shape's edge.
3688
+ */
3689
+ function parseRadialGradient(text, ctx) {
3690
+ const m = /^radial-gradient\((.*)\)$/is.exec(text.trim());
3691
+ if (!m)
3692
+ return null;
3693
+ const args = splitCommas(m[1]).map((a) => a.trim());
3694
+ const out = {
3695
+ kind: 'radial',
3696
+ circle: false,
3697
+ extent: null,
3698
+ radii: null,
3699
+ at: [{ pct: 50 }, { pct: 50 }],
3700
+ stops: [],
3701
+ };
3702
+ const first = withoutInterpolation(args[0] ?? '');
3703
+ let from = 0;
3704
+ if (!first)
3705
+ from = 1;
3706
+ else if (parseColor(splitValue(first)[0]) === null) {
3707
+ from = 1;
3708
+ const words = splitValue(first);
3709
+ const at = words.indexOf('at');
3710
+ if (at >= 0) {
3711
+ const position = positionPair(words.slice(at + 1), ctx);
3712
+ if (!position)
3713
+ return null;
3714
+ out.at = position;
3715
+ }
3716
+ let shape = null;
3717
+ const sizes = [];
3718
+ // the size is one run of words, the shape's keyword before or after it
3719
+ let sized = false;
3720
+ for (const word of at >= 0 ? words.slice(0, at) : words) {
3721
+ if (word === 'circle' || word === 'ellipse') {
3722
+ if (shape)
3723
+ return null;
3724
+ shape = word;
3725
+ if (sizes.length || out.extent)
3726
+ sized = true;
3727
+ continue;
3728
+ }
3729
+ if (sized || out.extent)
3730
+ return null;
3731
+ if (RADIAL_EXTENTS.has(word)) {
3732
+ if (sizes.length)
3733
+ return null;
3734
+ out.extent = word;
3735
+ continue;
3736
+ }
3737
+ const len = parseLength(word, ctx);
3738
+ if (len === null || len === AUTO || !notNegative(len))
3739
+ return null;
3740
+ sizes.push(len);
3741
+ }
3742
+ if (sizes.length === 1) {
3743
+ // a circle's radius is a length: a percentage of what, the spec asks
3744
+ const [r] = sizes;
3745
+ if (shape === 'ellipse' || typeof r !== 'number') {
3746
+ return null;
3747
+ }
3748
+ out.circle = true;
3749
+ out.radii = [r, r];
3750
+ }
3751
+ else if (sizes.length === 2) {
3752
+ if (shape === 'circle')
3753
+ return null;
3754
+ out.radii = [sizes[0], sizes[1]];
3755
+ }
3756
+ else if (sizes.length)
3757
+ return null;
3758
+ else {
3759
+ out.circle = shape === 'circle';
3760
+ out.extent ??= 'farthest-corner';
3761
+ }
3762
+ }
3763
+ const stops = parseStops(args.slice(from), ctx);
3764
+ if (!stops)
3765
+ return null;
3766
+ out.stops = stops;
3767
+ return out;
3768
+ }
3769
+ const RADIAL_EXTENTS = new Set([
3770
+ 'closest-side',
3771
+ 'closest-corner',
3772
+ 'farthest-side',
3773
+ 'farthest-corner',
3774
+ ]);
3775
+ /** A gradient's first argument, lower-cased, without the colour
3776
+ * interpolation method CSS Images 4 adds to it (`in oklab`, which
3777
+ * Tailwind 4 writes): read, and not honoured. */
3778
+ function withoutInterpolation(arg) {
3779
+ return arg
3780
+ .toLowerCase()
3781
+ .replace(/\bin\s+[a-z-]+(?:\s+(?:shorter|longer|increasing|decreasing)\s+hue)?/, '')
3782
+ .trim();
3783
+ }
3784
+ /** A gradient's colour stops, at least two, each with no position, one or
3785
+ * two; a bare length between two stops, a hint, is passed over. Null
3786
+ * where an argument is none of those. */
3787
+ function parseStops(args, ctx) {
3788
+ const stops = [];
3789
+ for (const arg of args) {
3423
3790
  const parts = splitValue(arg);
3424
3791
  const color = parseColor(parts[0] ?? '');
3425
3792
  if (color === null) {
@@ -3434,14 +3801,18 @@ function parseLinearGradient(text, ctx) {
3434
3801
  if (at.some((p) => p === null || p === AUTO))
3435
3802
  return null;
3436
3803
  if (!at.length)
3437
- out.stops.push({ color, at: null });
3804
+ stops.push({ color, at: null });
3438
3805
  for (const p of at)
3439
- out.stops.push({ color, at: p });
3806
+ stops.push({ color, at: p });
3440
3807
  }
3441
- return out.stops.length >= 2 ? out : null;
3808
+ return stops.length >= 2 ? stops : null;
3809
+ }
3810
+ /** A gradient function's image, where it is one this draws. */
3811
+ function parseGradient(text, ctx) {
3812
+ return parseLinearGradient(text, ctx) ?? parseRadialGradient(text, ctx);
3442
3813
  }
3443
3814
  /** The images CSS3 has beyond `url()`, which a layer may name: a linear
3444
- * gradient is drawn, and the rest draw as nothing. */
3815
+ * and a radial gradient are drawn, and the rest draw as nothing. */
3445
3816
  const IMAGE_FUNCTION = /^(?:-(?:webkit|moz|o|ms)-)?(?:(?:repeating-)?(?:linear|radial|conic)-gradient|gradient|image-set|cross-fade|element|paint)\(/;
3446
3817
  function isPositionPart(word, ctx) {
3447
3818
  return (word in HORIZONTAL || word in VERTICAL || positionLength(word, ctx) !== null);
@@ -3474,95 +3845,6 @@ function whiteSpaceOf(collapse, wrap) {
3474
3845
  return wrap ? 'pre-wrap' : 'pre';
3475
3846
  return 'pre-line';
3476
3847
  }
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
3848
  /** An intrinsic size keyword, with the prefixes browsers still read, or
3567
3849
  * `fit-content()` of a length or a percentage (CSS Sizing 3, 3.1) — a
3568
3850
  * width's, where a height's is its content height as the keyword is;
@@ -4195,6 +4477,19 @@ function splitTopLevelSlash(value) {
4195
4477
  return out;
4196
4478
  }
4197
4479
  // --- logical properties ----------------------------------------------------
4480
+ /** `pointer-events`' values besides `none` (CSS UI 4, SVG 2 15.6). */
4481
+ const POINTER_EVENTS = new Set([
4482
+ 'auto',
4483
+ 'bounding-box',
4484
+ 'visiblepainted',
4485
+ 'visiblefill',
4486
+ 'visiblestroke',
4487
+ 'visible',
4488
+ 'painted',
4489
+ 'fill',
4490
+ 'stroke',
4491
+ 'all',
4492
+ ]);
4198
4493
  const OBJECT_FITS = new Set(['fill', 'contain', 'cover', 'none', 'scale-down']);
4199
4494
  /**
4200
4495
  * `aspect-ratio` (CSS Sizing 4, 5.1): `auto`, a ratio — one number, or
@@ -4285,6 +4580,25 @@ function radiusOf(text, ctx) {
4285
4580
  return len < 0 ? null : len;
4286
4581
  return len.pct < 0 && !len.px && !len.of ? null : len;
4287
4582
  }
4583
+ /**
4584
+ * `border-radius`'s value: one to four radii, and after a `/` one to four
4585
+ * more for the vertical radii where they differ, elliptical corners — the
4586
+ * radii across, and the ones down, null where they are the same values.
4587
+ * Null for anything else.
4588
+ */
4589
+ function parseRadii(value, ctx) {
4590
+ const halves = value.split('/');
4591
+ if (halves.length > 2)
4592
+ return null;
4593
+ const [horizontal, vertical] = halves.map((half) => splitValue(half).map((p) => radiusOf(p, ctx)));
4594
+ for (const parts of vertical ? [horizontal, vertical] : [horizontal]) {
4595
+ if (!parts.length || parts.length > 4 || parts.includes(null))
4596
+ return null;
4597
+ }
4598
+ const across = fourSides(horizontal);
4599
+ const down = vertical ? fourSides(vertical) : null;
4600
+ return [across, down && !sameRadii(across, down) ? down : null];
4601
+ }
4288
4602
  /** Whether two corners' radii are the same values. */
4289
4603
  function sameRadii(a, b) {
4290
4604
  for (let i = 0; i < 4; i += 1) {
@@ -4485,10 +4799,15 @@ const INHERITED_NAMES = new Set([
4485
4799
  'list-style-position',
4486
4800
  'list-style-image',
4487
4801
  'cursor',
4802
+ 'pointer-events',
4803
+ 'fill',
4804
+ 'stroke',
4488
4805
  'border-collapse',
4489
4806
  'border-spacing',
4490
4807
  'empty-cells',
4491
4808
  'quotes',
4809
+ 'orphans',
4810
+ 'widows',
4492
4811
  ]);
4493
4812
  export function isInherited(name) {
4494
4813
  return INHERITED_NAMES.has(name);
@@ -4629,6 +4948,9 @@ const INHERIT_TARGETS = {
4629
4948
  'list-style-position': ['listStylePosition'],
4630
4949
  'list-style-image': ['listStyleImage'],
4631
4950
  cursor: ['cursor'],
4951
+ 'pointer-events': ['pointerEvents'],
4952
+ fill: ['fill'],
4953
+ stroke: ['stroke'],
4632
4954
  'border-collapse': ['borderCollapse'],
4633
4955
  'caption-side': ['captionSide'],
4634
4956
  'empty-cells': ['emptyCells'],
@@ -4695,9 +5017,16 @@ const INHERIT_TARGETS = {
4695
5017
  '-webkit-box-pack': ['webkitBoxPack'],
4696
5018
  '-webkit-box-align': ['webkitBoxAlign'],
4697
5019
  '-webkit-box-flex': ['webkitBoxFlex'],
4698
- columns: ['columns'],
4699
- 'column-count': ['columns'],
4700
- 'column-width': ['columns'],
5020
+ columns: ['columns', 'columnCount', 'columnWidth'],
5021
+ 'column-count': ['columns', 'columnCount'],
5022
+ 'column-width': ['columns', 'columnWidth'],
5023
+ orphans: ['orphans'],
5024
+ widows: ['widows'],
5025
+ 'break-inside': ['breakInside'],
5026
+ 'column-fill': ['columnFill'],
5027
+ 'column-span': ['columnSpan'],
5028
+ '-webkit-column-span': ['columnSpan'],
5029
+ 'page-break-inside': ['breakInside'],
4701
5030
  'text-overflow': ['textOverflow'],
4702
5031
  // a flex box's and its items', and a grid's
4703
5032
  'flex-direction': ['flexDirection'],
@@ -4712,12 +5041,12 @@ const INHERIT_TARGETS = {
4712
5041
  'flex-shrink': ['flexShrink'],
4713
5042
  'flex-basis': ['flexBasis'],
4714
5043
  order: ['order'],
4715
- gap: ['rowGap', 'columnGap'],
5044
+ gap: ['rowGap', 'columnGap', 'columnGapNormal'],
4716
5045
  'row-gap': ['rowGap'],
4717
- 'column-gap': ['columnGap'],
4718
- 'grid-gap': ['rowGap', 'columnGap'],
5046
+ 'column-gap': ['columnGap', 'columnGapNormal'],
5047
+ 'grid-gap': ['rowGap', 'columnGap', 'columnGapNormal'],
4719
5048
  'grid-row-gap': ['rowGap'],
4720
- 'grid-column-gap': ['columnGap'],
5049
+ 'grid-column-gap': ['columnGap', 'columnGapNormal'],
4721
5050
  'grid-template-columns': ['gridColumns'],
4722
5051
  'grid-template-rows': ['gridRows'],
4723
5052
  'grid-auto-rows': ['gridAutoRows'],
@@ -4812,11 +5141,18 @@ const INHERIT_TARGETS = {
4812
5141
  clear: ['clear'],
4813
5142
  overflow: ['overflowX', 'overflowY'],
4814
5143
  clip: ['clip'],
5144
+ 'clip-path': ['clipPath'],
5145
+ '-webkit-clip-path': ['clipPath'],
4815
5146
  'overflow-x': ['overflowX'],
4816
5147
  'overflow-y': ['overflowY'],
4817
5148
  opacity: ['opacity'],
4818
5149
  translate: ['translate'],
4819
- transform: ['transformTranslate'],
5150
+ rotate: ['rotate'],
5151
+ scale: ['scale'],
5152
+ transform: ['transform'],
5153
+ '-webkit-transform': ['transform'],
5154
+ 'transform-origin': ['transformOrigin'],
5155
+ '-webkit-transform-origin': ['transformOrigin'],
4820
5156
  'z-index': ['zIndex'],
4821
5157
  'vertical-align': ['verticalAlign'],
4822
5158
  'text-decoration': [
@@ -4871,13 +5207,30 @@ export function decorate(style) {
4871
5207
  if (lines.includes('underline')) {
4872
5208
  style.underline = inkColor(style.textDecorationColor ?? 'currentColor', style.color);
4873
5209
  style.underlineStyle = style.textDecorationStyle;
4874
- style.underlineThickness = style.textDecorationThickness;
5210
+ style.underlineThickness = usedThickness(style);
4875
5211
  style.underlineOffset = style.textUnderlineOffset;
4876
5212
  }
4877
5213
  if (lines.includes('line-through')) {
4878
5214
  style.lineThrough = inkColor(style.textDecorationColor ?? 'currentColor', style.color);
5215
+ style.lineThroughStyle = style.textDecorationStyle;
5216
+ style.lineThroughThickness = usedThickness(style);
4879
5217
  }
4880
5218
  }
5219
+ /**
5220
+ * How thick the lines a box decorates its text with are. `auto` leaves it
5221
+ * to the user agent, which is to take it from the font where the font says
5222
+ * (CSS Text Decoration 4, 2.4.1); neither text engine here reports a face's
5223
+ * underline, so it is a part of the font size, Blink's tenth — of the size
5224
+ * of the box that set the decoration, which draws one line of one thickness
5225
+ * through everything inside it (2.9). A pixel for every size drew the dots
5226
+ * under the `<abbr>` of a 140px title as a hairline. `from-font` is `auto`
5227
+ * for the same want of a metric, a length is itself on whole pixels, and
5228
+ * no line is thinner than one.
5229
+ */
5230
+ function usedThickness(style) {
5231
+ const length = style.textDecorationThickness;
5232
+ return Math.max(1, length === null ? style.fontSize / 10 : Math.round(length));
5233
+ }
4881
5234
  /**
4882
5235
  * The absolutely positioned styles that were inline-level before they were
4883
5236
  * made blocks: where such a box would have been in flow is on its line,
@@ -4984,4 +5337,54 @@ export function blockify(style, inFlexContainer) {
4984
5337
  return;
4985
5338
  }
4986
5339
  }
5340
+ /**
5341
+ * The `display` a `<button>` is laid out with, which is not always the one
5342
+ * it was given (HTML's rendering section, button layout, 15.5.3): a flex
5343
+ * box or a grid is that, and of the rest, "a value such that the outer
5344
+ * display type is 'inline'" behaves as `inline-block` and anything else as
5345
+ * `flow-root`. Blink computes them so — `inline-block` for `inline` and
5346
+ * `inline-table`, `block` for `table` and `list-item` — and makes the one
5347
+ * block flow of each; the formatting context is the layout's to give every
5348
+ * button that is a block (`establishesBFC`).
5349
+ *
5350
+ * Left an inline box, a button around a block was broken in two around it,
5351
+ * as any inline box is: as wide as the line, its `height` applied to
5352
+ * nothing, and its border and padding drawn above and below the line it
5353
+ * opened. A table's part is left what it is, where the two disagree: a
5354
+ * block to the text, an inline-block in Blink. After `blockify`, since a
5355
+ * float, an absolute box and a flex item are blocks first.
5356
+ */
5357
+ export function settleButton(style) {
5358
+ switch (style.display) {
5359
+ case 'inline':
5360
+ case 'inline-table':
5361
+ style.display = 'inline-block';
5362
+ return;
5363
+ case 'list-item':
5364
+ case 'table':
5365
+ style.display = 'block';
5366
+ return;
5367
+ default:
5368
+ return;
5369
+ }
5370
+ }
5371
+ /** The generic font families, which a list ending in one falls back
5372
+ * through already, in the text engine (CSS Fonts 4, 4.2). */
5373
+ const GENERIC_FAMILY = new Set([
5374
+ 'serif',
5375
+ 'sans-serif',
5376
+ 'monospace',
5377
+ 'cursive',
5378
+ 'fantasy',
5379
+ 'system-ui',
5380
+ 'ui-serif',
5381
+ 'ui-sans-serif',
5382
+ 'ui-monospace',
5383
+ 'ui-rounded',
5384
+ 'math',
5385
+ 'emoji',
5386
+ 'fangsong',
5387
+ '-apple-system',
5388
+ 'blinkmacsystemfont',
5389
+ ]);
4987
5390
  //# sourceMappingURL=style.js.map