@react-x11/components 0.11.0 → 0.13.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/flow/node.d.ts +14 -6
- package/dist/flow/node.d.ts.map +1 -1
- package/dist/flow/node.js +82 -64
- package/dist/flow/node.js.map +1 -1
- package/dist/html/controls.d.ts +38 -10
- package/dist/html/controls.d.ts.map +1 -1
- package/dist/html/controls.js +43 -58
- package/dist/html/controls.js.map +1 -1
- package/dist/html/css/cascade.d.ts +134 -20
- package/dist/html/css/cascade.d.ts.map +1 -1
- package/dist/html/css/cascade.js +538 -128
- package/dist/html/css/cascade.js.map +1 -1
- package/dist/html/css/color.d.ts +19 -0
- package/dist/html/css/color.d.ts.map +1 -1
- package/dist/html/css/color.js +86 -0
- package/dist/html/css/color.js.map +1 -1
- package/dist/html/css/parse.d.ts +40 -2
- package/dist/html/css/parse.d.ts.map +1 -1
- package/dist/html/css/parse.js +391 -13
- package/dist/html/css/parse.js.map +1 -1
- package/dist/html/css/style.d.ts +52 -1
- package/dist/html/css/style.d.ts.map +1 -1
- package/dist/html/css/style.js +328 -11
- package/dist/html/css/style.js.map +1 -1
- package/dist/html/css/ua.d.ts +11 -1
- package/dist/html/css/ua.d.ts.map +1 -1
- package/dist/html/css/ua.js +73 -7
- package/dist/html/css/ua.js.map +1 -1
- package/dist/html/css/values.d.ts +10 -1
- package/dist/html/css/values.d.ts.map +1 -1
- package/dist/html/css/values.js +27 -2
- package/dist/html/css/values.js.map +1 -1
- package/dist/html/css/vars.d.ts +5 -0
- package/dist/html/css/vars.d.ts.map +1 -1
- package/dist/html/css/vars.js +2 -2
- package/dist/html/css/vars.js.map +1 -1
- package/dist/html/dom.d.ts.map +1 -1
- package/dist/html/dom.js +38 -3
- package/dist/html/dom.js.map +1 -1
- package/dist/html/fonts.d.ts +25 -1
- package/dist/html/fonts.d.ts.map +1 -1
- package/dist/html/fonts.js +99 -6
- package/dist/html/fonts.js.map +1 -1
- package/dist/html/form.d.ts +200 -0
- package/dist/html/form.d.ts.map +1 -0
- package/dist/html/form.js +801 -0
- package/dist/html/form.js.map +1 -0
- package/dist/html/index.d.ts +12 -0
- package/dist/html/index.d.ts.map +1 -1
- package/dist/html/index.js +30 -264
- package/dist/html/index.js.map +1 -1
- package/dist/html/layout/block.d.ts +11 -2
- package/dist/html/layout/block.d.ts.map +1 -1
- package/dist/html/layout/block.js +186 -19
- package/dist/html/layout/block.js.map +1 -1
- package/dist/html/layout/boxes.d.ts +65 -7
- package/dist/html/layout/boxes.d.ts.map +1 -1
- package/dist/html/layout/boxes.js +87 -22
- package/dist/html/layout/boxes.js.map +1 -1
- package/dist/html/layout/cache.d.ts +2 -2
- package/dist/html/layout/cache.d.ts.map +1 -1
- package/dist/html/layout/cache.js +8 -3
- package/dist/html/layout/cache.js.map +1 -1
- package/dist/html/layout/flex.js +4 -1
- package/dist/html/layout/flex.js.map +1 -1
- package/dist/html/layout/floats.d.ts +4 -0
- package/dist/html/layout/floats.d.ts.map +1 -1
- package/dist/html/layout/floats.js +26 -0
- package/dist/html/layout/floats.js.map +1 -1
- package/dist/html/layout/inline.d.ts +26 -2
- package/dist/html/layout/inline.d.ts.map +1 -1
- package/dist/html/layout/inline.js +1232 -115
- package/dist/html/layout/inline.js.map +1 -1
- package/dist/html/node.d.ts +120 -15
- package/dist/html/node.d.ts.map +1 -1
- package/dist/html/node.js +993 -174
- package/dist/html/node.js.map +1 -1
- package/dist/html/paint.d.ts +75 -2
- package/dist/html/paint.d.ts.map +1 -1
- package/dist/html/paint.js +836 -200
- package/dist/html/paint.js.map +1 -1
- package/dist/html/surfaces.d.ts +7 -2
- package/dist/html/surfaces.d.ts.map +1 -1
- package/dist/html/surfaces.js +16 -0
- package/dist/html/surfaces.js.map +1 -1
- package/dist/html/widgets.d.ts +29 -0
- package/dist/html/widgets.d.ts.map +1 -0
- package/dist/html/widgets.js +620 -0
- package/dist/html/widgets.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/internal/window.d.ts +22 -0
- package/dist/internal/window.d.ts.map +1 -1
- package/dist/internal/window.js +35 -8
- package/dist/internal/window.js.map +1 -1
- package/dist/richtext/node.d.ts +7 -0
- package/dist/richtext/node.d.ts.map +1 -1
- package/dist/richtext/node.js.map +1 -1
- package/package.json +6 -4
- package/src/flow/node.ts +80 -64
- package/src/html/controls.ts +79 -58
- package/src/html/css/cascade.ts +610 -146
- package/src/html/css/color.ts +87 -0
- package/src/html/css/parse.ts +431 -13
- package/src/html/css/style.ts +367 -11
- package/src/html/css/ua.ts +76 -8
- package/src/html/css/values.ts +31 -3
- package/src/html/css/vars.ts +2 -2
- package/src/html/dom.ts +41 -3
- package/src/html/fonts.ts +116 -6
- package/src/html/form.ts +962 -0
- package/src/html/index.ts +49 -300
- package/src/html/layout/block.ts +231 -16
- package/src/html/layout/boxes.ts +133 -32
- package/src/html/layout/cache.ts +8 -3
- package/src/html/layout/flex.ts +4 -1
- package/src/html/layout/floats.ts +24 -0
- package/src/html/layout/inline.ts +1445 -108
- package/src/html/node.ts +1010 -166
- package/src/html/paint.ts +980 -270
- package/src/html/surfaces.ts +21 -1
- package/src/html/widgets.ts +735 -0
- package/src/index.ts +1 -0
- package/src/internal/window.ts +36 -8
- package/src/richtext/node.ts +7 -0
package/src/html/css/style.ts
CHANGED
|
@@ -40,6 +40,7 @@ import {
|
|
|
40
40
|
} from './content.js';
|
|
41
41
|
import type { ContentItem, CounterChange } from './content.js';
|
|
42
42
|
import type { CustomProps } from './vars.js';
|
|
43
|
+
import { LIGHT_DARK, lightDark, usedColorScheme } from './color.js';
|
|
43
44
|
|
|
44
45
|
export type Display =
|
|
45
46
|
| 'none'
|
|
@@ -202,6 +203,14 @@ export type ContentSize =
|
|
|
202
203
|
export interface ComputedStyle {
|
|
203
204
|
// --- inherited ------------------------------------------------------------
|
|
204
205
|
color: string;
|
|
206
|
+
/**
|
|
207
|
+
* The colour scheme the element is drawn in (CSS Color Adjust 1, 2.2):
|
|
208
|
+
* its `color-scheme` resolved against the scheme the palette is, which
|
|
209
|
+
* stands for the reader's preference, as it does for `@media
|
|
210
|
+
* (prefers-color-scheme)`. What a `light-dark()` picks by. `normal` is
|
|
211
|
+
* the palette's own, since the palette is this renderer's default look.
|
|
212
|
+
*/
|
|
213
|
+
colorScheme: 'light' | 'dark';
|
|
205
214
|
fontFamily: string;
|
|
206
215
|
fontSize: number;
|
|
207
216
|
fontWeight: number;
|
|
@@ -320,6 +329,11 @@ export interface ComputedStyle {
|
|
|
320
329
|
float: 'none' | 'left' | 'right';
|
|
321
330
|
clear: 'none' | 'left' | 'right' | 'both';
|
|
322
331
|
boxSizing: 'content-box' | 'border-box';
|
|
332
|
+
/** `box-decoration-break` (CSS Fragmentation 3, 5.4): how an inline box
|
|
333
|
+
* that goes on to another line places its background images and
|
|
334
|
+
* gradients — `slice`, from its fragments laid end to end as one box,
|
|
335
|
+
* or `clone`, from each fragment's own. */
|
|
336
|
+
boxDecorationBreak: 'slice' | 'clone';
|
|
323
337
|
/** Whether a block is a line-clamp container, and how many lines of its
|
|
324
338
|
* formatting context it shows (CSS Overflow 4, 5.3.1): its `max-lines`,
|
|
325
339
|
* or Infinity for `line-clamp: auto`, which shows as many as its height
|
|
@@ -505,6 +519,10 @@ export interface ComputedStyle {
|
|
|
505
519
|
* replaced whole when any of them changes, and `NO_BORDER_IMAGE` where
|
|
506
520
|
* nothing sets them. */
|
|
507
521
|
borderImage: BorderImage;
|
|
522
|
+
/** `mask-image` and the longhands that place its layers (CSS Masking 1,
|
|
523
|
+
* 7): one object, replaced whole when any of them changes, and
|
|
524
|
+
* `NO_MASK` where nothing sets them. */
|
|
525
|
+
mask: MaskLayers;
|
|
508
526
|
/** `-webkit-text-fill-color`: what the glyphs are filled with where it
|
|
509
527
|
* is not the text's `color` — Tailwind's `text-transparent` over a
|
|
510
528
|
* `bg-clip-text` gradient; null for `color`, as `currentColor` is. */
|
|
@@ -608,6 +626,7 @@ export interface ComputedStyle {
|
|
|
608
626
|
* same list. */
|
|
609
627
|
export const INHERITED = [
|
|
610
628
|
'color',
|
|
629
|
+
'colorScheme',
|
|
611
630
|
'textFillColor',
|
|
612
631
|
'fontFamily',
|
|
613
632
|
'fontSize',
|
|
@@ -681,6 +700,20 @@ export interface RootLook {
|
|
|
681
700
|
controlPadY: number;
|
|
682
701
|
controlBorder: number;
|
|
683
702
|
controlRadius: number;
|
|
703
|
+
/**
|
|
704
|
+
* The size the palette's widgets set their text at, `theme.fontSize` — and
|
|
705
|
+
* so the size of a control's text, whatever the text around it is: a
|
|
706
|
+
* browser's controls take a system font rather than their parent's, and
|
|
707
|
+
* this is the system's. Without it, `fontSize`.
|
|
708
|
+
*/
|
|
709
|
+
controlFontSize?: number;
|
|
710
|
+
/**
|
|
711
|
+
* The family they set it in, `theme.fontFamily`, for the same reason: a
|
|
712
|
+
* browser's system font is its own, Arial in Chrome whatever the page is
|
|
713
|
+
* set in. A `<textarea>` is `monoFamily`, a browser's `monospace`, as it
|
|
714
|
+
* is in Chrome. Without it, `fontFamily`.
|
|
715
|
+
*/
|
|
716
|
+
controlFontFamily?: string;
|
|
684
717
|
}
|
|
685
718
|
|
|
686
719
|
export function initialStyle(look: RootLook, scale = 1): ComputedStyle {
|
|
@@ -689,6 +722,7 @@ export function initialStyle(look: RootLook, scale = 1): ComputedStyle {
|
|
|
689
722
|
// a copy of the literal, not the literal: see `copyStyle`
|
|
690
723
|
return copyStyle({
|
|
691
724
|
color: look.color,
|
|
725
|
+
colorScheme: look.colorScheme,
|
|
692
726
|
fontFamily: look.fontFamily,
|
|
693
727
|
fontSize: look.fontSize,
|
|
694
728
|
fontWeight: 400,
|
|
@@ -747,6 +781,7 @@ export function initialStyle(look: RootLook, scale = 1): ComputedStyle {
|
|
|
747
781
|
float: 'none',
|
|
748
782
|
clear: 'none',
|
|
749
783
|
boxSizing: 'content-box',
|
|
784
|
+
boxDecorationBreak: 'slice',
|
|
750
785
|
lineClamp: null,
|
|
751
786
|
maxLines: null,
|
|
752
787
|
clampContinue: 'auto',
|
|
@@ -846,6 +881,7 @@ export function initialStyle(look: RootLook, scale = 1): ComputedStyle {
|
|
|
846
881
|
backgroundClips: null,
|
|
847
882
|
backgroundOrigins: null,
|
|
848
883
|
borderImage: NO_BORDER_IMAGE,
|
|
884
|
+
mask: NO_MASK,
|
|
849
885
|
textFillColor: null,
|
|
850
886
|
|
|
851
887
|
textDecorationLine: 'none',
|
|
@@ -941,6 +977,7 @@ export function inherit(
|
|
|
941
977
|
// quarters of the cost of every element's style, a store through a
|
|
942
978
|
// computed name costing thirty times one through a written one
|
|
943
979
|
out.color = parent.color;
|
|
980
|
+
out.colorScheme = parent.colorScheme;
|
|
944
981
|
out.textFillColor = parent.textFillColor;
|
|
945
982
|
out.fontFamily = parent.fontFamily;
|
|
946
983
|
out.fontSize = parent.fontSize;
|
|
@@ -1162,6 +1199,15 @@ export function applyDeclaration(
|
|
|
1162
1199
|
if (/!\s*important$/i.test(value)) {
|
|
1163
1200
|
value = value.replace(/!\s*important$/i, '').trim();
|
|
1164
1201
|
}
|
|
1202
|
+
// the branch of each `light-dark()` this element's scheme picks, before
|
|
1203
|
+
// anything reads the value: it stands wherever a colour can, in a
|
|
1204
|
+
// shorthand, a shadow, a gradient or a mix, and most often arrives
|
|
1205
|
+
// through a `var()` from a theme on `:root`
|
|
1206
|
+
if (LIGHT_DARK.test(value)) {
|
|
1207
|
+
const picked = lightDark(value, style.colorScheme);
|
|
1208
|
+
if (picked === null) return;
|
|
1209
|
+
value = picked;
|
|
1210
|
+
}
|
|
1165
1211
|
|
|
1166
1212
|
const color = COLOR_PROPS[name];
|
|
1167
1213
|
if (color) {
|
|
@@ -1178,6 +1224,12 @@ export function applyDeclaration(
|
|
|
1178
1224
|
}
|
|
1179
1225
|
|
|
1180
1226
|
switch (name) {
|
|
1227
|
+
case 'color-scheme': {
|
|
1228
|
+
const scheme = usedColorScheme(value, ctx.initial?.colorScheme);
|
|
1229
|
+
if (scheme) style.colorScheme = scheme;
|
|
1230
|
+
return;
|
|
1231
|
+
}
|
|
1232
|
+
|
|
1181
1233
|
// --- box ----------------------------------------------------------------
|
|
1182
1234
|
case 'display': {
|
|
1183
1235
|
const v = value.toLowerCase();
|
|
@@ -1366,6 +1418,12 @@ export function applyDeclaration(
|
|
|
1366
1418
|
if (v === 'border-box' || v === 'content-box') style.boxSizing = v;
|
|
1367
1419
|
return;
|
|
1368
1420
|
}
|
|
1421
|
+
case 'box-decoration-break':
|
|
1422
|
+
case '-webkit-box-decoration-break': {
|
|
1423
|
+
const v = value.trim().toLowerCase();
|
|
1424
|
+
if (v === 'slice' || v === 'clone') style.boxDecorationBreak = v;
|
|
1425
|
+
return;
|
|
1426
|
+
}
|
|
1369
1427
|
case 'aspect-ratio': {
|
|
1370
1428
|
const parsed = parseAspectRatio(value);
|
|
1371
1429
|
if (parsed !== undefined) style.aspectRatio = parsed;
|
|
@@ -1906,6 +1964,67 @@ export function applyDeclaration(
|
|
|
1906
1964
|
return;
|
|
1907
1965
|
}
|
|
1908
1966
|
|
|
1967
|
+
// --- masking --------------------------------------------------------------
|
|
1968
|
+
// Each `-webkit-` name is the same property's (Compatibility, 5): every
|
|
1969
|
+
// engine takes both, and a page writes both — Wikipedia's icons do.
|
|
1970
|
+
case 'mask':
|
|
1971
|
+
case '-webkit-mask': {
|
|
1972
|
+
const mask = readMask(value, ctx);
|
|
1973
|
+
if (mask) style.mask = mask;
|
|
1974
|
+
return;
|
|
1975
|
+
}
|
|
1976
|
+
case 'mask-image':
|
|
1977
|
+
case '-webkit-mask-image': {
|
|
1978
|
+
// `none` a layer as much as an image is; a bad url drops it all
|
|
1979
|
+
const images: BackgroundImage[] = [];
|
|
1980
|
+
for (const part of splitCommas(value)) {
|
|
1981
|
+
const image = maskImageOf(part, ctx);
|
|
1982
|
+
if (image === undefined) return;
|
|
1983
|
+
images.push(image);
|
|
1984
|
+
}
|
|
1985
|
+
if (images.length) style.mask = { ...style.mask, images };
|
|
1986
|
+
return;
|
|
1987
|
+
}
|
|
1988
|
+
case 'mask-repeat':
|
|
1989
|
+
case '-webkit-mask-repeat': {
|
|
1990
|
+
const repeats = layerValues(value, (part) => {
|
|
1991
|
+
const words = splitValue(part.toLowerCase());
|
|
1992
|
+
return words.length <= 2 ? readRepeat(words) : null;
|
|
1993
|
+
});
|
|
1994
|
+
if (repeats) style.mask = { ...style.mask, repeats };
|
|
1995
|
+
return;
|
|
1996
|
+
}
|
|
1997
|
+
case 'mask-size':
|
|
1998
|
+
case '-webkit-mask-size': {
|
|
1999
|
+
const sizes = layerValues(value, (part) =>
|
|
2000
|
+
backgroundSizeOf(splitValue(part), ctx),
|
|
2001
|
+
);
|
|
2002
|
+
if (sizes) style.mask = { ...style.mask, sizes };
|
|
2003
|
+
return;
|
|
2004
|
+
}
|
|
2005
|
+
case 'mask-position':
|
|
2006
|
+
case '-webkit-mask-position': {
|
|
2007
|
+
const positions = layerValues(value, (part) =>
|
|
2008
|
+
positionPair(splitValue(part), ctx),
|
|
2009
|
+
);
|
|
2010
|
+
if (positions) style.mask = { ...style.mask, positions };
|
|
2011
|
+
return;
|
|
2012
|
+
}
|
|
2013
|
+
case 'mask-origin':
|
|
2014
|
+
case '-webkit-mask-origin': {
|
|
2015
|
+
const origins = layerValues(value, (part) => maskBox(part));
|
|
2016
|
+
if (origins) style.mask = { ...style.mask, origins };
|
|
2017
|
+
return;
|
|
2018
|
+
}
|
|
2019
|
+
case 'mask-clip':
|
|
2020
|
+
case '-webkit-mask-clip': {
|
|
2021
|
+
const clips = layerValues(value, (part) =>
|
|
2022
|
+
part.toLowerCase() === 'no-clip' ? 'border-box' : maskBox(part),
|
|
2023
|
+
);
|
|
2024
|
+
if (clips) style.mask = { ...style.mask, clips };
|
|
2025
|
+
return;
|
|
2026
|
+
}
|
|
2027
|
+
|
|
1909
2028
|
// --- generated content --------------------------------------------------
|
|
1910
2029
|
case 'content': {
|
|
1911
2030
|
const parsed = parseContent(value);
|
|
@@ -1962,7 +2081,11 @@ export function applyDeclaration(
|
|
|
1962
2081
|
// handed a face at a size of millions shapes and caches glyphs that
|
|
1963
2082
|
// size, and a nest of `larger`s gets there on its own
|
|
1964
2083
|
const most = MAX_FONT_SIZE * ctx.scale;
|
|
1965
|
-
const kw = keywordFontSize(
|
|
2084
|
+
const kw = keywordFontSize(
|
|
2085
|
+
value,
|
|
2086
|
+
parent.fontSize,
|
|
2087
|
+
ctx.initial?.fontSize ?? ctx.rem,
|
|
2088
|
+
);
|
|
1966
2089
|
if (kw !== null) {
|
|
1967
2090
|
style.fontSize = Math.min(kw, most);
|
|
1968
2091
|
return;
|
|
@@ -2013,7 +2136,13 @@ export function applyDeclaration(
|
|
|
2013
2136
|
style.lineHeight = len;
|
|
2014
2137
|
style.lineHeightIsLength = true;
|
|
2015
2138
|
} else if (len && typeof len === 'object') {
|
|
2016
|
-
|
|
2139
|
+
// a percentage of the element's own font size, the one it is
|
|
2140
|
+
// computed with (CSS 2.1 10.8.1), which `em` already is: not the
|
|
2141
|
+
// size in `style` as this declaration comes up, which a `font`
|
|
2142
|
+
// earlier in the same rule has just set to its own — `p { font:
|
|
2143
|
+
// 12px …; line-height: 100% }` under a `.lead { font-size: 16px }`
|
|
2144
|
+
// set sixteen-pixel text on twelve-pixel lines
|
|
2145
|
+
const px = resolve(len, ctx.em);
|
|
2017
2146
|
if (px < 0) return;
|
|
2018
2147
|
style.lineHeight = px;
|
|
2019
2148
|
style.lineHeightIsLength = true;
|
|
@@ -2462,7 +2591,12 @@ export function applyDeclaration(
|
|
|
2462
2591
|
return;
|
|
2463
2592
|
}
|
|
2464
2593
|
case 'align-content': {
|
|
2465
|
-
|
|
2594
|
+
// `normal` is `stretch` in a flex and a grid container, the only
|
|
2595
|
+
// boxes it moves anything in (CSS Box Alignment 3, 5.1)
|
|
2596
|
+
const v =
|
|
2597
|
+
value.trim().toLowerCase() === 'normal'
|
|
2598
|
+
? 'stretch'
|
|
2599
|
+
: alignKeyword(value);
|
|
2466
2600
|
if (v) style.alignContent = v as ComputedStyle['alignContent'];
|
|
2467
2601
|
return;
|
|
2468
2602
|
}
|
|
@@ -2697,6 +2831,30 @@ export function applyDeclaration(
|
|
|
2697
2831
|
}
|
|
2698
2832
|
return;
|
|
2699
2833
|
}
|
|
2834
|
+
case 'place-content': {
|
|
2835
|
+
// `<'align-content'> <'justify-content'>?` (CSS Box Alignment 3,
|
|
2836
|
+
// 5.3), either half one keyword or two — `first baseline`, `safe
|
|
2837
|
+
// center` — and the second the first again where it is left out,
|
|
2838
|
+
// but for a baseline, which is no `justify-content`: `start`
|
|
2839
|
+
const words = value.trim().toLowerCase().split(/\s+/);
|
|
2840
|
+
const span = (at: number) =>
|
|
2841
|
+
/^(?:safe|unsafe|first|last)$/.test(words[at] ?? '') ? 2 : 1;
|
|
2842
|
+
const cut = span(0);
|
|
2843
|
+
const align = words.slice(0, cut).join(' ');
|
|
2844
|
+
const rest = words.slice(cut);
|
|
2845
|
+
if (rest.length && rest.length !== span(cut)) return;
|
|
2846
|
+
const justify = rest.length
|
|
2847
|
+
? rest.join(' ')
|
|
2848
|
+
: /baseline$/.test(align)
|
|
2849
|
+
? 'start'
|
|
2850
|
+
: align;
|
|
2851
|
+
// a shorthand is one declaration: either half unread drops both
|
|
2852
|
+
if (align !== 'normal' && !alignKeyword(align)) return;
|
|
2853
|
+
if (!justifyKeyword(justify)) return;
|
|
2854
|
+
applyDeclaration(style, parent, 'align-content', align, ctx);
|
|
2855
|
+
applyDeclaration(style, parent, 'justify-content', justify, ctx);
|
|
2856
|
+
return;
|
|
2857
|
+
}
|
|
2700
2858
|
case 'place-items':
|
|
2701
2859
|
case 'place-self': {
|
|
2702
2860
|
const parts = splitValue(value);
|
|
@@ -3144,6 +3302,157 @@ export const NO_BORDER_IMAGE: BorderImage = {
|
|
|
3144
3302
|
repeat: ['stretch', 'stretch'],
|
|
3145
3303
|
};
|
|
3146
3304
|
|
|
3305
|
+
/**
|
|
3306
|
+
* An element's mask layers (CSS Masking 1, 7), top first, a list a
|
|
3307
|
+
* property as a background's are: each layer takes the images' place in
|
|
3308
|
+
* the others, and they repeat where they are fewer. A layer's image is a
|
|
3309
|
+
* url, a gradient, or null for `none` — which is the one layer of an
|
|
3310
|
+
* element that is not masked.
|
|
3311
|
+
*/
|
|
3312
|
+
export interface MaskLayers {
|
|
3313
|
+
images: readonly BackgroundImage[];
|
|
3314
|
+
repeats: readonly BackgroundRepeat[];
|
|
3315
|
+
sizes: readonly ComputedStyle['backgroundSize'][];
|
|
3316
|
+
positions: readonly (readonly [Len, Len])[];
|
|
3317
|
+
/** The box a layer is placed in, and the one it is painted in: the
|
|
3318
|
+
* border box unless a layer says (7.5, 7.6). */
|
|
3319
|
+
origins: readonly VisualBox[];
|
|
3320
|
+
clips: readonly VisualBox[];
|
|
3321
|
+
}
|
|
3322
|
+
|
|
3323
|
+
/** Whether a style masks its element: a layer of its mask has an image. */
|
|
3324
|
+
export function masked(style: ComputedStyle): boolean {
|
|
3325
|
+
const mask = style.mask;
|
|
3326
|
+
if (mask === NO_MASK) return false;
|
|
3327
|
+
for (const image of mask.images) if (image !== null) return true;
|
|
3328
|
+
return false;
|
|
3329
|
+
}
|
|
3330
|
+
|
|
3331
|
+
/**
|
|
3332
|
+
* One layer's `mask-image`: a url, a gradient — or none, for an image
|
|
3333
|
+
* function this does not draw, or a `url(#id)`, which names an SVG
|
|
3334
|
+
* `<mask>` element in the document rather than an image to fetch (7.2):
|
|
3335
|
+
* drawn with no mask rather than as the transparent layer a mask that
|
|
3336
|
+
* cannot be had is, since this draws no `<mask>`. Undefined where it is no
|
|
3337
|
+
* image at all.
|
|
3338
|
+
*/
|
|
3339
|
+
function maskImageOf(
|
|
3340
|
+
text: string,
|
|
3341
|
+
ctx: UnitContext,
|
|
3342
|
+
): BackgroundImage | undefined {
|
|
3343
|
+
const image = backgroundImageOf(text, ctx);
|
|
3344
|
+
return typeof image === 'string' && image[0] === '#' ? null : image;
|
|
3345
|
+
}
|
|
3346
|
+
|
|
3347
|
+
/** A `<geometry-box>` as an HTML element has it (CSS Masking 1, 7.5): the
|
|
3348
|
+
* SVG boxes are its content box and its border box. */
|
|
3349
|
+
function maskBox(text: string): VisualBox | null {
|
|
3350
|
+
const v = text.trim().toLowerCase();
|
|
3351
|
+
if (isVisualBox(v)) return v;
|
|
3352
|
+
if (v === 'fill-box') return 'content-box';
|
|
3353
|
+
if (v === 'stroke-box' || v === 'view-box') return 'border-box';
|
|
3354
|
+
return null;
|
|
3355
|
+
}
|
|
3356
|
+
|
|
3357
|
+
/**
|
|
3358
|
+
* The `mask` shorthand (CSS Masking 1, 7.10): a layer a comma group, each
|
|
3359
|
+
* an image, a position with a size after a `/`, a repeat, one box for the
|
|
3360
|
+
* origin and the clip or two, `no-clip`, a compositing operator and a
|
|
3361
|
+
* mode, each at most once, in any order. The operator and the mode are
|
|
3362
|
+
* read and not honoured: layers are added, and an image masks by its
|
|
3363
|
+
* alpha, which is what `match-source` makes of one. Null where a layer is
|
|
3364
|
+
* not one, which drops the declaration.
|
|
3365
|
+
*/
|
|
3366
|
+
function readMask(value: string, ctx: UnitContext): MaskLayers | null {
|
|
3367
|
+
const out = {
|
|
3368
|
+
images: [] as BackgroundImage[],
|
|
3369
|
+
repeats: [] as BackgroundRepeat[],
|
|
3370
|
+
sizes: [] as ComputedStyle['backgroundSize'][],
|
|
3371
|
+
positions: [] as [Len, Len][],
|
|
3372
|
+
origins: [] as VisualBox[],
|
|
3373
|
+
clips: [] as VisualBox[],
|
|
3374
|
+
};
|
|
3375
|
+
for (const text of splitCommas(value)) {
|
|
3376
|
+
const parts = splitValue(text).flatMap(splitSlash);
|
|
3377
|
+
let image: BackgroundImage = null;
|
|
3378
|
+
let repeat = NO_MASK.repeats[0];
|
|
3379
|
+
let size: ComputedStyle['backgroundSize'] = 'auto';
|
|
3380
|
+
let position: [Len, Len] = [0, 0];
|
|
3381
|
+
const boxes: VisualBox[] = [];
|
|
3382
|
+
let seen = 0;
|
|
3383
|
+
const once = (bit: number): boolean => {
|
|
3384
|
+
if (seen & bit) return false;
|
|
3385
|
+
seen |= bit;
|
|
3386
|
+
return true;
|
|
3387
|
+
};
|
|
3388
|
+
for (let i = 0; i < parts.length;) {
|
|
3389
|
+
const part = parts[i];
|
|
3390
|
+
const v = part.toLowerCase();
|
|
3391
|
+
if (v === 'none' || v.startsWith('url(') || IMAGE_FUNCTION.test(v)) {
|
|
3392
|
+
if (!once(1)) return null;
|
|
3393
|
+
const read = maskImageOf(part, ctx);
|
|
3394
|
+
if (read === undefined) return null;
|
|
3395
|
+
image = read;
|
|
3396
|
+
i += 1;
|
|
3397
|
+
} else if (v === 'repeat-x' || v === 'repeat-y' || REPEATS.has(v)) {
|
|
3398
|
+
if (!once(2)) return null;
|
|
3399
|
+
const pair = REPEATS.has(v) && REPEATS.has(parts[i + 1]?.toLowerCase());
|
|
3400
|
+
const read = readRepeat(pair ? [v, parts[i + 1].toLowerCase()] : [v]);
|
|
3401
|
+
if (!read) return null;
|
|
3402
|
+
repeat = read;
|
|
3403
|
+
i += pair ? 2 : 1;
|
|
3404
|
+
} else if (maskBox(v) || v === 'no-clip') {
|
|
3405
|
+
if (boxes.length === 2 || (v === 'no-clip' && !once(4))) return null;
|
|
3406
|
+
boxes.push(maskBox(v) ?? 'border-box');
|
|
3407
|
+
i += 1;
|
|
3408
|
+
} else if (/^(?:add|subtract|intersect|exclude)$/.test(v)) {
|
|
3409
|
+
if (!once(8)) return null;
|
|
3410
|
+
i += 1;
|
|
3411
|
+
} else if (/^(?:alpha|luminance|match-source)$/.test(v)) {
|
|
3412
|
+
if (!once(16)) return null;
|
|
3413
|
+
i += 1;
|
|
3414
|
+
} else if (isPositionPart(v, ctx)) {
|
|
3415
|
+
if (!once(32)) return null;
|
|
3416
|
+
let end = i;
|
|
3417
|
+
while (
|
|
3418
|
+
end < parts.length &&
|
|
3419
|
+
isPositionPart(parts[end].toLowerCase(), ctx)
|
|
3420
|
+
) {
|
|
3421
|
+
end += 1;
|
|
3422
|
+
}
|
|
3423
|
+
const read = positionPair(parts.slice(i, end), ctx);
|
|
3424
|
+
if (!read) return null;
|
|
3425
|
+
position = read;
|
|
3426
|
+
i = end;
|
|
3427
|
+
if (parts[i] !== '/') continue;
|
|
3428
|
+
const first = parts[i + 1]?.toLowerCase();
|
|
3429
|
+
if (first === 'cover' || first === 'contain') {
|
|
3430
|
+
size = first;
|
|
3431
|
+
i += 2;
|
|
3432
|
+
} else if (first !== undefined && isSizePart(first, ctx)) {
|
|
3433
|
+
const two =
|
|
3434
|
+
i + 2 < parts.length && isSizePart(parts[i + 2].toLowerCase(), ctx);
|
|
3435
|
+
size =
|
|
3436
|
+
backgroundSizeOf(parts.slice(i + 1, i + (two ? 3 : 2)), ctx) ??
|
|
3437
|
+
'auto';
|
|
3438
|
+
i += two ? 3 : 2;
|
|
3439
|
+
} else {
|
|
3440
|
+
return null;
|
|
3441
|
+
}
|
|
3442
|
+
} else {
|
|
3443
|
+
return null;
|
|
3444
|
+
}
|
|
3445
|
+
}
|
|
3446
|
+
out.images.push(image);
|
|
3447
|
+
out.repeats.push(repeat);
|
|
3448
|
+
out.sizes.push(size);
|
|
3449
|
+
out.positions.push(position);
|
|
3450
|
+
out.origins.push(boxes[0] ?? 'border-box');
|
|
3451
|
+
out.clips.push(boxes[1] ?? boxes[0] ?? 'border-box');
|
|
3452
|
+
}
|
|
3453
|
+
return out.images.length ? out : null;
|
|
3454
|
+
}
|
|
3455
|
+
|
|
3147
3456
|
/** One to four values, one a side as `margin` takes them; null where one
|
|
3148
3457
|
* is not a value. */
|
|
3149
3458
|
function sidesOf<T>(
|
|
@@ -3338,6 +3647,15 @@ function repeatPair(x: RepeatMode, y: RepeatMode): BackgroundRepeat {
|
|
|
3338
3647
|
|
|
3339
3648
|
const REPEAT = repeatPair('repeat', 'repeat');
|
|
3340
3649
|
|
|
3650
|
+
export const NO_MASK: MaskLayers = {
|
|
3651
|
+
images: [null],
|
|
3652
|
+
repeats: [REPEAT],
|
|
3653
|
+
sizes: ['auto'],
|
|
3654
|
+
positions: [[0, 0]],
|
|
3655
|
+
origins: ['border-box'],
|
|
3656
|
+
clips: ['border-box'],
|
|
3657
|
+
};
|
|
3658
|
+
|
|
3341
3659
|
function applyBorderShorthand(
|
|
3342
3660
|
style: ComputedStyle,
|
|
3343
3661
|
name: string,
|
|
@@ -4007,8 +4325,11 @@ function applyFontShorthand(
|
|
|
4007
4325
|
// dropped whole, as CSS drops any value it cannot read — a line height
|
|
4008
4326
|
// below nought included: `font: 4em/-2em serif` set the text at 4em.
|
|
4009
4327
|
const size =
|
|
4010
|
-
keywordFontSize(
|
|
4011
|
-
|
|
4328
|
+
keywordFontSize(
|
|
4329
|
+
sizeText ?? '',
|
|
4330
|
+
parent.fontSize,
|
|
4331
|
+
ctx.initial?.fontSize ?? ctx.rem,
|
|
4332
|
+
) ?? parseLength(sizeText ?? '', { ...ctx, em: parent.fontSize });
|
|
4012
4333
|
if (!family || size === null || size === AUTO) return;
|
|
4013
4334
|
if (lineText && negativeLength(lineText, ctx)) return;
|
|
4014
4335
|
// What the shorthand does not name goes back to its initial value rather
|
|
@@ -4026,12 +4347,10 @@ function applyFontShorthand(
|
|
|
4026
4347
|
style.lineHeight = 'normal';
|
|
4027
4348
|
style.lineHeightIsLength = false;
|
|
4028
4349
|
applyDeclaration(style, parent, 'font-size', sizeText, ctx);
|
|
4029
|
-
|
|
4030
|
-
|
|
4031
|
-
|
|
4032
|
-
|
|
4033
|
-
});
|
|
4034
|
-
}
|
|
4350
|
+
// against the element's own size, which `ctx.em` is once the cascade has
|
|
4351
|
+
// settled it: a later rule's `font-size` outranks this one's, and
|
|
4352
|
+
// `font: 12px/150%` under a `font-size: 16px` is on 24px lines
|
|
4353
|
+
if (lineText) applyDeclaration(style, parent, 'line-height', lineText, ctx);
|
|
4035
4354
|
applyDeclaration(style, parent, 'font-family', family, ctx);
|
|
4036
4355
|
}
|
|
4037
4356
|
|
|
@@ -4751,6 +5070,7 @@ function camel(name: string): string {
|
|
|
4751
5070
|
|
|
4752
5071
|
const INHERITED_NAMES = new Set<string>([
|
|
4753
5072
|
'color',
|
|
5073
|
+
'color-scheme',
|
|
4754
5074
|
'-webkit-text-fill-color',
|
|
4755
5075
|
'font',
|
|
4756
5076
|
'font-family',
|
|
@@ -4801,6 +5121,11 @@ export function initialOne(
|
|
|
4801
5121
|
initial: ComputedStyle,
|
|
4802
5122
|
name: string,
|
|
4803
5123
|
): void {
|
|
5124
|
+
const list = MASK_LISTS[name];
|
|
5125
|
+
if (list) {
|
|
5126
|
+
style.mask = { ...style.mask, [list]: initial.mask[list] };
|
|
5127
|
+
return;
|
|
5128
|
+
}
|
|
4804
5129
|
const keys = INHERIT_TARGETS[name];
|
|
4805
5130
|
if (!keys) return;
|
|
4806
5131
|
for (const key of keys) {
|
|
@@ -4813,6 +5138,11 @@ function inheritOne(
|
|
|
4813
5138
|
parent: ComputedStyle,
|
|
4814
5139
|
name: string,
|
|
4815
5140
|
): void {
|
|
5141
|
+
const list = MASK_LISTS[name];
|
|
5142
|
+
if (list) {
|
|
5143
|
+
style.mask = { ...style.mask, [list]: parent.mask[list] };
|
|
5144
|
+
return;
|
|
5145
|
+
}
|
|
4816
5146
|
const keys = INHERIT_TARGETS[name];
|
|
4817
5147
|
if (!keys) return;
|
|
4818
5148
|
// A border colour left to `currentColor` inherits as the keyword and
|
|
@@ -4828,8 +5158,30 @@ const sides = (
|
|
|
4828
5158
|
make: (side: (typeof SIDES)[number]) => keyof ComputedStyle,
|
|
4829
5159
|
): (keyof ComputedStyle)[] => SIDES.map(make);
|
|
4830
5160
|
|
|
5161
|
+
/**
|
|
5162
|
+
* The list of the mask's layers each longhand is, which `initial`,
|
|
5163
|
+
* `inherit` and a `var()` that fails reset alone: one field holds them all,
|
|
5164
|
+
* and reset whole, a `mask-size: calc(var(--x) - 4px)` put back the
|
|
5165
|
+
* `mask-repeat: no-repeat` before it, and Wikipedia's chevron repeated.
|
|
5166
|
+
*/
|
|
5167
|
+
const MASK_LISTS: Record<string, keyof MaskLayers> = {
|
|
5168
|
+
'mask-image': 'images',
|
|
5169
|
+
'-webkit-mask-image': 'images',
|
|
5170
|
+
'mask-repeat': 'repeats',
|
|
5171
|
+
'-webkit-mask-repeat': 'repeats',
|
|
5172
|
+
'mask-size': 'sizes',
|
|
5173
|
+
'-webkit-mask-size': 'sizes',
|
|
5174
|
+
'mask-position': 'positions',
|
|
5175
|
+
'-webkit-mask-position': 'positions',
|
|
5176
|
+
'mask-origin': 'origins',
|
|
5177
|
+
'-webkit-mask-origin': 'origins',
|
|
5178
|
+
'mask-clip': 'clips',
|
|
5179
|
+
'-webkit-mask-clip': 'clips',
|
|
5180
|
+
};
|
|
5181
|
+
|
|
4831
5182
|
const INHERIT_TARGETS: Record<string, readonly (keyof ComputedStyle)[]> = {
|
|
4832
5183
|
color: ['color'],
|
|
5184
|
+
'color-scheme': ['colorScheme'],
|
|
4833
5185
|
'-webkit-text-fill-color': ['textFillColor'],
|
|
4834
5186
|
'background-clip': [
|
|
4835
5187
|
'backgroundClipText',
|
|
@@ -4843,6 +5195,8 @@ const INHERIT_TARGETS: Record<string, readonly (keyof ComputedStyle)[]> = {
|
|
|
4843
5195
|
],
|
|
4844
5196
|
'background-origin': ['backgroundOrigin', 'backgroundOrigins'],
|
|
4845
5197
|
'border-image': ['borderImage'],
|
|
5198
|
+
mask: ['mask'],
|
|
5199
|
+
'-webkit-mask': ['mask'],
|
|
4846
5200
|
'border-image-source': ['borderImage'],
|
|
4847
5201
|
'border-image-slice': ['borderImage'],
|
|
4848
5202
|
'border-image-width': ['borderImage'],
|
|
@@ -4923,6 +5277,8 @@ const INHERIT_TARGETS: Record<string, readonly (keyof ComputedStyle)[]> = {
|
|
|
4923
5277
|
'min-height': ['minHeight', 'minHeightKeyword'],
|
|
4924
5278
|
'max-height': ['maxHeight', 'maxHeightKeyword'],
|
|
4925
5279
|
'box-sizing': ['boxSizing'],
|
|
5280
|
+
'box-decoration-break': ['boxDecorationBreak'],
|
|
5281
|
+
'-webkit-box-decoration-break': ['boxDecorationBreak'],
|
|
4926
5282
|
margin: sides((s) => `margin${s}`),
|
|
4927
5283
|
'margin-top': ['marginTop'],
|
|
4928
5284
|
'margin-right': ['marginRight'],
|
package/src/html/css/ua.ts
CHANGED
|
@@ -9,9 +9,20 @@
|
|
|
9
9
|
// author stylesheet still overrides all of it — that is what makes it a UA
|
|
10
10
|
// sheet and not a skin.
|
|
11
11
|
import { parseStylesheet } from './parse.js';
|
|
12
|
-
import type { Stylesheet } from './parse.js';
|
|
12
|
+
import type { Declaration, Stylesheet } from './parse.js';
|
|
13
13
|
import type { RootLook } from './style.js';
|
|
14
14
|
|
|
15
|
+
/**
|
|
16
|
+
* The declaration blocks that draw a control as the palette's widget rather
|
|
17
|
+
* than as the web's UA sheet has it. The cascade drops them from an element
|
|
18
|
+
* whose page styles its background or border (`Cascade`'s
|
|
19
|
+
* `dropPaletteChrome`), as Blink drops a control's native appearance
|
|
20
|
+
* (`LayoutTheme::IsControlStyled`), since a page that styles a control
|
|
21
|
+
* builds on the UA values a browser gives it. Keyed by the array, which is
|
|
22
|
+
* what a cascade candidate carries.
|
|
23
|
+
*/
|
|
24
|
+
export const PALETTE_CHROME = new WeakSet<Declaration[]>();
|
|
25
|
+
|
|
15
26
|
/** Cache key → parsed sheet. The text depends only on the look, so two
|
|
16
27
|
* documents in one themed window parse this once between them. */
|
|
17
28
|
const CACHE = new Map<string, Stylesheet>();
|
|
@@ -30,6 +41,8 @@ export function lookKey(look: RootLook): string {
|
|
|
30
41
|
look.controlPadY,
|
|
31
42
|
look.controlBorder,
|
|
32
43
|
look.controlRadius,
|
|
44
|
+
look.controlFontSize,
|
|
45
|
+
look.controlFontFamily,
|
|
33
46
|
].join('|');
|
|
34
47
|
}
|
|
35
48
|
|
|
@@ -38,6 +51,13 @@ export function uaStylesheet(look: RootLook): Stylesheet {
|
|
|
38
51
|
const hit = CACHE.get(key);
|
|
39
52
|
if (hit) return hit;
|
|
40
53
|
const sheet = parseStylesheet(uaText(look), -1_000_000);
|
|
54
|
+
// after the rest, so its values win over theirs while it applies
|
|
55
|
+
const last = sheet.rules[sheet.rules.length - 1];
|
|
56
|
+
const chrome = parseStylesheet(chromeText(look), last.order + 1);
|
|
57
|
+
for (const rule of chrome.rules) {
|
|
58
|
+
PALETTE_CHROME.add(rule.declarations);
|
|
59
|
+
sheet.rules.push(rule);
|
|
60
|
+
}
|
|
41
61
|
if (CACHE.size > 8) CACHE.clear();
|
|
42
62
|
CACHE.set(key, sheet);
|
|
43
63
|
return sheet;
|
|
@@ -181,26 +201,55 @@ col { display: table-column; }
|
|
|
181
201
|
makes a label and its input share a line. */
|
|
182
202
|
input, button, select, textarea, meter, progress {
|
|
183
203
|
display: inline-block;
|
|
184
|
-
font-family: ${look.fontFamily};
|
|
185
|
-
font-size: 1em;
|
|
186
204
|
vertical-align: middle;
|
|
187
205
|
margin: 3px 2px;
|
|
188
206
|
}
|
|
207
|
+
/* A control's text is a system font's, not its parent's: Chrome gives these
|
|
208
|
+
four \`font: -webkit-small-control\`, Arial at the default size less 2pt
|
|
209
|
+
(13.33px) whatever the text around it is set in, and Gecko \`-moz-field\`
|
|
210
|
+
the same. The system here is the palette, and its family and size are the
|
|
211
|
+
ones core's widgets are set in — so a document's form is its window's,
|
|
212
|
+
and the box a control is measured into is the size the widget mounted in
|
|
213
|
+
it draws. A <textarea> is then \`monospace\` in Chrome, the sheet's code
|
|
214
|
+
face here; a <meter> and a <progress> keep their parent's font, as they
|
|
215
|
+
do in Chrome. */
|
|
216
|
+
input, button, select, textarea {
|
|
217
|
+
font-family: ${look.controlFontFamily ?? look.fontFamily};
|
|
218
|
+
font-size: ${look.controlFontSize ?? look.fontSize}px;
|
|
219
|
+
}
|
|
220
|
+
textarea { font-family: ${mono}; }
|
|
189
221
|
/* Chrome's own UA margins for the checkables, near enough: they are the
|
|
190
222
|
controls that sit hard against their label text otherwise. */
|
|
223
|
+
/* HTML's rendering section (15.3.10): a control's text keeps none of the
|
|
224
|
+
spacing, the line height, the case or the indent of the text around it.
|
|
225
|
+
A button in a paragraph of 'line-height: 1.5' is its own font's line
|
|
226
|
+
tall, as it is in a browser. */
|
|
227
|
+
input, button, textarea {
|
|
228
|
+
letter-spacing: initial;
|
|
229
|
+
word-spacing: initial;
|
|
230
|
+
line-height: initial;
|
|
231
|
+
}
|
|
232
|
+
input, select, button, textarea {
|
|
233
|
+
text-transform: initial;
|
|
234
|
+
text-indent: initial;
|
|
235
|
+
text-shadow: initial;
|
|
236
|
+
}
|
|
191
237
|
input[type=checkbox] { margin: 3px 4px 3px 4px; }
|
|
192
238
|
input[type=radio] { margin: 3px 4px 3px 5px; }
|
|
193
239
|
input[type=hidden] { display: none; }
|
|
194
240
|
/* A <button> is drawn rather than mounted: its content is the document's —
|
|
195
241
|
an icon, a label in spans, a pill of the page's own design, which is what
|
|
196
242
|
most buttons on the web are — and the page restyles it as it restyles
|
|
197
|
-
anything.
|
|
198
|
-
|
|
243
|
+
anything. The edges here are Chrome's, which a page that styles its
|
|
244
|
+
buttons builds on: Codex gives Wikipedia's search button its side padding
|
|
245
|
+
and a 32px min-height, and leaves the 1px above and below the label, and
|
|
246
|
+
the border box the minimum holds, to the browser. One the page left alone
|
|
247
|
+
is the palette's control instead, below the rest of this sheet. */
|
|
199
248
|
button {
|
|
200
249
|
vertical-align: baseline;
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
border
|
|
250
|
+
box-sizing: border-box;
|
|
251
|
+
padding: 1px 6px;
|
|
252
|
+
border: 2px outset ${look.borderColor};
|
|
204
253
|
background-color: ${look.surface};
|
|
205
254
|
color: ${look.color};
|
|
206
255
|
text-align: center;
|
|
@@ -221,3 +270,22 @@ details[open] > summary:first-of-type { list-style-type: disclosure-open; }
|
|
|
221
270
|
[hidden] { display: none; }
|
|
222
271
|
`;
|
|
223
272
|
}
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* The palette's control, for a `<button>` whose page left its background and
|
|
276
|
+
* border alone: the chrome a mounted widget has, so a form in a document is
|
|
277
|
+
* the size of the window's own. The rules land in `PALETTE_CHROME`, and a
|
|
278
|
+
* page that styles either loses all of them — Blink counts every background
|
|
279
|
+
* and border longhand, radius included — along with the native look it
|
|
280
|
+
* would lose in a browser. Nothing stays half the palette's and half the
|
|
281
|
+
* page's.
|
|
282
|
+
*/
|
|
283
|
+
function chromeText(look: RootLook): string {
|
|
284
|
+
return `
|
|
285
|
+
button {
|
|
286
|
+
padding: ${look.controlPadY}px 0.75em;
|
|
287
|
+
border: ${look.controlBorder}px solid ${look.borderColor};
|
|
288
|
+
border-radius: ${look.controlRadius}px;
|
|
289
|
+
}
|
|
290
|
+
`;
|
|
291
|
+
}
|