@expressivecss/expressive 0.1.0-alpha.3 → 0.1.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/LICENSE +82 -2
- package/README.md +153 -43
- package/dist/css/expressive.css +7066 -9658
- package/dist/css/expressive.css.map +1 -0
- package/dist/css/expressive.min.css +1 -1
- package/dist/css/expressive.min.css.map +1 -0
- package/dist/js/expressive.cjs +7451 -0
- package/dist/js/expressive.cjs.map +7 -0
- package/dist/js/expressive.js +7313 -8099
- package/dist/js/expressive.js.map +7 -1
- package/dist/js/expressive.min.js +75 -1
- package/dist/js/expressive.min.js.map +7 -1
- package/dist/js/expressive.mjs +7222 -7998
- package/dist/js/expressive.mjs.map +7 -1
- package/dist/{js → types}/components/autocomplete.d.ts +10 -1
- package/dist/{js → types}/components/buttons.d.ts +11 -1
- package/dist/{js → types}/components/cards.d.ts +10 -1
- package/dist/{js → types}/components/carousel.d.ts +4 -5
- package/dist/{js/forms/character-counter.d.ts → types/components/characterCounter.d.ts} +1 -1
- package/dist/{js → types}/components/chips.d.ts +1 -1
- package/dist/{js → types}/components/collapsible.d.ts +1 -1
- package/dist/{js → types}/components/datepicker.d.ts +63 -2
- package/dist/{js → types}/components/dropdown.d.ts +3 -3
- package/dist/types/components/index.d.ts +23 -0
- package/dist/{js/components/materialbox.d.ts → types/components/lightbox.d.ts} +21 -21
- package/dist/{js → types}/components/modal.d.ts +1 -1
- package/dist/{js → types}/components/parallax.d.ts +11 -5
- package/dist/{js → types}/components/pushpin.d.ts +1 -1
- package/dist/{js/forms → types/components}/range.d.ts +3 -3
- package/dist/types/components/registry.d.ts +107 -0
- package/dist/{js → types}/components/scrollspy.d.ts +6 -9
- package/dist/{js/forms → types/components}/select.d.ts +11 -2
- package/dist/{js → types}/components/sidenav.d.ts +1 -1
- package/dist/{js → types}/components/slider.d.ts +3 -2
- package/dist/{js → types}/components/tabs.d.ts +3 -2
- package/dist/{js/components/tap-target.d.ts → types/components/tapTarget.d.ts} +2 -2
- package/dist/{js → types}/components/timepicker.d.ts +13 -5
- package/dist/{js/components/toast.d.ts → types/components/toasts.d.ts} +28 -4
- package/dist/{js → types}/components/tooltip.d.ts +2 -2
- package/dist/types/core/bounding.d.ts +6 -0
- package/dist/{js/foundation → types/core}/component.d.ts +1 -1
- package/dist/types/core/edges.d.ts +6 -0
- package/dist/{js/foundation → types/core}/utils.d.ts +21 -14
- package/dist/types/index.d.ts +6 -0
- package/dist/{js/plugins/docked-display.d.ts → types/plugins/dockedDisplayPlugin.d.ts} +7 -0
- package/package.json +60 -36
- package/src/sass/README.md +200 -0
- package/src/sass/abstracts/_breakpoints.scss +69 -0
- package/src/sass/abstracts/_elevation.scss +34 -0
- package/src/sass/abstracts/_index.scss +7 -0
- package/src/sass/abstracts/_mixins.scss +171 -0
- package/src/sass/abstracts/_variables.scss +57 -0
- package/src/{scss/layout/_table.scss → sass/base/_global.scss} +64 -2
- package/src/{scss/layout → sass/base}/_grid.scss +6 -6
- package/src/sass/base/_index.scss +6 -0
- package/src/{scss/foundation → sass/base}/_normalize.scss +51 -139
- package/src/sass/base/_typography.scss +90 -0
- package/src/sass/components/_badges.scss +77 -0
- package/src/{scss → sass}/components/_breadcrumb.scss +2 -0
- package/src/sass/components/_buttons.scss +407 -0
- package/src/sass/components/_cards.scss +328 -0
- package/src/{scss → sass}/components/_carousel.scss +5 -3
- package/src/sass/components/_chips.scss +140 -0
- package/src/sass/components/_collapsible.scss +81 -0
- package/src/{scss → sass}/components/_collection.scss +2 -2
- package/src/{scss/components/_date-picker.scss → sass/components/_datepicker.scss} +51 -44
- package/src/sass/components/_docked-display.scss +11 -0
- package/src/sass/components/_dropdown.scss +129 -0
- package/src/{scss/components/_material-icons.scss → sass/components/_icons-material-design.scss} +2 -0
- package/src/sass/components/_index.scss +35 -0
- package/src/{scss/components/_material-box.scss → sass/components/_lightbox.scss} +6 -4
- package/src/sass/components/_modal.scss +144 -0
- package/src/sass/components/_navbar.scss +345 -0
- package/src/{scss/layout/_footer.scss → sass/components/_page-footer.scss} +7 -6
- package/src/{scss → sass}/components/_pagination.scss +4 -3
- package/src/{scss → sass}/components/_parallax.scss +4 -1
- package/src/{scss → sass}/components/_preloader.scss +19 -97
- package/src/{scss → sass}/components/_pulse.scss +2 -0
- package/src/sass/components/_pushpin.scss +11 -0
- package/src/{scss → sass}/components/_sidenav.scss +99 -82
- package/src/{scss → sass}/components/_slider.scss +3 -1
- package/src/{scss/components/_table-of-contents.scss → sass/components/_table_of_contents.scss} +2 -0
- package/src/sass/components/_tabs.scss +232 -0
- package/src/{scss/components/_tap-target.scss → sass/components/_tapTarget.scss} +24 -22
- package/src/{scss/components/_time-picker.scss → sass/components/_timepicker.scss} +56 -56
- package/src/sass/components/_toast.scss +260 -0
- package/src/sass/components/_toolbar.scss +192 -0
- package/src/sass/components/_tooltip.scss +166 -0
- package/src/{scss → sass}/components/_transitions.scss +4 -4
- package/src/sass/components/forms/_checkboxes.scss +288 -0
- package/src/sass/components/forms/_fieldset.scss +128 -0
- package/src/{scss → sass/components}/forms/_file-input.scss +11 -9
- package/src/{scss → sass/components}/forms/_index.scss +2 -0
- package/src/sass/components/forms/_input-fields.scss +326 -0
- package/src/sass/components/forms/_radio-buttons.scss +190 -0
- package/src/sass/components/forms/_range.scss +310 -0
- package/src/sass/components/forms/_select.scss +103 -0
- package/src/sass/components/forms/_switches.scss +239 -0
- package/src/sass/expressive.scss +60 -0
- package/src/sass/tokens/_index.scss +4 -0
- package/src/sass/tokens/_reference.scss +265 -0
- package/src/sass/tokens/_theme.scss +49 -0
- package/src/{scss → sass}/utilities/_helpers.scss +33 -30
- package/src/sass/utilities/_index.scss +8 -0
- package/src/{scss → sass}/utilities/_spacing.scss +4 -3
- package/src/sass/utilities/_typescale.scss +45 -0
- package/src/sass/utilities/_visibility.scss +63 -0
- package/src/sass/utilities/_z-depth.scss +18 -0
- package/dist/js/components/index.d.ts +0 -21
- package/dist/js/forms/index.d.ts +0 -4
- package/dist/js/foundation/index.d.ts +0 -2
- package/dist/js/index.d.ts +0 -52
- package/dist/js/plugins/index.d.ts +0 -1
- package/src/scss/components/_appbar.scss +0 -454
- package/src/scss/components/_badges.scss +0 -77
- package/src/scss/components/_buttons.scss +0 -412
- package/src/scss/components/_cards.scss +0 -267
- package/src/scss/components/_chips.scss +0 -147
- package/src/scss/components/_collapsible.scss +0 -84
- package/src/scss/components/_dropdown.scss +0 -98
- package/src/scss/components/_index.scss +0 -28
- package/src/scss/components/_modal.scss +0 -84
- package/src/scss/components/_tabs.scss +0 -176
- package/src/scss/components/_toast.scss +0 -70
- package/src/scss/components/_tooltip.scss +0 -49
- package/src/scss/components/_video.scss +0 -15
- package/src/scss/expressive.scss +0 -7
- package/src/scss/forms/_checkboxes.scss +0 -199
- package/src/scss/forms/_input-fields.scss +0 -360
- package/src/scss/forms/_radio-buttons.scss +0 -114
- package/src/scss/forms/_range.scss +0 -148
- package/src/scss/forms/_select.scss +0 -108
- package/src/scss/forms/_switches.scss +0 -127
- package/src/scss/foundation/_base.scss +0 -71
- package/src/scss/foundation/_color-palette.scss +0 -402
- package/src/scss/foundation/_index.scss +0 -10
- package/src/scss/foundation/_mixins.scss +0 -198
- package/src/scss/foundation/_motion.scss +0 -70
- package/src/scss/foundation/_shape.scss +0 -30
- package/src/scss/foundation/_state-layers.scss +0 -68
- package/src/scss/foundation/_theme.scss +0 -238
- package/src/scss/foundation/_tokens.scss +0 -511
- package/src/scss/foundation/_variables.scss +0 -57
- package/src/scss/layout/_index.scss +0 -3
- package/src/scss/utilities/_a11y.scss +0 -77
- package/src/scss/utilities/_elevation.scss +0 -63
- package/src/scss/utilities/_index.scss +0 -7
- package/src/scss/utilities/_responsive.scss +0 -62
- package/src/scss/utilities/_typography.scss +0 -115
- /package/dist/{js/forms → types/behaviors}/forms.d.ts +0 -0
- /package/dist/{js/components → types/behaviors}/waves.d.ts +0 -0
- /package/src/{scss → sass}/utilities/_colors.scss +0 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Carousel } from './carousel';
|
|
2
|
-
import { Component, BaseOptions, InitElements, MElement } from '../
|
|
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:
|
|
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 '../
|
|
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: () =>
|
|
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 '../
|
|
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
|
-
/**
|
|
133
|
-
|
|
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:
|
|
144
|
-
vibrateTimer:
|
|
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 '../
|
|
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
|
-
|
|
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
|
-
*
|
|
86
|
-
*
|
|
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 '../
|
|
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
|
|
27
|
+
* @default 4
|
|
28
28
|
*/
|
|
29
29
|
margin: number;
|
|
30
30
|
/**
|
|
@@ -1,15 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
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: (
|
|
109
|
+
static throttle(func: (...args: unknown[]) => void, wait: number, options?: Partial<{
|
|
103
110
|
leading: boolean;
|
|
104
111
|
trailing: boolean;
|
|
105
|
-
}>): (...args:
|
|
112
|
+
}>): (...args: unknown[]) => any;
|
|
106
113
|
/**
|
|
107
114
|
* Renders confirm/close buttons with callback function
|
|
108
115
|
*/
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export * from './components';
|
|
2
|
+
export { Forms } from './behaviors/forms';
|
|
3
|
+
export { Waves } from './behaviors/waves';
|
|
4
|
+
export { AutoInit } from './components/registry';
|
|
5
|
+
export type { AutoInitOptions } from './components/registry';
|
|
6
|
+
export declare const version = "0.1.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
|
|
4
|
-
"description": "Material Design 3
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "ExpressiveCSS - a Material Design 3 front-end framework.",
|
|
5
5
|
"license": "MIT",
|
|
6
|
-
"author": "
|
|
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
|
-
"
|
|
25
|
-
|
|
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/
|
|
41
|
+
"types": "./dist/types/index.d.ts",
|
|
35
42
|
"import": "./dist/js/expressive.mjs",
|
|
36
|
-
"require": "./dist/js/expressive.
|
|
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/
|
|
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
|
-
"
|
|
56
|
-
"
|
|
57
|
-
"build
|
|
58
|
-
"
|
|
59
|
-
"build": "
|
|
60
|
-
"
|
|
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
|
-
"
|
|
64
|
-
"
|
|
65
|
-
"
|
|
66
|
-
"
|
|
67
|
-
"
|
|
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.
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
@use "sass:map";
|
|
2
|
+
@use "sass:list";
|
|
3
|
+
|
|
4
|
+
// Breakpoints, as media-query range syntax.
|
|
5
|
+
//
|
|
6
|
+
// The old form built a query by interpolating a whole string:
|
|
7
|
+
//
|
|
8
|
+
// $medium-and-down: "only screen and (max-width : 992.99px)";
|
|
9
|
+
// @media #{$medium-and-down} { ... }
|
|
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:
|
|
14
|
+
//
|
|
15
|
+
// @include bp-down("large") { ... } -> @media (width < 993px)
|
|
16
|
+
//
|
|
17
|
+
// `only screen` went with them: it existed to hide queries from Netscape 4
|
|
18
|
+
// and IE, and `screen` alone excludes print anyway - which these rules never
|
|
19
|
+
// wanted to do, since a hidden element should stay hidden on paper.
|
|
20
|
+
|
|
21
|
+
$breakpoints: (
|
|
22
|
+
"small": 601px, // phone -> tablet
|
|
23
|
+
"large": 993px, // tablet -> desktop
|
|
24
|
+
"xlarge": 1201px, // desktop -> wide desktop
|
|
25
|
+
) !default;
|
|
26
|
+
|
|
27
|
+
@function breakpoint($name) {
|
|
28
|
+
@if not map.has-key($breakpoints, $name) {
|
|
29
|
+
@error "Unknown breakpoint `#{$name}`. Available: #{map.keys($breakpoints)}.";
|
|
30
|
+
}
|
|
31
|
+
@return map.get($breakpoints, $name);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// At or above a breakpoint.
|
|
35
|
+
@mixin bp-up($name) {
|
|
36
|
+
@media (width >= #{breakpoint($name)}) {
|
|
37
|
+
@content;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Strictly below a breakpoint.
|
|
42
|
+
@mixin bp-down($name) {
|
|
43
|
+
@media (width < #{breakpoint($name)}) {
|
|
44
|
+
@content;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// From one breakpoint up to (not including) another.
|
|
49
|
+
@mixin bp-between($from, $to) {
|
|
50
|
+
@media (#{breakpoint($from)} <= width < #{breakpoint($to)}) {
|
|
51
|
+
@content;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Deprecated: the interpolated-string breakpoints.
|
|
56
|
+
//
|
|
57
|
+
// Kept so a downstream `@media #{$medium-and-up}` keeps compiling - the
|
|
58
|
+
// framework itself no longer uses them. Values are now range syntax, so the
|
|
59
|
+
// queries they produce match the mixins above exactly. Prefer the mixins.
|
|
60
|
+
$small-screen-up: breakpoint("small") !default;
|
|
61
|
+
$medium-screen-up: breakpoint("large") !default;
|
|
62
|
+
$large-screen-up: breakpoint("xlarge") !default;
|
|
63
|
+
|
|
64
|
+
$medium-and-up: "(width >= #{breakpoint('small')})" !default;
|
|
65
|
+
$large-and-up: "(width >= #{breakpoint('large')})" !default;
|
|
66
|
+
$extra-large-and-up: "(width >= #{breakpoint('xlarge')})" !default;
|
|
67
|
+
$small-and-down: "(width < #{breakpoint('small')})" !default;
|
|
68
|
+
$medium-and-down: "(width < #{breakpoint('large')})" !default;
|
|
69
|
+
$medium-only: "(#{breakpoint('small')} <= width < #{breakpoint('large')})" !default;
|