@expressivecss/expressive 0.1.0-alpha.3 → 0.2.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 (154) hide show
  1. package/LICENSE +82 -2
  2. package/README.md +153 -43
  3. package/dist/css/expressive.css +7121 -9658
  4. package/dist/css/expressive.css.map +1 -0
  5. package/dist/css/expressive.min.css +1 -1
  6. package/dist/css/expressive.min.css.map +1 -0
  7. package/dist/js/expressive.cjs +7521 -0
  8. package/dist/js/expressive.cjs.map +7 -0
  9. package/dist/js/expressive.js +7383 -8099
  10. package/dist/js/expressive.js.map +7 -1
  11. package/dist/js/expressive.min.js +75 -1
  12. package/dist/js/expressive.min.js.map +7 -1
  13. package/dist/js/expressive.mjs +7292 -7998
  14. package/dist/js/expressive.mjs.map +7 -1
  15. package/dist/types/behaviors/dialogs.d.ts +25 -0
  16. package/dist/{js → types}/components/autocomplete.d.ts +10 -1
  17. package/dist/{js → types}/components/buttons.d.ts +11 -1
  18. package/dist/{js → types}/components/cards.d.ts +10 -1
  19. package/dist/{js → types}/components/carousel.d.ts +4 -5
  20. package/dist/{js/forms/character-counter.d.ts → types/components/characterCounter.d.ts} +1 -1
  21. package/dist/{js → types}/components/chips.d.ts +1 -1
  22. package/dist/{js → types}/components/collapsible.d.ts +1 -1
  23. package/dist/{js → types}/components/datepicker.d.ts +63 -2
  24. package/dist/{js → types}/components/dropdown.d.ts +3 -3
  25. package/dist/types/components/index.d.ts +23 -0
  26. package/dist/{js/components/materialbox.d.ts → types/components/lightbox.d.ts} +21 -21
  27. package/dist/{js → types}/components/modal.d.ts +1 -1
  28. package/dist/{js → types}/components/parallax.d.ts +11 -5
  29. package/dist/{js → types}/components/pushpin.d.ts +1 -1
  30. package/dist/{js/forms → types/components}/range.d.ts +3 -3
  31. package/dist/types/components/registry.d.ts +107 -0
  32. package/dist/{js → types}/components/scrollspy.d.ts +6 -9
  33. package/dist/{js/forms → types/components}/select.d.ts +11 -2
  34. package/dist/{js → types}/components/sidenav.d.ts +1 -1
  35. package/dist/{js → types}/components/slider.d.ts +3 -2
  36. package/dist/{js → types}/components/tabs.d.ts +3 -2
  37. package/dist/{js/components/tap-target.d.ts → types/components/tapTarget.d.ts} +2 -2
  38. package/dist/{js → types}/components/timepicker.d.ts +13 -5
  39. package/dist/{js/components/toast.d.ts → types/components/toasts.d.ts} +28 -4
  40. package/dist/{js → types}/components/tooltip.d.ts +2 -2
  41. package/dist/types/core/bounding.d.ts +6 -0
  42. package/dist/{js/foundation → types/core}/component.d.ts +1 -1
  43. package/dist/types/core/edges.d.ts +6 -0
  44. package/dist/{js/foundation → types/core}/utils.d.ts +21 -14
  45. package/dist/types/index.d.ts +7 -0
  46. package/dist/{js/plugins/docked-display.d.ts → types/plugins/dockedDisplayPlugin.d.ts} +7 -0
  47. package/package.json +60 -36
  48. package/src/sass/README.md +200 -0
  49. package/src/sass/abstracts/_breakpoints.scss +69 -0
  50. package/src/sass/abstracts/_elevation.scss +34 -0
  51. package/src/sass/abstracts/_index.scss +7 -0
  52. package/src/sass/abstracts/_mixins.scss +171 -0
  53. package/src/sass/abstracts/_variables.scss +57 -0
  54. package/src/{scss/layout/_table.scss → sass/base/_global.scss} +64 -2
  55. package/src/{scss/layout → sass/base}/_grid.scss +6 -6
  56. package/src/sass/base/_index.scss +6 -0
  57. package/src/{scss/foundation → sass/base}/_normalize.scss +51 -139
  58. package/src/sass/base/_typography.scss +90 -0
  59. package/src/sass/components/_badges.scss +77 -0
  60. package/src/{scss → sass}/components/_breadcrumb.scss +2 -0
  61. package/src/sass/components/_buttons.scss +407 -0
  62. package/src/sass/components/_cards.scss +328 -0
  63. package/src/{scss → sass}/components/_carousel.scss +5 -3
  64. package/src/sass/components/_chips.scss +140 -0
  65. package/src/sass/components/_collapsible.scss +81 -0
  66. package/src/{scss → sass}/components/_collection.scss +2 -2
  67. package/src/{scss/components/_date-picker.scss → sass/components/_datepicker.scss} +51 -44
  68. package/src/sass/components/_docked-display.scss +11 -0
  69. package/src/sass/components/_dropdown.scss +129 -0
  70. package/src/{scss/components/_material-icons.scss → sass/components/_icons-material-design.scss} +2 -0
  71. package/src/sass/components/_index.scss +35 -0
  72. package/src/{scss/components/_material-box.scss → sass/components/_lightbox.scss} +6 -4
  73. package/src/sass/components/_modal.scss +208 -0
  74. package/src/sass/components/_navbar.scss +345 -0
  75. package/src/{scss/layout/_footer.scss → sass/components/_page-footer.scss} +7 -6
  76. package/src/{scss → sass}/components/_pagination.scss +4 -3
  77. package/src/{scss → sass}/components/_parallax.scss +4 -1
  78. package/src/{scss → sass}/components/_preloader.scss +19 -97
  79. package/src/{scss → sass}/components/_pulse.scss +2 -0
  80. package/src/sass/components/_pushpin.scss +11 -0
  81. package/src/{scss → sass}/components/_sidenav.scss +99 -82
  82. package/src/{scss → sass}/components/_slider.scss +3 -1
  83. package/src/{scss/components/_table-of-contents.scss → sass/components/_table_of_contents.scss} +2 -0
  84. package/src/sass/components/_tabs.scss +232 -0
  85. package/src/{scss/components/_tap-target.scss → sass/components/_tapTarget.scss} +24 -22
  86. package/src/{scss/components/_time-picker.scss → sass/components/_timepicker.scss} +56 -56
  87. package/src/sass/components/_toast.scss +260 -0
  88. package/src/sass/components/_toolbar.scss +192 -0
  89. package/src/sass/components/_tooltip.scss +166 -0
  90. package/src/{scss → sass}/components/_transitions.scss +4 -4
  91. package/src/sass/components/forms/_checkboxes.scss +288 -0
  92. package/src/sass/components/forms/_fieldset.scss +128 -0
  93. package/src/{scss → sass/components}/forms/_file-input.scss +11 -9
  94. package/src/{scss → sass/components}/forms/_index.scss +2 -0
  95. package/src/sass/components/forms/_input-fields.scss +326 -0
  96. package/src/sass/components/forms/_radio-buttons.scss +190 -0
  97. package/src/sass/components/forms/_range.scss +310 -0
  98. package/src/sass/components/forms/_select.scss +103 -0
  99. package/src/sass/components/forms/_switches.scss +239 -0
  100. package/src/sass/expressive.scss +60 -0
  101. package/src/sass/tokens/_index.scss +4 -0
  102. package/src/sass/tokens/_reference.scss +265 -0
  103. package/src/sass/tokens/_theme.scss +49 -0
  104. package/src/sass/utilities/_helpers.scss +63 -0
  105. package/src/sass/utilities/_index.scss +8 -0
  106. package/src/{scss → sass}/utilities/_spacing.scss +4 -3
  107. package/src/sass/utilities/_typescale.scss +45 -0
  108. package/src/sass/utilities/_visibility.scss +63 -0
  109. package/src/sass/utilities/_z-depth.scss +18 -0
  110. package/dist/js/components/index.d.ts +0 -21
  111. package/dist/js/forms/index.d.ts +0 -4
  112. package/dist/js/foundation/index.d.ts +0 -2
  113. package/dist/js/index.d.ts +0 -52
  114. package/dist/js/plugins/index.d.ts +0 -1
  115. package/src/scss/components/_appbar.scss +0 -454
  116. package/src/scss/components/_badges.scss +0 -77
  117. package/src/scss/components/_buttons.scss +0 -412
  118. package/src/scss/components/_cards.scss +0 -267
  119. package/src/scss/components/_chips.scss +0 -147
  120. package/src/scss/components/_collapsible.scss +0 -84
  121. package/src/scss/components/_dropdown.scss +0 -98
  122. package/src/scss/components/_index.scss +0 -28
  123. package/src/scss/components/_modal.scss +0 -84
  124. package/src/scss/components/_tabs.scss +0 -176
  125. package/src/scss/components/_toast.scss +0 -70
  126. package/src/scss/components/_tooltip.scss +0 -49
  127. package/src/scss/components/_video.scss +0 -15
  128. package/src/scss/expressive.scss +0 -7
  129. package/src/scss/forms/_checkboxes.scss +0 -199
  130. package/src/scss/forms/_input-fields.scss +0 -360
  131. package/src/scss/forms/_radio-buttons.scss +0 -114
  132. package/src/scss/forms/_range.scss +0 -148
  133. package/src/scss/forms/_select.scss +0 -108
  134. package/src/scss/forms/_switches.scss +0 -127
  135. package/src/scss/foundation/_base.scss +0 -71
  136. package/src/scss/foundation/_color-palette.scss +0 -402
  137. package/src/scss/foundation/_index.scss +0 -10
  138. package/src/scss/foundation/_mixins.scss +0 -198
  139. package/src/scss/foundation/_motion.scss +0 -70
  140. package/src/scss/foundation/_shape.scss +0 -30
  141. package/src/scss/foundation/_state-layers.scss +0 -68
  142. package/src/scss/foundation/_theme.scss +0 -238
  143. package/src/scss/foundation/_tokens.scss +0 -511
  144. package/src/scss/foundation/_variables.scss +0 -57
  145. package/src/scss/layout/_index.scss +0 -3
  146. package/src/scss/utilities/_a11y.scss +0 -77
  147. package/src/scss/utilities/_elevation.scss +0 -63
  148. package/src/scss/utilities/_helpers.scss +0 -50
  149. package/src/scss/utilities/_index.scss +0 -7
  150. package/src/scss/utilities/_responsive.scss +0 -62
  151. package/src/scss/utilities/_typography.scss +0 -115
  152. /package/dist/{js/forms → types/behaviors}/forms.d.ts +0 -0
  153. /package/dist/{js/components → types/behaviors}/waves.d.ts +0 -0
  154. /package/src/{scss → sass}/utilities/_colors.scss +0 -0
@@ -1,4 +1,4 @@
1
- import { Component, BaseOptions, InitElements, MElement } from '../foundation/component';
1
+ import { Component, BaseOptions, InitElements, MElement } from '../core/component';
2
2
  export interface SliderOptions extends BaseOptions {
3
3
  /**
4
4
  * Set to false to hide slide indicators.
@@ -42,7 +42,7 @@ export interface SliderOptions extends BaseOptions {
42
42
  export declare class Slider extends Component<SliderOptions> {
43
43
  /** Index of current slide. */
44
44
  activeIndex: number;
45
- interval: string | number | NodeJS.Timeout;
45
+ interval: ReturnType<typeof setTimeout>;
46
46
  eventPause: boolean;
47
47
  _slider: HTMLUListElement;
48
48
  _slides: HTMLLIElement[];
@@ -70,6 +70,7 @@ export declare class Slider extends Component<SliderOptions> {
70
70
  destroy(): void;
71
71
  private _setupEventHandlers;
72
72
  private _removeEventHandlers;
73
+ private _handleVisibilityChange;
73
74
  private _handleIndicatorClick;
74
75
  private _handleAutoPauseHover;
75
76
  private _handleAutoPauseFocus;
@@ -1,5 +1,5 @@
1
1
  import { Carousel } from './carousel';
2
- import { Component, BaseOptions, InitElements, MElement } from '../foundation/component';
2
+ import { Component, BaseOptions, InitElements, MElement } from '../core/component';
3
3
  export interface TabsOptions extends BaseOptions {
4
4
  /**
5
5
  * Transition duration in milliseconds.
@@ -27,7 +27,7 @@ export interface TabsOptions extends BaseOptions {
27
27
  export declare class Tabs extends Component<TabsOptions> {
28
28
  _tabLinks: NodeListOf<HTMLAnchorElement>;
29
29
  _index: number;
30
- _indicator: HTMLLIElement;
30
+ _indicator: HTMLElement;
31
31
  _tabWidth: number;
32
32
  _tabsWidth: number;
33
33
  _tabsCarousel: Carousel;
@@ -56,6 +56,7 @@ export declare class Tabs extends Component<TabsOptions> {
56
56
  _setupEventHandlers(): void;
57
57
  _removeEventHandlers(): void;
58
58
  _handleWindowResize: () => void;
59
+ _queryTabLinks(): NodeListOf<HTMLAnchorElement>;
59
60
  _handleTabClick: (e: MouseEvent) => void;
60
61
  _createIndicator(): void;
61
62
  _setupActiveTabLink(): void;
@@ -1,4 +1,4 @@
1
- import { Component, BaseOptions, InitElements, MElement, Openable } from '../foundation/component';
1
+ import { Component, BaseOptions, InitElements, MElement, Openable } from '../core/component';
2
2
  export interface TapTargetOptions extends BaseOptions {
3
3
  /**
4
4
  * Callback function called when Tap Target is opened.
@@ -39,7 +39,7 @@ export declare class TapTarget extends Component<TapTargetOptions> implements Op
39
39
  destroy(): void;
40
40
  _setupEventHandlers(): void;
41
41
  _removeEventHandlers(): void;
42
- _handleThrottledResize: () => void;
42
+ _handleThrottledResize: (...args: unknown[]) => any;
43
43
  _handleKeyboardInteraction: (e: KeyboardEvent) => void;
44
44
  _handleTargetToggle: () => void;
45
45
  _handleResize: () => void;
@@ -1,4 +1,4 @@
1
- import { Component, BaseOptions, InitElements, MElement, I18nOptions } from '../foundation/component';
1
+ import { Component, BaseOptions, InitElements, MElement, I18nOptions } from '../core/component';
2
2
  export type Views = 'hours' | 'minutes';
3
3
  export interface TimepickerOptions extends BaseOptions {
4
4
  /**
@@ -129,8 +129,8 @@ export declare class Timepicker extends Component<TimepickerOptions> {
129
129
  */
130
130
  amOrPm: 'AM' | 'PM';
131
131
  static _template: any;
132
- /** Vibrate device when dragging clock hand. */
133
- vibrate: 'vibrate' | 'webkitVibrate' | null;
132
+ /** Whether this device exposes the Vibration API at all. */
133
+ canVibrate: boolean;
134
134
  _canvas: HTMLElement;
135
135
  hoursView: HTMLElement;
136
136
  spanAmPm: HTMLSpanElement;
@@ -140,9 +140,11 @@ export declare class Timepicker extends Component<TimepickerOptions> {
140
140
  bg: Element;
141
141
  bearing: Element;
142
142
  g: Element;
143
- toggleViewTimer: string | number | NodeJS.Timeout;
144
- vibrateTimer: NodeJS.Timeout | number;
143
+ toggleViewTimer: ReturnType<typeof setTimeout>;
144
+ vibrateTimer: ReturnType<typeof setTimeout>;
145
145
  private displayPlugin;
146
+ /** Whether the dial (ticks + SVG + AM/PM buttons) has been built yet. */
147
+ private _clockBuilt;
146
148
  constructor(el: HTMLInputElement, options: Partial<TimepickerOptions>);
147
149
  static get defaults(): TimepickerOptions;
148
150
  /**
@@ -173,6 +175,12 @@ export declare class Timepicker extends Component<TimepickerOptions> {
173
175
  _insertHTMLIntoDOM(): void;
174
176
  _setupVariables(): void;
175
177
  _pickerSetup(): void;
178
+ /**
179
+ * Build the dial, once. Cheap to call repeatedly - every entry point that
180
+ * touches the clock calls it first, so the picker cannot be interacted with
181
+ * before it exists.
182
+ */
183
+ private _ensureClockBuilt;
176
184
  _clockSetup(): void;
177
185
  _buildSVGClock(): void;
178
186
  _buildHoursView(): void;
@@ -1,10 +1,25 @@
1
- import { BaseOptions } from '../foundation/component';
1
+ import { BaseOptions } from '../core/component';
2
2
  export interface ToastOptions extends BaseOptions {
3
3
  /**
4
4
  * The content of the Toast.
5
5
  * @default ""
6
6
  */
7
7
  text: string;
8
+ /**
9
+ * Optional action label. Rendered as a trailing text button.
10
+ * @default ""
11
+ */
12
+ action: string;
13
+ /**
14
+ * Called when the action button is pressed. The toast still dismisses.
15
+ * @default null
16
+ */
17
+ onAction: () => void;
18
+ /**
19
+ * Show a trailing close icon button.
20
+ * @default false
21
+ */
22
+ dismissible: boolean;
8
23
  /**
9
24
  * Element Id for the tooltip.
10
25
  * @default ""
@@ -56,7 +71,9 @@ export declare class Toast {
56
71
  panning: boolean;
57
72
  options: ToastOptions;
58
73
  message: string;
59
- counterInterval: NodeJS.Timeout | number;
74
+ counterTimeout: ReturnType<typeof setTimeout>;
75
+ /** Wall-clock time the toast is due to dismiss, for pause/resume. */
76
+ private _deadline;
60
77
  wasSwiped: boolean;
61
78
  startingXPos: number;
62
79
  xPos: number;
@@ -82,10 +99,17 @@ export declare class Toast {
82
99
  _createToast(): HTMLElement;
83
100
  _animateIn(): void;
84
101
  /**
85
- * Create setInterval which automatically removes toast when timeRemaining >= 0
86
- * has been reached.
102
+ * Arm the dismissal timer.
103
+ *
104
+ * One timeout, not a 20ms interval ticking a counter down: the old form woke
105
+ * the page up 50 times a second per toast purely to subtract 20 from a
106
+ * number. Dragging pauses the countdown via _pauseTimer/_resumeTimer instead.
87
107
  */
88
108
  _setTimer(): void;
109
+ /** Stop the countdown, banking the time that was left on it. */
110
+ _pauseTimer(): void;
111
+ /** Restart a countdown stopped by {@link _pauseTimer}. */
112
+ _resumeTimer(): void;
89
113
  /**
90
114
  * Dismiss toast with animation.
91
115
  */
@@ -1,4 +1,4 @@
1
- import { Component, BaseOptions, InitElements, MElement } from '../foundation/component';
1
+ import { Component, BaseOptions, InitElements, MElement } from '../core/component';
2
2
  export type TooltipPosition = 'top' | 'right' | 'bottom' | 'left';
3
3
  export interface TooltipOptions extends BaseOptions {
4
4
  /**
@@ -24,7 +24,7 @@ export interface TooltipOptions extends BaseOptions {
24
24
  /**
25
25
  * Set distance tooltip appears away from its activator
26
26
  * excluding transitionMovement.
27
- * @default 5
27
+ * @default 4
28
28
  */
29
29
  margin: number;
30
30
  /**
@@ -0,0 +1,6 @@
1
+ export declare class Bounding {
2
+ left: number;
3
+ top: number;
4
+ width: number;
5
+ height: number;
6
+ }
@@ -20,7 +20,7 @@ export interface Openable {
20
20
  close(): void;
21
21
  }
22
22
  /**
23
- * Base class implementation for Materialize components.
23
+ * Base class implementation for Expressive components.
24
24
  */
25
25
  export declare class Component<O extends BaseOptions> {
26
26
  /**
@@ -0,0 +1,6 @@
1
+ export declare class Edges {
2
+ top: boolean;
3
+ right: boolean;
4
+ bottom: boolean;
5
+ left: boolean;
6
+ }
@@ -1,15 +1,5 @@
1
- export interface Edges {
2
- top: boolean;
3
- right: boolean;
4
- bottom: boolean;
5
- left: boolean;
6
- }
7
- export interface Bounding {
8
- left: number;
9
- top: number;
10
- width: number;
11
- height: number;
12
- }
1
+ import { Edges } from './edges';
2
+ import { Bounding } from './bounding';
13
3
  /**
14
4
  * Class with utilitary functions for global usage.
15
5
  */
@@ -32,6 +22,14 @@ export declare class Utils {
32
22
  ARROW_RIGHT: string[];
33
23
  DELETE: string[];
34
24
  };
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;
35
33
  /**
36
34
  * Detects when a key is pressed.
37
35
  * @param e Event instance.
@@ -52,6 +50,15 @@ export declare class Utils {
52
50
  * @param e Event instance.
53
51
  */
54
52
  static docHandleBlur(e: FocusEvent): void;
53
+ /**
54
+ * Run `callback` once the document is parsed.
55
+ *
56
+ * The document-level behaviors all used a bare DOMContentLoaded listener,
57
+ * which never fires if the bundle is loaded after that event - an async or
58
+ * deferred script tag, or a dynamic import. Checking readyState first covers
59
+ * both orders.
60
+ */
61
+ static onDocumentReady(callback: () => void): void;
55
62
  /**
56
63
  * Generates a unique string identifier.
57
64
  */
@@ -99,10 +106,10 @@ export declare class Utils {
99
106
  * @param wait Wait time.
100
107
  * @param options Additional options.
101
108
  */
102
- static throttle(func: (Function: object) => void, wait: number, options?: Partial<{
109
+ static throttle(func: (...args: unknown[]) => void, wait: number, options?: Partial<{
103
110
  leading: boolean;
104
111
  trailing: boolean;
105
- }>): (...args: any[]) => any;
112
+ }>): (...args: unknown[]) => any;
106
113
  /**
107
114
  * Renders confirm/close buttons with callback function
108
115
  */
@@ -0,0 +1,7 @@
1
+ export * from './components';
2
+ export { Dialogs } from './behaviors/dialogs';
3
+ export { Forms } from './behaviors/forms';
4
+ export { Waves } from './behaviors/waves';
5
+ export { AutoInit } from './components/registry';
6
+ export type { AutoInitOptions } from './components/registry';
7
+ export declare const version = "0.2.0";
@@ -22,6 +22,13 @@ export declare class DockedDisplayPlugin {
22
22
  private options;
23
23
  private visible;
24
24
  constructor(el: HTMLElement, container: HTMLElement, options: Partial<DockedDisplayPluginOptions>);
25
+ private _handleDocumentClick;
26
+ /**
27
+ * Tears the plugin down. The document listener holds a reference to this
28
+ * instance - and through it the whole picker subtree - so the owning
29
+ * component must call this from its own destroy().
30
+ */
31
+ destroy: () => void;
25
32
  /**
26
33
  * Initializes instance of DockedDisplayPlugin
27
34
  * @param el HTMLElement to position to
package/package.json CHANGED
@@ -1,14 +1,14 @@
1
1
  {
2
2
  "name": "@expressivecss/expressive",
3
- "version": "0.1.0-alpha.3",
4
- "description": "Material Design 3 CSS framework based on Materialize",
3
+ "version": "0.2.0",
4
+ "description": "ExpressiveCSS - a Material Design 3 front-end framework.",
5
5
  "license": "MIT",
6
- "author": "Javier Baez",
6
+ "author": "BaezFJ <baezdevs@gmail.com>",
7
+ "homepage": "https://github.com/BaezFJ/ExpressiveCSS#readme",
7
8
  "repository": {
8
9
  "type": "git",
9
10
  "url": "git+https://github.com/BaezFJ/ExpressiveCSS.git"
10
11
  },
11
- "homepage": "https://github.com/BaezFJ/ExpressiveCSS#readme",
12
12
  "bugs": {
13
13
  "url": "https://github.com/BaezFJ/ExpressiveCSS/issues"
14
14
  },
@@ -18,54 +18,78 @@
18
18
  "scss",
19
19
  "material-design",
20
20
  "material-design-3",
21
+ "md3",
22
+ "front-end",
21
23
  "framework",
22
24
  "ui",
23
25
  "components",
24
- "responsive",
25
- "materialize"
26
+ "typescript"
27
+ ],
28
+ "type": "module",
29
+ "main": "./dist/js/expressive.cjs",
30
+ "module": "./dist/js/expressive.mjs",
31
+ "types": "./dist/types/index.d.ts",
32
+ "unpkg": "./dist/js/expressive.min.js",
33
+ "jsdelivr": "./dist/js/expressive.min.js",
34
+ "sideEffects": [
35
+ "./dist/js/*",
36
+ "*.css",
37
+ "*.scss"
26
38
  ],
27
- "main": "dist/js/expressive.js",
28
- "module": "dist/js/expressive.mjs",
29
- "types": "dist/js/index.d.ts",
30
- "style": "dist/css/expressive.css",
31
- "sass": "src/scss/expressive.scss",
32
39
  "exports": {
33
40
  ".": {
34
- "types": "./dist/js/index.d.ts",
41
+ "types": "./dist/types/index.d.ts",
35
42
  "import": "./dist/js/expressive.mjs",
36
- "require": "./dist/js/expressive.js"
43
+ "require": "./dist/js/expressive.cjs",
44
+ "default": "./dist/js/expressive.mjs"
37
45
  },
38
46
  "./css": "./dist/css/expressive.css",
39
47
  "./css/min": "./dist/css/expressive.min.css",
40
- "./scss": "./src/scss/expressive.scss"
48
+ "./scss": "./src/sass/expressive.scss",
49
+ "./scss/*": "./src/sass/*"
41
50
  },
51
+ "files": [
52
+ "dist",
53
+ "src/sass"
54
+ ],
42
55
  "publishConfig": {
43
56
  "access": "public"
44
57
  },
45
- "files": [
46
- "dist/",
47
- "src/scss/",
48
- "LICENSE"
49
- ],
50
- "sideEffects": [
51
- "*.css",
52
- "dist/js/expressive.js"
53
- ],
54
58
  "scripts": {
55
- "build:ts": "rollup -c",
56
- "build:css": "sass src/scss/expressive.scss dist/css/expressive.css --no-source-map",
57
- "build:css:min": "sass src/scss/expressive.scss dist/css/expressive.min.css --no-source-map --style=compressed",
58
- "check:ts": "tsc --noEmit",
59
- "build": "npm run build:ts && npm run build:css && npm run build:css:min",
60
- "prepublishOnly": "npm run build"
59
+ "prepack": "npm run build",
60
+ "clean": "node -e \"require('fs').rmSync('dist', { recursive: true, force: true })\"",
61
+ "build": "run-s clean build:css build:js build:types",
62
+ "build:css": "run-s build:css:expanded build:css:min",
63
+ "build:css:expanded": "sass src/sass/expressive.scss dist/css/expressive.css --style=expanded --source-map --load-path=src/sass",
64
+ "build:css:min": "sass src/sass/expressive.scss dist/css/expressive.min.css --style=compressed --source-map --load-path=src/sass",
65
+ "build:js": "run-s build:js:esm build:js:cjs build:js:iife build:js:iife-min",
66
+ "build:js:esm": "esbuild src/ts/index.ts --bundle --format=esm --target=es2020 --outfile=dist/js/expressive.mjs --sourcemap",
67
+ "build:js:cjs": "esbuild src/ts/index.ts --bundle --format=cjs --target=es2020 --outfile=dist/js/expressive.cjs --sourcemap",
68
+ "build:js:iife": "esbuild src/ts/index.ts --bundle --format=iife --global-name=Expressive --target=es2020 --outfile=dist/js/expressive.js --sourcemap",
69
+ "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",
70
+ "build:types": "tsc -p tsconfig.build.json",
71
+ "typecheck": "tsc -p tsconfig.json --noEmit",
72
+ "test": "run-s build:js test:run",
73
+ "test:run": "node --test",
74
+ "test:watch": "node --test --watch",
75
+ "watch": "run-p watch:css watch:js",
76
+ "watch:css": "sass --watch src/sass/expressive.scss:dist/css/expressive.css --style=expanded --source-map --load-path=src/sass",
77
+ "watch:js": "esbuild src/ts/index.ts --bundle --format=iife --global-name=Expressive --target=es2020 --outfile=dist/js/expressive.js --sourcemap --watch=forever"
78
+ },
79
+ "engines": {
80
+ "node": ">=20"
61
81
  },
82
+ "browserslist": [
83
+ "last 5 Chrome versions",
84
+ "last 5 Firefox versions",
85
+ "not IE 11",
86
+ "not dead"
87
+ ],
62
88
  "devDependencies": {
63
- "@rollup/plugin-terser": "^0.4.4",
64
- "@rollup/plugin-typescript": "^12.1.2",
65
- "@types/node": "^25.2.1",
66
- "rollup": "^4.30.1",
67
- "sass": "^1.97.3",
68
- "tslib": "^2.8.1",
69
- "typescript": "^5.7.3"
89
+ "esbuild": "^0.28.2",
90
+ "jsdom": "^30.0.1",
91
+ "npm-run-all2": "^9.0.3",
92
+ "sass": "^1.102.0",
93
+ "typescript": "^7.0.2"
70
94
  }
71
95
  }
@@ -0,0 +1,200 @@
1
+ # src/sass
2
+
3
+ ```
4
+ expressive.scss entry point - decides cascade order, nothing else
5
+ abstracts/ variables, functions, mixins (emits no CSS)
6
+ tokens/ M3 design tokens as :root custom properties
7
+ base/ normalize, element defaults, typography, grid
8
+ components/ one file per widget (+ components/forms/)
9
+ utilities/ single-purpose helper classes
10
+ ```
11
+
12
+ ## Browser support
13
+
14
+ The last 5 versions of Chrome and Firefox. No IE, no Edge Legacy. Declared in
15
+ `package.json` under `browserslist` - nothing reads it automatically, since
16
+ there is no autoprefixer or postcss step, but it is the baseline every
17
+ judgement call in here is made against.
18
+
19
+ That baseline is what licenses `@layer`, `light-dark()`, `color-mix()`,
20
+ `aspect-ratio`, `clamp()`, `inset` and media-query range syntax to be used
21
+ directly, with no fallback. A vendor prefix is only justified now if the
22
+ property is non-standard with no unprefixed equivalent
23
+ (`-webkit-tap-highlight-color`, `-webkit-font-smoothing`) or the selector is an
24
+ engine's private pseudo-element (`::-webkit-slider-thumb`, `::-moz-range-track`).
25
+ Everything else was removed.
26
+
27
+ ## Two rules
28
+
29
+ **1. Every partial starts with the same line.**
30
+
31
+ ```scss
32
+ @use "../abstracts" as *; // components/, base/, tokens/, utilities/
33
+ @use "../../abstracts" as *; // components/forms/
34
+ ```
35
+
36
+ That single line brings in the breakpoint mixins, the Sass variables (grid,
37
+ font sizes), `z-depth()`, and the `btn-*` mixins.
38
+ There is never a second project-local `@use` to work out — if you find yourself
39
+ adding one, see rule 2.
40
+
41
+ **2. Nothing outside `abstracts/` is ever imported by another partial.**
42
+
43
+ Dependencies point one way: everything depends on `abstracts`, `abstracts`
44
+ depends on nothing. This is what keeps `expressive.scss` the only place cascade
45
+ order is decided. (This is the Sass dependency graph, not the CSS `@layer`
46
+ list — they happen to be described by the same word.)
47
+
48
+ The trap is `@extend`: it only resolves if the extending file loads the module
49
+ that defines the class, so `@extend .z-depth-1` used to force components to
50
+ `@use` a CSS-emitting file, and whichever partial got loaded first silently
51
+ decided where its CSS landed in the output. Use a mixin instead:
52
+
53
+ ```scss
54
+ // no
55
+ @extend .z-depth-1;
56
+ @extend .surface-variant;
57
+
58
+ // yes
59
+ @include z-depth("1");
60
+ background-color: var(--md-sys-color-surface-variant);
61
+ ```
62
+
63
+ `@extend` within a single file (placeholders, `.btn-large { @extend .btn; }`) is
64
+ fine — it crosses no boundary.
65
+
66
+ ## Breakpoints
67
+
68
+ Three of them, in `abstracts/_breakpoints.scss`, reached through mixins:
69
+
70
+ ```scss
71
+ @include bp-up("small") { } // @media (width >= 601px)
72
+ @include bp-down("large") { } // @media (width < 993px)
73
+ @include bp-between("small", "large") { }
74
+ ```
75
+
76
+ Names are `small` (601px), `large` (993px), `xlarge` (1201px) — the boundary a
77
+ query sits on, not the size of the screen it targets. Range syntax has an
78
+ exclusive comparator, so the old `600.99px` / `992.99px` values used to stop
79
+ `max-width` and `min-width` both matching on the boundary pixel are gone, and
80
+ each boundary is written once.
81
+
82
+ The interpolated-string form (`@media #{$medium-and-up}`) still compiles and now
83
+ produces the same range queries, but it is deprecated and unused in here.
84
+
85
+ ## Theming
86
+
87
+ `tokens/_reference.scss` **generates** five of the six M3 tonal ramps from
88
+ `--md-source` with relative color syntax, then resolves them into a
89
+ `--md-sys-color-<role>-light` / `-dark` pair per role. `tokens/_theme.scss` maps
90
+ each pair onto the live `--md-sys-color-<role>` name with `light-dark()`, in one
91
+ `@each` over `$sys-color-roles`.
92
+
93
+ The shipped seed is `#006A79`, a teal. It is chosen, not arbitrary: the error
94
+ ramp is pinned at hue 27.7°, and both primary and tertiary (source + 62°) derive
95
+ from the seed, so the seed has to keep both clear of red. Warm "appetising" hues
96
+ (0–60°) put primary on top of error — ΔEok 0.07–0.12 against ~0.28 for teal.
97
+ Green is avoided so it stays available for a "settled/paid" role, which Material
98
+ does not supply. See the comment above `$md-source`.
99
+
100
+ Setting `--md-source` re-themes the whole system at runtime, with no rebuild:
101
+
102
+ ```css
103
+ :root { --md-source: #6750a4; } /* everything but the error ramp follows */
104
+ ```
105
+
106
+ Two things about that file are load-bearing and non-obvious. **M3 "tone" is
107
+ CIELAB L\*, not OKLCH lightness** — `oklch(40% c h)` is not tone 40, and using it
108
+ drifts far past the just-noticeable difference; the `$tones` table is the
109
+ conversion. And the **error ramp is literal hex on purpose**, because Material
110
+ fixes the error hue rather than deriving it. `tests/color-drift.test.js` enforces
111
+ both.
112
+
113
+ The pairs are public API — the docs' Themes page tells people to override them —
114
+ so they stay. Consume the live name:
115
+
116
+ ```scss
117
+ // no - locks the rule to one theme
118
+ border-bottom: 1px solid var(--md-sys-color-surface-variant-light);
119
+
120
+ // yes
121
+ border-bottom: 1px solid var(--md-sys-color-surface-variant);
122
+ ```
123
+
124
+ `light-dark()` resolves against the element's used `color-scheme` at the point
125
+ of *use*, not where the custom property was declared. So `:root[theme='dark']`
126
+ only has to set `color-scheme: dark` and all 30 tokens re-resolve — and setting
127
+ `color-scheme` on any subtree re-themes just that subtree. `color-scheme` is
128
+ load-bearing now; changing it is not cosmetic.
129
+
130
+ ## Translucent colors
131
+
132
+ ```scss
133
+ // no - tokens are hex, not channels; the browser drops the whole declaration
134
+ background-color: rgba(var(--md-sys-color-primary), 0.06);
135
+
136
+ // yes
137
+ background-color: color-mix(in oklab, var(--md-sys-color-primary) 6%, transparent);
138
+ ```
139
+
140
+ Mix `in oklab`, not `in srgb`. sRGB interpolation darkens and desaturates
141
+ through the midtones, so a 50% tint reads muddier than either endpoint; OKLab is
142
+ perceptually uniform, so a 16% state layer looks like 16% on every hue. Use
143
+ `oklab` rather than `oklch` for mixing: `oklch` interpolates the hue *angle*,
144
+ which can swing a tint through unrelated hues on its way between two colors.
145
+
146
+ A `var()` that names an undefined custom property fails the same silent way, so
147
+ a typo costs you the rule with no warning at build time. To audit the compiled
148
+ sheet, compare every `var(--x)` reference against every `--x:` definition — the
149
+ names use hyphens, and a regex of `[\w-]+` (not `[a-z-]+`) will also catch
150
+ stragglers written with underscores.
151
+
152
+ ## Where things go
153
+
154
+ | Adding | Goes in | Then |
155
+ | --- | --- | --- |
156
+ | A component | `components/_thing.scss` | add `@forward "thing";` to `components/_index.scss` |
157
+ | A helper class | `utilities/` — `_z-depth`, `_spacing`, `_visibility`, `_helpers`, `_colors` | add to `utilities/_index.scss` if it is a new file |
158
+ | A shared value or mixin | `abstracts/` | add to `abstracts/_index.scss` |
159
+ | An element default | `base/_global.scss` | — |
160
+
161
+ A rule is a **utility** when it is one job, one class, and independent of any
162
+ component (`.hide`, `.m-3`, `.truncate`). It belongs in **base** when it styles
163
+ bare elements (`a`, `table`, `blockquote`), and in **components** when it only
164
+ exists to serve one widget — including widgets whose CSS is driven by
165
+ JavaScript rather than by a user-applied class (`.pinned`, `.display-docked`,
166
+ `ul.staggered-list li`). If a selector in `base/` names a component, it is in
167
+ the wrong file.
168
+ | A colour/type token | `tokens/_reference.scss` | wire it up in `tokens/_theme.scss` |
169
+
170
+ Anything under `abstracts/` that emits a selector is a bug: it would put CSS in
171
+ the output at a position `expressive.scss` does not control.
172
+
173
+ ## Cascade layers
174
+
175
+ `expressive.scss` declares `@layer tokens, base, components, utilities;` and
176
+ loads each layer with `meta.load-css()` — `@forward` cannot appear inside
177
+ `@layer`, which is the only reason the entry file uses a different mechanism
178
+ from every other index file.
179
+
180
+ Three consequences worth knowing:
181
+
182
+ **Utilities beat components, without specificity.** Layer order outranks
183
+ specificity entirely, so `.hide` wins over `.collection .collection-item` even
184
+ though it is less specific. Utilities used to be emitted *before* components and
185
+ had to out-shout them; they are now last.
186
+
187
+ **Unlayered CSS beats every layer.** A consumer overriding the framework writes
188
+ plain `.btn { … }` and wins — no specificity war, no dependence on where
189
+ Expressive sits in their bundle. This is the main thing layers buy.
190
+
191
+ **`!important` is deliberately kept on utilities.** It looks redundant now, and
192
+ it is not. Layer order *reverses* for important declarations, but the rule that
193
+ matters here is simpler: a normal declaration inside a layer loses to any
194
+ unlayered declaration. Drop the flag from `.hide` and it stops beating a
195
+ consumer's own `display` — silently, and in the common case. Classes whose whole
196
+ job is to be unconditional (`.hide`, `.m-3`, the palette classes) keep it.
197
+
198
+ The `!important` in `components/` is a different thing again: `.pushpin`,
199
+ `.tap-target` and the preloader set JS-driven geometry that must not be
200
+ overridable. Those stay too.