@expressivecss/expressive 0.7.0 → 0.8.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 (105) hide show
  1. package/CHANGELOG.md +1266 -1
  2. package/README.md +14 -11
  3. package/dist/css/expressive.css +4215 -1668
  4. package/dist/css/expressive.css.map +1 -1
  5. package/dist/css/expressive.min.css +1 -1
  6. package/dist/css/expressive.min.css.map +1 -1
  7. package/dist/js/expressive.cjs +1231 -1253
  8. package/dist/js/expressive.cjs.map +4 -4
  9. package/dist/js/expressive.js +1231 -1253
  10. package/dist/js/expressive.js.map +4 -4
  11. package/dist/js/expressive.min.js +12 -12
  12. package/dist/js/expressive.min.js.map +4 -4
  13. package/dist/js/expressive.mjs +1231 -1253
  14. package/dist/js/expressive.mjs.map +4 -4
  15. package/dist/types/behaviors/bottomSheets.d.ts +19 -11
  16. package/dist/types/behaviors/sheetDrag.d.ts +40 -0
  17. package/dist/types/behaviors/sideSheets.d.ts +2 -9
  18. package/dist/types/components/autocomplete.d.ts +15 -3
  19. package/dist/types/components/buttons.d.ts +5 -1
  20. package/dist/types/components/carousel.d.ts +87 -53
  21. package/dist/types/components/chips.d.ts +21 -1
  22. package/dist/types/components/expandingCard.d.ts +31 -0
  23. package/dist/types/components/index.d.ts +21 -20
  24. package/dist/types/components/lightbox.d.ts +1 -0
  25. package/dist/types/components/menu.d.ts +7 -1
  26. package/dist/types/components/{sidenav.d.ts → navigationDrawer.d.ts} +16 -16
  27. package/dist/types/components/registry.d.ts +8 -8
  28. package/dist/types/components/slider.d.ts +26 -85
  29. package/dist/types/components/snackbar.d.ts +20 -5
  30. package/dist/types/components/tabs.d.ts +12 -0
  31. package/dist/types/components/timepicker.d.ts +12 -9
  32. package/dist/types/core/utils.d.ts +64 -53
  33. package/dist/types/index.d.ts +8 -9
  34. package/package.json +15 -1
  35. package/src/sass/README.md +61 -11
  36. package/src/sass/abstracts/_breakpoints.scss +17 -19
  37. package/src/sass/abstracts/_mixins.scss +103 -21
  38. package/src/sass/abstracts/_variables.scss +50 -5
  39. package/src/sass/base/_global.scss +6 -24
  40. package/src/sass/base/_grid.scss +15 -12
  41. package/src/sass/base/_normalize.scss +34 -38
  42. package/src/sass/base/_typography.scss +4 -18
  43. package/src/sass/components/_badges.scss +2 -3
  44. package/src/sass/components/_banners.scss +298 -0
  45. package/src/sass/components/_bottom-app-bar.scss +180 -0
  46. package/src/sass/components/_bottom-sheet.scss +12 -4
  47. package/src/sass/components/_button-groups.scss +186 -0
  48. package/src/sass/components/_buttons.scss +245 -92
  49. package/src/sass/components/_cards.scss +494 -73
  50. package/src/sass/components/_carousel.scss +562 -68
  51. package/src/sass/components/_chips.scss +64 -5
  52. package/src/sass/components/_datepicker.scss +24 -24
  53. package/src/sass/components/_dialog.scss +17 -11
  54. package/src/sass/components/_drag-handle.scss +130 -0
  55. package/src/sass/components/_expanding-card.scss +260 -0
  56. package/src/sass/components/_fab-menu.scss +232 -0
  57. package/src/sass/components/_floating-sheet.scss +86 -0
  58. package/src/sass/components/_icon-buttons.scss +194 -0
  59. package/src/sass/components/_icons-material-design.scss +16 -4
  60. package/src/sass/components/_index.scss +22 -6
  61. package/src/sass/components/_list.scss +16 -13
  62. package/src/sass/components/_loading-indicator.scss +106 -0
  63. package/src/sass/components/_menu.scss +335 -61
  64. package/src/sass/components/_navbar.scss +71 -24
  65. package/src/sass/components/_navigation-bar.scss +13 -10
  66. package/src/sass/components/{_sidenav.scss → _navigation-drawer.scss} +123 -33
  67. package/src/sass/components/_navigation-rail.scss +22 -22
  68. package/src/sass/components/_page-footer.scss +19 -16
  69. package/src/sass/components/_pagination.scss +24 -6
  70. package/src/sass/components/_panes.scss +52 -11
  71. package/src/sass/components/{_preloader.scss → _progress.scss} +11 -3
  72. package/src/sass/components/_scrim.scss +37 -0
  73. package/src/sass/components/_search.scss +259 -0
  74. package/src/sass/components/_segmented-buttons.scss +188 -0
  75. package/src/sass/components/_side-sheet.scss +2 -2
  76. package/src/sass/components/_snackbar.scss +20 -15
  77. package/src/sass/components/_split-button.scss +196 -0
  78. package/src/sass/components/_tabs.scss +7 -8
  79. package/src/sass/components/_timepicker.scss +67 -58
  80. package/src/sass/components/_toolbar.scss +206 -80
  81. package/src/sass/components/forms/_checkboxes.scss +49 -12
  82. package/src/sass/components/forms/_fieldset.scss +4 -4
  83. package/src/sass/components/forms/_file-input.scss +8 -1
  84. package/src/sass/components/forms/_index.scss +1 -1
  85. package/src/sass/components/forms/_input-fields.scss +50 -44
  86. package/src/sass/components/forms/_radio-buttons.scss +42 -10
  87. package/src/sass/components/forms/_select.scss +0 -1
  88. package/src/sass/components/forms/{_range.scss → _slider.scss} +8 -9
  89. package/src/sass/components/forms/_switches.scss +6 -7
  90. package/src/sass/tokens/_index.scss +4 -1
  91. package/src/sass/tokens/_reference.scss +70 -61
  92. package/src/sass/tokens/_state.scss +24 -0
  93. package/src/sass/tokens/_theme.scss +6 -3
  94. package/src/sass/tokens/_vibrant.scss +76 -0
  95. package/src/sass/utilities/_colors.scss +14 -1
  96. package/src/sass/utilities/_helpers.scss +5 -5
  97. package/src/sass/utilities/_visibility.scss +48 -26
  98. package/dist/types/behaviors/waves.d.ts +0 -15
  99. package/dist/types/components/parallax.d.ts +0 -16
  100. package/dist/types/components/range.d.ts +0 -43
  101. package/dist/types/core/bounding.d.ts +0 -6
  102. package/dist/types/core/edges.d.ts +0 -6
  103. package/src/sass/components/_parallax.scss +0 -104
  104. package/src/sass/components/_pulse.scss +0 -37
  105. package/src/sass/components/_slider.scss +0 -129
@@ -1,102 +1,43 @@
1
1
  import { Component, BaseOptions, InitElements, InitElement } from '../core/component';
2
2
  export interface SliderOptions extends BaseOptions {
3
- /**
4
- * Set to false to hide slide indicators.
5
- * @default true
6
- */
7
- indicators: boolean;
8
- /**
9
- * Set height of slider.
10
- * @default 400
11
- */
12
- height: number;
13
- /**
14
- * Set the duration of the transition animation in ms.
15
- * @default 500
16
- */
17
- duration: number;
18
- /**
19
- * Set the duration between transitions in ms.
20
- * @default 6000
21
- */
22
- interval: number;
23
- /**
24
- * If slider should pause when keyboard focus is received.
25
- * @default true
26
- */
27
- pauseOnFocus: boolean;
28
- /**
29
- * If slider should pause when is hovered by a pointer.
30
- * @default true
31
- */
32
- pauseOnHover: boolean;
33
- /**
34
- * Optional function used to generate ARIA label to indicators (for accessibility purposes).
35
- * @param index Current index, starting from "1".
36
- * @param current A which indicates whether it is the current element or not
37
- * @returns a string to be used as label indicator.
38
- * @default null
39
- */
40
- indicatorLabelFunc: (index: number, current: boolean) => string;
41
3
  }
42
4
  export declare class Slider extends Component<SliderOptions> {
43
- /** Index of current slide. */
44
- activeIndex: number;
45
- interval: ReturnType<typeof setTimeout>;
46
- eventPause: boolean;
47
- _slider: HTMLUListElement;
48
- _slides: HTMLLIElement[];
49
- _activeSlide: HTMLLIElement;
50
- _indicators: HTMLLIElement[];
51
- _hovered: boolean;
52
- _focused: boolean;
53
- _focusCurrent: boolean;
54
- _sliderId: string;
55
- constructor(el: HTMLElement, options: Partial<SliderOptions>);
5
+ el: HTMLInputElement;
6
+ private _pointerDown;
7
+ value: HTMLElement;
8
+ thumb: HTMLElement;
9
+ constructor(el: HTMLInputElement, options: Partial<SliderOptions>);
56
10
  static get defaults(): SliderOptions;
57
11
  /**
58
12
  * Initializes instance of Slider.
59
13
  * @param el HTML element.
60
14
  * @param options Component options.
61
15
  */
62
- static init(el: HTMLElement, options?: Partial<SliderOptions>): Slider;
16
+ static init(el: HTMLInputElement, options?: Partial<SliderOptions>): Slider;
63
17
  /**
64
18
  * Initializes instances of Slider.
65
19
  * @param els HTML elements.
66
20
  * @param options Component options.
67
21
  */
68
- static init(els: InitElements<InitElement>, options?: Partial<SliderOptions>): Slider[];
69
- static getInstance(el: HTMLElement): Slider;
22
+ static init(els: InitElements<HTMLInputElement | InitElement>, options?: Partial<SliderOptions>): Slider[];
23
+ static getInstance(el: HTMLInputElement): Slider;
70
24
  destroy(): void;
71
- private _setupEventHandlers;
72
- private _removeEventHandlers;
73
- private _handleVisibilityChange;
74
- private _handleIndicatorClick;
75
- private _handleAutoPauseHover;
76
- private _handleAutoPauseFocus;
77
- private _handleAutoStartHover;
78
- private _handleAutoStartFocus;
79
- private _handleInterval;
80
- private _animateSlide;
81
- private _setSliderHeight;
82
- private _setupIndicators;
83
- private _removeIndicators;
84
- set(index: number): void;
85
- _pause(fromEvent: boolean): void;
86
- /**
87
- * Pause slider autoslide.
88
- */
89
- pause: () => void;
90
- /**
91
- * Start slider autoslide.
92
- */
93
- start: () => void;
94
- /**
95
- * Move to next slider.
96
- */
97
- next: () => void;
98
- /**
99
- * Move to prev slider.
100
- */
101
- prev: () => void;
25
+ _setupEventHandlers(): void;
26
+ _removeEventHandlers(): void;
27
+ _handleRangeChange: () => void;
28
+ _handleRangePointerDown: (e: PointerEvent) => void;
29
+ _handleRangeInputOrMove: () => void;
30
+ _handleRangePointerUp: (e: PointerEvent) => void;
31
+ _handleRangeRelease: () => void;
32
+ _setupThumb(): void;
33
+ _removeThumb(): void;
34
+ _isVertical(): boolean;
35
+ _host(): HTMLElement | null;
36
+ _clampDual(): void;
37
+ _fraction(el: HTMLInputElement): number;
38
+ _sync(): void;
39
+ /**
40
+ * Initializes every range input in the current document.
41
+ */
42
+ static Init(): void;
102
43
  }
@@ -58,6 +58,14 @@ export interface SnackbarOptions extends BaseOptions {
58
58
  * @default 0.8
59
59
  */
60
60
  activationPercent: number;
61
+ /**
62
+ * Any element in the tree the snackbar should render into. A Snackbar has no
63
+ * originating element to infer this from, so pass one when the page lives in
64
+ * a shadow root — the container is appended to that root instead of to
65
+ * `document.body`, where a root-scoped stylesheet could not reach it.
66
+ * @default null
67
+ */
68
+ root: HTMLElement;
61
69
  }
62
70
  export declare class Snackbar {
63
71
  /** The snackbar element. */
@@ -86,15 +94,22 @@ export declare class Snackbar {
86
94
  static _snackbars: Snackbar[];
87
95
  static _container: HTMLElement;
88
96
  static _draggedSnackbar: Snackbar;
97
+ /** Which pointer owns the drag in flight; a second finger is ignored. */
98
+ static _dragPointerId: number | null;
89
99
  constructor(options?: Partial<SnackbarOptions>);
90
100
  static get defaults(): SnackbarOptions;
91
101
  static getInstance(el: HTMLElement): Snackbar;
92
- static _createContainer(): void;
102
+ static _createContainer(root?: HTMLElement | ShadowRoot): void;
93
103
  static _removeContainer(): void;
94
- static _onDragStart(e: TouchEvent | MouseEvent): void;
95
- static _onDragMove(e: TouchEvent | MouseEvent): void;
96
- static _onDragEnd(): void;
97
- static _xPos(e: TouchEvent | MouseEvent): number;
104
+ static _onDragStart(e: PointerEvent): void;
105
+ static _onDragMove(e: PointerEvent): void;
106
+ static _onDragEnd(e: PointerEvent): void;
107
+ /**
108
+ * A cancelled pointer is not a short swipe. Snap back and resume the timer
109
+ * without consulting the distance travelled - the browser took the gesture
110
+ * over, so the movement so far says nothing about what the user wanted.
111
+ */
112
+ static _onDragCancel(e: PointerEvent): void;
98
113
  /**
99
114
  * dismiss all snackbars.
100
115
  */
@@ -59,6 +59,18 @@ export declare class Tabs extends Component<TabsOptions> {
59
59
  _queryTabLinks(): NodeListOf<HTMLAnchorElement>;
60
60
  _handleTabClick: (e: MouseEvent) => void;
61
61
  _createIndicator(): void;
62
+ /**
63
+ * Which tab is current, for the eye and for assistive technology.
64
+ *
65
+ * The class alone is a colour. `aria-current` is the part a screen reader
66
+ * reads, and because tabs are navigation rather than a tablist (rule 2 -
67
+ * there is no keyboard model here to back a tablist), "current" is exactly
68
+ * the right word for it. Its *value* changes as the user clicks, which makes
69
+ * maintaining it the component's job, not the author's - see CONTEXT.md on
70
+ * static semantics versus dynamic state. Writing it once in the markup and
71
+ * never moving it left the old tab announcing itself as current.
72
+ */
73
+ private _setActiveTabLink;
62
74
  _setupActiveTabLink(): void;
63
75
  _setupSwipeableTabs(): void;
64
76
  _teardownSwipeableTabs(): void;
@@ -95,10 +95,6 @@ export interface TimepickerOptions extends BaseOptions {
95
95
  */
96
96
  displayPluginOptions: object;
97
97
  }
98
- type Point = {
99
- x: number;
100
- y: number;
101
- };
102
98
  export declare class Timepicker extends Component<TimepickerOptions> {
103
99
  el: HTMLInputElement;
104
100
  id: string;
@@ -110,6 +106,8 @@ export declare class Timepicker extends Component<TimepickerOptions> {
110
106
  x0: number;
111
107
  y0: number;
112
108
  moved: boolean;
109
+ /** Which pointer is dragging the clock hand; a second finger is ignored. */
110
+ private _dragPointerId;
113
111
  dx: number;
114
112
  dy: number;
115
113
  /**
@@ -161,7 +159,6 @@ export declare class Timepicker extends Component<TimepickerOptions> {
161
159
  static init(els: InitElements<HTMLInputElement | InitElement>, options?: Partial<TimepickerOptions>): Timepicker[];
162
160
  static _addLeadingZero(num: number): string;
163
161
  static _createSVGEl(name: string): SVGElement;
164
- static _Pos(e: TouchEvent | MouseEvent): Point;
165
162
  static getInstance(el: HTMLElement): Timepicker;
166
163
  destroy(): void;
167
164
  _setupEventHandlers(): void;
@@ -169,9 +166,16 @@ export declare class Timepicker extends Component<TimepickerOptions> {
169
166
  _handleInputClick: () => void;
170
167
  _handleInputKeydown: (e: KeyboardEvent) => void;
171
168
  _handleTimeInputEnterKey: (e: KeyboardEvent) => void;
172
- _handleClockClickStart: (e: any) => void;
173
- _handleDocumentClickMove: (e: any) => void;
174
- _handleDocumentClickEnd: (e: any) => void;
169
+ _handleClockClickStart: (e: PointerEvent) => void;
170
+ _handleDocumentClickMove: (e: PointerEvent) => void;
171
+ _handleDocumentClickEnd: (e: PointerEvent) => void;
172
+ /**
173
+ * A cancelled pointer is not a completed selection. The browser or the OS
174
+ * took the gesture over, so this only lets go of the drag - it must not run
175
+ * the pointer-up path, which advances hours to minutes, fires `onSelect`,
176
+ * and under `autoSubmit` closes the picker on a time the user never chose.
177
+ */
178
+ _handleDocumentClickCancel: (e: PointerEvent) => void;
175
179
  _insertHTMLIntoDOM(): void;
176
180
  _setupVariables(): void;
177
181
  _pickerSetup(): void;
@@ -210,4 +214,3 @@ export declare class Timepicker extends Component<TimepickerOptions> {
210
214
  cancel: () => void;
211
215
  clear: () => void;
212
216
  }
213
- export {};
@@ -1,55 +1,32 @@
1
- import { Edges } from './edges';
2
- import { Bounding } from './bounding';
1
+ /** The part of a `DOMRect` the positioning helpers below actually read. */
2
+ type Bounding = Pick<DOMRect, 'left' | 'top' | 'width' | 'height'>;
3
+ /** Which edges of a container a box has crossed. */
4
+ type Edges = {
5
+ top: boolean;
6
+ right: boolean;
7
+ bottom: boolean;
8
+ left: boolean;
9
+ };
3
10
  /**
4
11
  * Class with utilitary functions for global usage.
5
12
  */
6
13
  export declare class Utils {
7
- /** Specifies wether tab is pressed or not. */
8
- static tabPressed: boolean;
9
- /** Specifies wether there is a key pressed. */
10
- static keyDown: boolean;
11
14
  /**
12
- * Key maps.
15
+ * Key maps. One `KeyboardEvent.key` value each: the second entries these used
16
+ * to carry (`Esc`, `Up`, `Del`, ...) were IE and Edge Legacy spellings, and
17
+ * the support baseline excludes both.
13
18
  */
14
19
  static keys: {
15
- TAB: string[];
16
- ENTER: string[];
17
- ESC: string[];
18
- BACKSPACE: string[];
19
- ARROW_UP: string[];
20
- ARROW_DOWN: string[];
21
- ARROW_LEFT: string[];
22
- ARROW_RIGHT: string[];
23
- DELETE: string[];
20
+ TAB: string;
21
+ ENTER: string;
22
+ ESC: string;
23
+ BACKSPACE: string;
24
+ ARROW_UP: string;
25
+ ARROW_DOWN: string;
26
+ ARROW_LEFT: string;
27
+ ARROW_RIGHT: string;
28
+ DELETE: string;
24
29
  };
25
- /**
26
- * Keys that count as keyboard navigation, precomputed.
27
- *
28
- * These handlers run at capture phase on the document for every keystroke on
29
- * the page, and used to rebuild three arrays and spread them into a fourth
30
- * on each one.
31
- */
32
- private static _navigationKeys;
33
- /**
34
- * Detects when a key is pressed.
35
- * @param e Event instance.
36
- */
37
- static docHandleKeydown(e: KeyboardEvent): void;
38
- /**
39
- * Detects when a key is released.
40
- * @param e Event instance.
41
- */
42
- static docHandleKeyup(e: KeyboardEvent): void;
43
- /**
44
- * Detects when document is focused.
45
- * @param e Event instance.
46
- */
47
- static docHandleFocus(e: FocusEvent): void;
48
- /**
49
- * Detects when document is not focused.
50
- * @param e Event instance.
51
- */
52
- static docHandleBlur(e: FocusEvent): void;
53
30
  /**
54
31
  * Run `callback` once the document is parsed.
55
32
  *
@@ -60,9 +37,39 @@ export declare class Utils {
60
37
  */
61
38
  static onDocumentReady(callback: () => void): void;
62
39
  /**
63
- * Generates a unique string identifier.
40
+ * Generates a unique string identifier. Every caller prefixes the result and
41
+ * uses it as an element id, and a UUID is valid in that position.
42
+ *
43
+ * `randomUUID` is exposed only in secure contexts, and a dev server on a
44
+ * plain-HTTP LAN address - the usual way a page is opened on a phone - is
45
+ * not one. Without the fallback every component that stamps an id would
46
+ * throw there. The ids only have to be unique within one document, so the
47
+ * fallback does not need to be cryptographic.
64
48
  */
65
49
  static guid(): string;
50
+ /**
51
+ * Where an element created at runtime has to be appended to stay in the same
52
+ * tree as `el`. A stylesheet adopted into a shadow root cannot match a node
53
+ * in the document, so a portal that lands on `document.body` is unstyled
54
+ * whenever its origin lives in a shadow root (adr/0002).
55
+ *
56
+ * Light-DOM elements still get `document.body`, which is what escapes an
57
+ * ancestor's `overflow: hidden` and stacking context. Inside a shadow root
58
+ * both hazards come back: the host's own ancestors still form the containing
59
+ * block, so a transformed ancestor re-anchors `position: fixed` and a
60
+ * positioned one re-anchors `position: absolute`.
61
+ */
62
+ static portalRoot(el: Node): HTMLElement | ShadowRoot;
63
+ /**
64
+ * `getElementById` against the tree `el` lives in. The mirror of
65
+ * `portalRoot`: an id in a shadow root is invisible to
66
+ * `document.getElementById`, so a trigger and its target inside the same
67
+ * root could not find each other at all.
68
+ *
69
+ * Falls back to the document for a detached `el`, whose root is a plain
70
+ * element with no lookup of its own.
71
+ */
72
+ static getElementById(el: Node, id: string): HTMLElement;
66
73
  /**
67
74
  * Checks for exceeded edges
68
75
  * @param container Container element.
@@ -101,15 +108,18 @@ export declare class Utils {
101
108
  */
102
109
  static getDocumentScrollLeft(): number;
103
110
  /**
104
- * Fires the given function after a certain ammount of time.
105
- * @param func Function to be fired.
106
- * @param wait Wait time.
107
- * @param options Additional options.
111
+ * Trailing-edge throttle: run `func` now, then at most once per `wait` ms
112
+ * with the most recent arguments.
113
+ *
114
+ * This used to carry its underscore ancestor's whole configuration - leading
115
+ * and trailing switches, a result passthrough - and both call sites are
116
+ * resize handlers passing nothing but a function and a delay.
117
+ *
118
+ * Assign the return value once. `x = Utils.throttle(fn, 200)` is right;
119
+ * wrapping it in an arrow builds a fresh closure per event and never calls
120
+ * it, which is how resize handling was dead in three components.
108
121
  */
109
- static throttle(func: (...args: unknown[]) => void, wait: number, options?: Partial<{
110
- leading: boolean;
111
- trailing: boolean;
112
- }>): (...args: unknown[]) => any;
122
+ static throttle(func: (...args: unknown[]) => void, wait: number): (...args: unknown[]) => void;
113
123
  /**
114
124
  * Renders confirm/close buttons with callback function
115
125
  */
@@ -127,3 +137,4 @@ export declare class Utils {
127
137
  y: number;
128
138
  };
129
139
  }
140
+ export {};
@@ -1,9 +1,8 @@
1
- export * from './components';
2
- export { Dialogs } from './behaviors/dialogs';
3
- export { BottomSheets } from './behaviors/bottomSheets';
4
- export { SideSheets } from './behaviors/sideSheets';
5
- export { Forms } from './behaviors/forms';
6
- export { Waves } from './behaviors/waves';
7
- export { AutoInit } from './components/registry';
8
- export type { AutoInitOptions } from './components/registry';
9
- export declare const version = "0.7.0";
1
+ export * from "./components";
2
+ export { Dialogs } from "./behaviors/dialogs";
3
+ export { BottomSheets } from "./behaviors/bottomSheets";
4
+ export { SideSheets } from "./behaviors/sideSheets";
5
+ export { Forms } from "./behaviors/forms";
6
+ export { AutoInit } from "./components/registry";
7
+ export type { AutoInitOptions } from "./components/registry";
8
+ export declare const version = "0.8.0";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@expressivecss/expressive",
3
- "version": "0.7.0",
3
+ "version": "0.8.0",
4
4
  "description": "ExpressiveCSS - a Material Design 3 front-end framework.",
5
5
  "license": "MIT",
6
6
  "author": "BaezFJ <baezdevs@gmail.com>",
@@ -69,10 +69,20 @@
69
69
  "build:js:iife": "esbuild src/ts/index.ts --bundle --format=iife --global-name=Expressive --target=es2020 --outfile=dist/js/expressive.js --sourcemap",
70
70
  "build:js:iife-min": "esbuild src/ts/index.ts --bundle --format=iife --global-name=Expressive --target=es2020 --outfile=dist/js/expressive.min.js --minify --sourcemap",
71
71
  "build:types": "tsc -p tsconfig.build.json",
72
+ "build:semantics": "node scripts/gen-semantics.mjs",
73
+ "docs:dev": "run-s build docs:watch",
74
+ "docs:watch": "run-p watch:css watch:js docs:server",
75
+ "docs:server": "astro dev --root docs",
76
+ "docs:build": "run-s build docs:build:astro docs:verify",
77
+ "docs:build:astro": "astro build --root docs",
78
+ "docs:preview": "astro preview --root docs",
79
+ "docs:verify": "node scripts/verify-site.mjs",
72
80
  "typecheck": "tsc -p tsconfig.json --noEmit",
73
81
  "test": "run-s build:js build:css test:run",
74
82
  "test:run": "node --test",
75
83
  "test:watch": "node --test --watch",
84
+ "test:visual": "node visual/run.mjs",
85
+ "test:visual:report": "playwright show-report visual/report",
76
86
  "watch": "run-p watch:css watch:js",
77
87
  "watch:css": "sass --watch src/sass/expressive.scss:dist/css/expressive.css --style=expanded --source-map --load-path=src/sass",
78
88
  "watch:js": "esbuild src/ts/index.ts --bundle --format=iife --global-name=Expressive --target=es2020 --outfile=dist/js/expressive.js --sourcemap --watch=forever"
@@ -87,10 +97,14 @@
87
97
  "not dead"
88
98
  ],
89
99
  "devDependencies": {
100
+ "@playwright/test": "^1.56.0",
101
+ "astro": "^7.2.8",
90
102
  "esbuild": "^0.28.2",
91
103
  "jsdom": "^30.0.1",
92
104
  "npm-run-all2": "^9.0.3",
93
105
  "sass": "^1.102.0",
106
+ "turndown": "^7.2.4",
107
+ "turndown-plugin-gfm": "^1.0.2",
94
108
  "typescript": "^7.0.2"
95
109
  }
96
110
  }
@@ -65,21 +65,26 @@ fine — it crosses no boundary.
65
65
 
66
66
  ## Breakpoints
67
67
 
68
- Four of them, in `abstracts/_breakpoints.scss`, reached through mixins:
68
+ The five Material 3 window size classes live in
69
+ `abstracts/_breakpoints.scss` and are reached through mixins:
69
70
 
70
71
  ```scss
71
- @include bp-up("small") { } // @media (width >= 601px)
72
- @include bp-down("large") { } // @media (width < 993px)
73
- @include bp-between("small", "large") { }
72
+ @include bp-up("medium") { } // width >= 600px
73
+ @include bp-down("expanded") { } // width < 840px
74
+ @include bp-between("expanded", "large") { } // 840px–1199px
74
75
  ```
75
76
 
76
- Names are `small` (601px), `large` (993px), `xlarge` (1201px), `xxlarge`
77
- (1601px) — the boundary a query sits on, not the size of the screen it
78
- targets. `xxlarge` is M3 extra-large (desktop / widescreen); the 12-column
79
- grid grows a `.xxl` prefix there, and `.container` lifts its 1280px cap to
80
- 1920px. Range syntax has an exclusive comparator, so the old `600.99px` /
81
- `992.99px` values used to stop `max-width` and `min-width` both matching on
82
- the boundary pixel are gone, and each boundary is written once.
77
+ | M3 class | Width | Grid prefix |
78
+ | --- | --- | --- |
79
+ | Compact | `< 600px` | `.s` (default) |
80
+ | Medium | `600–839px` | `.m` |
81
+ | Expanded | `840–1199px` | `.l` |
82
+ | Large | `1200–1599px` | `.xl` |
83
+ | Extra-large | `>= 1600px` | `.xxl` |
84
+
85
+ The grid prefixes remain unchanged for markup compatibility. At Extra-large,
86
+ `.container` lifts its 1280px cap to 1920px. Range syntax uses exclusive
87
+ comparators, so each boundary is written once without fractional max-widths.
83
88
 
84
89
  ## Theming
85
90
 
@@ -126,6 +131,51 @@ only has to set `color-scheme: dark` and all 30 tokens re-resolve — and settin
126
131
  `color-scheme` on any subtree re-themes just that subtree. `color-scheme` is
127
132
  load-bearing now; changing it is not cosmetic.
128
133
 
134
+ ## Shadow roots
135
+
136
+ The sheet is supported as a shadow root's **only** stylesheet — adopted into
137
+ `shadowRoot.adoptedStyleSheets` with no copy in the document. So a rule that
138
+ declares a token pairs its anchor with the `:host` twin that reaches the same
139
+ elements from inside a shadow tree:
140
+
141
+ ```scss
142
+ // no - :root matches the document element and nothing else, so this token
143
+ // is simply absent in a shadow-only load, and every declaration reading it
144
+ // is then invalid at computed-value time
145
+ :root { --md-comp-nav-drawer-width: 300px; }
146
+
147
+ // yes
148
+ :root, :host { --md-comp-nav-drawer-width: 300px; }
149
+ :root[theme='dark'], :host([theme='dark']) { color-scheme: dark; }
150
+ [vibrant], :host([vibrant]) { … }
151
+ ```
152
+
153
+ An anchor is anything that can only match a document root or a descendant of
154
+ one, which is `html` and `body` as well: a shadow root has neither inside it.
155
+ `base/_grid.scss` put `--gap-size` on `body` and `.row` reads it without a
156
+ fallback, so a `.row` in a shadow-only load had an invalid `gap` rather than a
157
+ default one.
158
+
159
+ `color-scheme` counts as a token here: `light-dark()` resolves against the
160
+ element's used `color-scheme`, so an unpaired `:root[theme='dark']` means a
161
+ shadow tree cannot be pinned to a theme the way a page can.
162
+
163
+ `:host` matches nothing in a document, so pairing an anchor cannot change a
164
+ normal load. It is not weightless, though: bare `:host` matches `:root` at
165
+ (0,1,0), but `:host(<compound>)` adds its argument's weight on top, so
166
+ `:host([vibrant])` is (0,2,0) against `[vibrant]`'s (0,1,0). The two arms match
167
+ disjoint elements and never compete, so nothing in the sheet turns on it — but
168
+ do not reach for the pairing as a way to keep specificity flat.
169
+
170
+ `tests/shadow-dom.test.js` fails any rule that skips the pairing; the commitment
171
+ behind it is `adr/0002-shadow-only-stylesheet-adoption.md`. That commitment
172
+ covers everything *inside* the shadow root, portals included: the six places that
173
+ create an element at runtime (Tooltip, Snackbar, Lightbox's caption, the drawer's
174
+ drag target, and the Datepicker's two menus) route through `Utils.portalRoot()`
175
+ and append into the originating shadow root, so no document-level copy is needed.
176
+ The cost is that they no longer escape an ancestor's `overflow` or stacking
177
+ context — the ADR states what that re-anchors.
178
+
129
179
  ## Translucent colors
130
180
 
131
181
  ```scss
@@ -1,38 +1,36 @@
1
1
  @use "sass:map";
2
2
  @use "sass:list";
3
3
 
4
- // Breakpoints, as media-query range syntax.
4
+ // Material 3 window size classes, expressed as media-query boundaries.
5
5
  //
6
6
  // The old form built a query by interpolating a whole string:
7
7
  //
8
8
  // $medium-and-down: "only screen and (max-width : 992.99px)";
9
9
  // @media #{$medium-and-down} { ... }
10
10
  //
11
- // which needed the `.99` because `max-width: 993px` and `min-width: 993px`
12
- // both match at exactly 993px. Range syntax has an exclusive comparator, so
13
- // the fractional values are gone and the boundary is stated once:
11
+ // which needed the `.99` because inclusive max/min queries both match on the
12
+ // boundary pixel. Range syntax has an exclusive comparator, so fractional
13
+ // values are unnecessary and every boundary is stated once:
14
14
  //
15
- // @include bp-down("large") { ... } -> @media (width < 993px)
15
+ // @include bp-down("expanded") { ... } -> @media (width < 840px)
16
16
  //
17
17
  // `only screen` went with them: it existed to hide queries from Netscape 4
18
18
  // and IE, and `screen` alone excludes print anyway - which these rules never
19
19
  // wanted to do, since a hidden element should stay hidden on paper.
20
20
 
21
- // Boundaries, not the size of the screen they target. Column prefixes
22
- // s / m / l / xl / xxl sit on these in order; there is no `medium` key
23
- // because the tablet class is `m` and the mixin name is the boundary.
24
- //
25
- // M3 window-size classes, for the ones that line up:
26
- // compact width < 600 below "small"
27
- // medium 600 ≤ width < 840 (we do not split 840 out of "small")
28
- // expanded 840 ≤ width < 1200 "large" is 993, close enough
29
- // large 1200 ≤ width < 1600 "xlarge"
30
- // extra-large width ≥ 1600 "xxlarge"
21
+ // Compact starts at zero and is the unwrapped/default style. The remaining
22
+ // entries are the exact lower bounds from the M3 adaptive-layout spec:
23
+ // compact 0 ≤ width < 600
24
+ // medium 600 ≤ width < 840
25
+ // expanded 840 ≤ width < 1200
26
+ // large 1200 ≤ width < 1600
27
+ // extra-large width ≥ 1600
31
28
  $breakpoints: (
32
- "small": 601px, // phone -> tablet
33
- "large": 993px, // tablet -> desktop
34
- "xlarge": 1201px, // desktop -> large desktop
35
- "xxlarge": 1601px, // large -> extra-large / widescreen
29
+ "compact": 0px,
30
+ "medium": 600px,
31
+ "expanded": 840px,
32
+ "large": 1200px,
33
+ "extra-large": 1600px,
36
34
  ) !default;
37
35
 
38
36
  @function breakpoint($name) {