m3-svelte 6.0.2 → 6.0.4

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.
@@ -16,7 +16,7 @@
16
16
  headline = "",
17
17
  supporting = "",
18
18
  trailing,
19
- lines = overline && supporting ? 3 : overline || supporting ? 2 : 1,
19
+ lines: _lines,
20
20
  ...props
21
21
  }: {
22
22
  leading?: Snippet;
@@ -26,6 +26,8 @@
26
26
  trailing?: Snippet;
27
27
  lines?: number;
28
28
  } & ActionProps = $props();
29
+
30
+ let lines = $derived(_lines ?? (overline && supporting ? 3 : overline || supporting ? 2 : 1));
29
31
  </script>
30
32
 
31
33
  {#snippet content()}
@@ -78,6 +80,7 @@
78
80
  <style>
79
81
  .m3-container {
80
82
  display: flex;
83
+ justify-self: stretch;
81
84
  padding: 0.5rem 1.5rem 0.5rem 1rem;
82
85
  align-items: center;
83
86
  gap: 1rem;
@@ -66,15 +66,7 @@ opacity: ${Math.min(t * 3, 1)};`,
66
66
  use:clickOutside
67
67
  style:--anchor-name="--{id}"
68
68
  >
69
- <input
70
- type="date"
71
- class="focus-none"
72
- {disabled}
73
- {required}
74
- {id}
75
- bind:value
76
- {...extra}
77
- />
69
+ <input type="date" class="focus-none" {disabled} {required} {id} bind:value {...extra} />
78
70
  <label for={id}>{label}</label>
79
71
  <button type="button" {disabled} title={datePickerTitle} onclick={() => (picker = !picker)}>
80
72
  <Layer />
@@ -66,15 +66,7 @@ opacity: ${Math.min(t * 3, 1)};`,
66
66
  use:clickOutside
67
67
  style:--anchor-name="--{id}"
68
68
  >
69
- <input
70
- type="date"
71
- class="focus-none"
72
- {disabled}
73
- {required}
74
- {id}
75
- bind:value
76
- {...extra}
77
- />
69
+ <input type="date" class="focus-none" {disabled} {required} {id} bind:value {...extra} />
78
70
  <div class="layer"></div>
79
71
  <label for={id}>{label}</label>
80
72
  <button type="button" {disabled} title={datePickerTitle} onclick={() => (picker = !picker)}>
@@ -9,7 +9,13 @@
9
9
  let {
10
10
  date = "",
11
11
  clearable,
12
+ // eslint and svelte disagree
13
+ // eslint-disable-next-line svelte/no-unused-svelte-ignore
14
+ // svelte-ignore state_referenced_locally
12
15
  focusedMonth = $bindable(parseInt(date.slice(5, 7)) - 1 || now.getMonth()),
16
+ // eslint and svelte disagree
17
+ // eslint-disable-next-line svelte/no-unused-svelte-ignore
18
+ // svelte-ignore state_referenced_locally
13
19
  focusedYear = $bindable(parseInt(date.slice(0, 4)) || now.getFullYear()),
14
20
  startYear = now.getFullYear() - 50,
15
21
  endYear = now.getFullYear() + 10,
@@ -29,10 +35,7 @@
29
35
  } = $props();
30
36
 
31
37
  let currentView: "calendar" | "year" | "month" = $state("calendar");
32
- let chosenDate = $state(date);
33
- $effect(() => {
34
- chosenDate = date;
35
- });
38
+ let chosenDate = $derived(date);
36
39
 
37
40
  const getLongMonth = (month: number) =>
38
41
  new Date(0, month).toLocaleDateString(undefined, { month: "long" });
@@ -367,7 +367,7 @@
367
367
 
368
368
  background-color: var(--m3c-inverse-surface);
369
369
  color: var(--m3c-inverse-on-surface);
370
- width: 3rem;
370
+ min-width: 3rem;
371
371
  padding: 0.75rem 1rem;
372
372
  border-radius: var(--m3-slider-handle-shape);
373
373
 
@@ -23,6 +23,11 @@
23
23
  const monthClick = () => (currentView = currentView == "calendar" ? "month" : "calendar");
24
24
  const getShortMonth = (month: number) =>
25
25
  new Date(0, month).toLocaleDateString(undefined, { month: "short" });
26
+
27
+ let prevMonth = $derived((focusedMonth - 1 + 12) % 12);
28
+ let nextMonth = $derived((focusedMonth + 1) % 12);
29
+ let prevYear = $derived(focusedYear - 1);
30
+ let nextYear = $derived(focusedYear + 1);
26
31
  </script>
27
32
 
28
33
  <div class="m3-container" class:choosing={currentView != "calendar"}>
@@ -30,22 +35,23 @@
30
35
  <button
31
36
  type="button"
32
37
  class="arrow"
33
- onclick={() => (focusedMonth = (focusedMonth - 1 + 12) % 12)}
38
+ onclick={() => (focusedMonth = prevMonth)}
39
+ title={getShortMonth(prevMonth)}
34
40
  >
35
41
  <Layer />
36
42
  <Icon icon={iconLeft} />
37
43
  </button>
38
- <button
39
- type="button"
40
- class="chooser"
41
- onclick={monthClick}
42
- disabled={currentView == "year"}
43
- >
44
+ <button type="button" class="chooser" onclick={monthClick} disabled={currentView == "year"}>
44
45
  <Layer />
45
46
  {getShortMonth(focusedMonth)}
46
47
  <Icon icon={iconDown} />
47
48
  </button>
48
- <button type="button" class="arrow" onclick={() => (focusedMonth = (focusedMonth + 1) % 12)}>
49
+ <button
50
+ type="button"
51
+ class="arrow"
52
+ onclick={() => (focusedMonth = nextMonth)}
53
+ title={getShortMonth(nextMonth)}
54
+ >
49
55
  <Layer />
50
56
  <Icon icon={iconRight} />
51
57
  </button>
@@ -55,17 +61,13 @@
55
61
  type="button"
56
62
  class="arrow"
57
63
  disabled={focusedYear <= startYear}
58
- onclick={() => focusedYear--}
64
+ onclick={() => (focusedYear = prevYear)}
65
+ title={prevYear.toString()}
59
66
  >
60
67
  <Layer />
61
68
  <Icon icon={iconLeft} />
62
69
  </button>
63
- <button
64
- type="button"
65
- class="chooser"
66
- onclick={yearClick}
67
- disabled={currentView == "month"}
68
- >
70
+ <button type="button" class="chooser" onclick={yearClick} disabled={currentView == "month"}>
69
71
  <Layer />
70
72
  {focusedYear}
71
73
  <Icon icon={iconDown} />
@@ -74,7 +76,8 @@
74
76
  type="button"
75
77
  class="arrow"
76
78
  disabled={focusedYear >= endYear}
77
- onclick={() => focusedYear++}
79
+ title={nextYear.toString()}
80
+ onclick={() => (focusedYear = nextYear)}
78
81
  >
79
82
  <Layer />
80
83
  <Icon icon={iconRight} />
@@ -16,14 +16,7 @@
16
16
  } = $props();
17
17
  </script>
18
18
 
19
- <button
20
- type="button"
21
- class="item"
22
- {disabled}
23
- class:today
24
- class:selected
25
- {onclick}
26
- >
19
+ <button type="button" class="item" {disabled} class:today class:selected {onclick}>
27
20
  <Layer />
28
21
  {label}
29
22
  </button>
@@ -1,2 +1,2 @@
1
- export declare const linear: (amp: number, center: number, from: number, to: number, time: number, cutoffTo?: number) => string;
1
+ export declare const linear: (amp: number, center: number, from: number, to: number, time: number, softTo?: number) => string;
2
2
  export declare const trackOpacity: (right: number, x: number) => number;
@@ -2,14 +2,14 @@ const frequencyT = (Math.PI * 2) / 1000;
2
2
  const frequencyX = (Math.PI * 2) / 40;
3
3
  const round2 = (x) => Math.round(x * 100) / 100;
4
4
  const round1 = (x) => Math.round(x * 10) / 10;
5
- export const linear = (amp, center, from, to, time, cutoffTo) => {
5
+ export const linear = (amp, center, from, to, time, softTo) => {
6
6
  time = time * frequencyT;
7
7
  time %= Math.PI * 2;
8
8
  if (from >= to)
9
9
  return "";
10
10
  let path = "";
11
11
  for (let xIterator = from; xIterator <= to; xIterator += 0.5) {
12
- const x = cutoffTo ? Math.min(cutoffTo, xIterator) : xIterator;
12
+ const x = softTo ? Math.min(softTo, xIterator) : xIterator;
13
13
  const sinV = Math.sin(x * frequencyX + time);
14
14
  const y = sinV * amp + center;
15
15
  if (x == from) {
@@ -1,7 +1,7 @@
1
1
  export const addBadge = (icon, n) => {
2
2
  const { width, height } = icon;
3
3
  if (!width || !height)
4
- throw new Error("Icon must have icon and height");
4
+ throw new Error("Icon must have width and height");
5
5
  let badge;
6
6
  if (n) {
7
7
  const x = width - 12;
@@ -21,7 +21,7 @@ export const addBadge = (icon, n) => {
21
21
  "border-radius:var(--m3-shape-full)",
22
22
  ].join(";")}">` +
23
23
  text +
24
- `</div>`;
24
+ `</div></foreignObject>`;
25
25
  }
26
26
  else {
27
27
  badge = `<circle cx="${width - 3}" cy="3" r="3" fill="var(--m3c-error)"/>`;
@@ -1,4 +1,13 @@
1
1
  import { MaterialDynamicColors, DynamicColor } from "@ktibow/material-color-utilities-nightly";
2
2
  export declare const materialColors: MaterialDynamicColors;
3
+ export declare const primaryContainerSubtle: DynamicColor;
4
+ export declare const onPrimaryContainerSubtle: DynamicColor;
5
+ export declare const secondaryContainerSubtle: DynamicColor;
6
+ export declare const onSecondaryContainerSubtle: DynamicColor;
7
+ export declare const tertiaryContainerSubtle: DynamicColor;
8
+ export declare const onTertiaryContainerSubtle: DynamicColor;
9
+ export declare const errorContainerSubtle: DynamicColor;
10
+ export declare const onErrorContainerSubtle: DynamicColor;
3
11
  export declare const colors: DynamicColor[];
12
+ /** @deprecated */
4
13
  export declare const pairs: DynamicColor[][];
@@ -1,6 +1,6 @@
1
1
  import { MaterialDynamicColors, DynamicColor, ContrastCurve, } from "@ktibow/material-color-utilities-nightly";
2
2
  export const materialColors = new MaterialDynamicColors();
3
- // Custom color role used for switch
3
+ // Generates on-on-primary for Switch, read more: https://ktibow.github.io/blog/humanresearch/ononprimary/
4
4
  const onOnPrimary = DynamicColor.fromPalette({
5
5
  name: "on_on_primary",
6
6
  palette: (s) => s.primaryPalette,
@@ -8,59 +8,60 @@ const onOnPrimary = DynamicColor.fromPalette({
8
8
  contrastCurve: () => new ContrastCurve(6, 6, 7, 11),
9
9
  });
10
10
  // Custom colors for you to use
11
- const primaryContainerSubtle = DynamicColor.fromPalette({
11
+ export const primaryContainerSubtle = DynamicColor.fromPalette({
12
12
  name: "primary_container_subtle",
13
13
  palette: (s) => s.primaryPalette,
14
14
  isBackground: true,
15
15
  background: (s) => materialColors.highestSurface(s),
16
16
  contrastCurve: () => undefined,
17
17
  });
18
- const onPrimaryContainerSubtle = DynamicColor.fromPalette({
18
+ export const onPrimaryContainerSubtle = DynamicColor.fromPalette({
19
19
  name: "on_primary_container_subtle",
20
20
  palette: (s) => s.primaryPalette,
21
21
  background: () => primaryContainerSubtle,
22
22
  contrastCurve: () => new ContrastCurve(6, 6, 7, 11),
23
23
  });
24
- const secondaryContainerSubtle = DynamicColor.fromPalette({
24
+ export const secondaryContainerSubtle = DynamicColor.fromPalette({
25
25
  name: "secondary_container_subtle",
26
26
  palette: (s) => s.secondaryPalette,
27
27
  isBackground: true,
28
28
  background: (s) => materialColors.highestSurface(s),
29
29
  contrastCurve: () => undefined,
30
30
  });
31
- const onSecondaryContainerSubtle = DynamicColor.fromPalette({
31
+ export const onSecondaryContainerSubtle = DynamicColor.fromPalette({
32
32
  name: "on_secondary_container_subtle",
33
33
  palette: (s) => s.secondaryPalette,
34
34
  background: () => secondaryContainerSubtle,
35
35
  contrastCurve: () => new ContrastCurve(6, 6, 7, 11),
36
36
  });
37
- const tertiaryContainerSubtle = DynamicColor.fromPalette({
37
+ export const tertiaryContainerSubtle = DynamicColor.fromPalette({
38
38
  name: "tertiary_container_subtle",
39
39
  palette: (s) => s.tertiaryPalette,
40
40
  isBackground: true,
41
41
  background: (s) => materialColors.highestSurface(s),
42
42
  contrastCurve: () => undefined,
43
43
  });
44
- const onTertiaryContainerSubtle = DynamicColor.fromPalette({
44
+ export const onTertiaryContainerSubtle = DynamicColor.fromPalette({
45
45
  name: "on_tertiary_container_subtle",
46
46
  palette: (s) => s.tertiaryPalette,
47
47
  background: () => tertiaryContainerSubtle,
48
48
  contrastCurve: () => new ContrastCurve(6, 6, 7, 11),
49
49
  });
50
- const errorContainerSubtle = DynamicColor.fromPalette({
50
+ export const errorContainerSubtle = DynamicColor.fromPalette({
51
51
  name: "error_container_subtle",
52
52
  palette: (s) => s.errorPalette,
53
53
  isBackground: true,
54
54
  background: (s) => materialColors.highestSurface(s),
55
55
  contrastCurve: () => undefined,
56
56
  });
57
- const onErrorContainerSubtle = DynamicColor.fromPalette({
57
+ export const onErrorContainerSubtle = DynamicColor.fromPalette({
58
58
  name: "on_error_container_subtle",
59
59
  palette: (s) => s.errorPalette,
60
60
  background: () => errorContainerSubtle,
61
61
  contrastCurve: () => new ContrastCurve(6, 6, 7, 11),
62
62
  });
63
63
  export const colors = [
64
+ // background is deprecated, need to move filtering it out from utils.ts to here
64
65
  ...materialColors.allColors,
65
66
  materialColors.shadow(),
66
67
  materialColors.scrim(),
@@ -74,6 +75,7 @@ export const colors = [
74
75
  errorContainerSubtle,
75
76
  onErrorContainerSubtle,
76
77
  ];
78
+ /** @deprecated */
77
79
  export const pairs = [
78
80
  [materialColors.primary(), materialColors.onPrimary()],
79
81
  [materialColors.primaryContainer(), materialColors.onPrimaryContainer()],
@@ -1,7 +1,8 @@
1
1
  // TODO: update for Expressive
2
2
  const createBezierLUT = (points, pointCount = 100) => {
3
3
  const lut = [];
4
- for (let t = 0; t < 1; t += 1 / pointCount) {
4
+ for (let n = 0; n <= pointCount; n++) {
5
+ const t = n / pointCount;
5
6
  const a = (1 - t) * (1 - t) * (1 - t);
6
7
  const b = (1 - t) * (1 - t) * t;
7
8
  const c = (1 - t) * t * t;
@@ -16,9 +17,9 @@ const createEase = (lutOptions) => {
16
17
  let lut;
17
18
  return (t) => {
18
19
  if (!lut)
19
- lut = lutOptions.map((args) => createBezierLUT(args)).flat();
20
+ lut = lutOptions.flatMap((args) => createBezierLUT(args));
20
21
  const closestPoint = lut.find((p) => p[0] >= t);
21
- const closestY = closestPoint ? closestPoint[1] : 1;
22
+ const closestY = closestPoint[1];
22
23
  return closestY;
23
24
  };
24
25
  };
@@ -32,7 +32,7 @@
32
32
  }
33
33
 
34
34
  [placeholder]::placeholder {
35
- color: --translucent(var(--m3c-on-background), 0.5);
35
+ color: --translucent(var(--m3c-on-surface), 0.5);
36
36
  opacity: 1;
37
37
  }
38
38
  ::selection {
@@ -0,0 +1,3 @@
1
+ import { type Preset } from "unocss";
2
+ declare const _default: () => Preset<object>;
3
+ export default _default;
@@ -0,0 +1,85 @@
1
+ import { symbols } from "unocss";
2
+ import { colors } from "./colors";
3
+ const easings = ["-fast-spatial", "-spatial", "-slow-spatial", "-fast", "", "-slow"];
4
+ const fontClasses = [
5
+ "display-large",
6
+ "display-medium",
7
+ "display-small",
8
+ "headline-large",
9
+ "headline-medium",
10
+ "headline-small",
11
+ "title-large",
12
+ "title-medium",
13
+ "title-small",
14
+ "label-large",
15
+ "label-medium",
16
+ "label-small",
17
+ "body-large",
18
+ "body-medium",
19
+ "body-small",
20
+ ];
21
+ const preset = {
22
+ name: "m3-svelte",
23
+ theme: {
24
+ breakpoints: {
25
+ m: "37.5rem", // ≅ sm
26
+ x: "52.5rem", // expanded; ≅ md
27
+ l: "75rem", // ≅ lg, xl
28
+ xl: "100rem", // ≅ 2xl
29
+ },
30
+ container: {
31
+ maxWidth: {
32
+ m: "37.5rem", // ≅ sm
33
+ x: "52.5rem", // expanded; ≅ md
34
+ l: "75rem", // ≅ lg, xl
35
+ xl: "100rem",
36
+ },
37
+ },
38
+ spacing: {
39
+ DEFAULT: "0.25rem",
40
+ },
41
+ fontFamily: {
42
+ sans: "var(--m3-font)",
43
+ mono: "var(--m3-font-mono)",
44
+ },
45
+ boxShadow: {
46
+ "1": "var(--m3-elevation-1)",
47
+ "2": "var(--m3-elevation-2)",
48
+ "3": "var(--m3-elevation-3)",
49
+ "4": "var(--m3-elevation-4)",
50
+ "5": "var(--m3-elevation-5)",
51
+ },
52
+ borderRadius: {
53
+ xs: "var(--m3-shape-extra-small)", // 4px
54
+ sm: "var(--m3-shape-small)", // 8px
55
+ md: "var(--m3-shape-medium)", // 12px
56
+ lg: "var(--m3-shape-large)", // 16px
57
+ xl: "var(--m3-shape-extra-large)", // 28px
58
+ },
59
+ colors: {
60
+ ...Object.fromEntries(colors.map((c) => [
61
+ c.name.replaceAll("_", "-"),
62
+ `var(--m3c-${c.name.replaceAll("_", "-")})`,
63
+ ])),
64
+ "v-background": "var(--m3v-background)",
65
+ },
66
+ },
67
+ rules: [
68
+ ...easings.map((e) => [
69
+ `transition${e}`,
70
+ {
71
+ "transition-timing-function": `var(--m3-timing-function${e})`,
72
+ "transition-duration": `var(--m3-duration${e})`,
73
+ },
74
+ ]),
75
+ ...fontClasses.map((c) => [`m3-font-${c}`, { [symbols.body]: `@apply --m3-${c}` }]),
76
+ ],
77
+ preflights: [
78
+ {
79
+ getCSS: () => `:root {
80
+ box-sizing: border-box;
81
+ }`,
82
+ },
83
+ ],
84
+ };
85
+ export default () => preset;
@@ -1,6 +1,3 @@
1
1
  import { type DynamicScheme } from "@ktibow/material-color-utilities-nightly";
2
- /**
3
- * @returns A string of CSS code with custom properties representing the color scheme values.
4
- * */
5
- export declare const genCSS: (light: DynamicScheme, dark: DynamicScheme) => string;
2
+ export declare const genCSS: (light: DynamicScheme, dark: DynamicScheme, cs?: import("@ktibow/material-color-utilities-nightly").DynamicColor[]) => string;
6
3
  export declare const parseSize: (size: string) => number;
@@ -1,18 +1,16 @@
1
1
  import {} from "@ktibow/material-color-utilities-nightly";
2
2
  import { colors } from "./colors";
3
- /**
4
- * @returns A string of CSS code with custom properties representing the color scheme values.
5
- * */
6
- export const genCSS = (light, dark) => {
3
+ // default cs value is deprecated
4
+ export const genCSS = (light, dark, cs = colors) => {
7
5
  const genColorVariable = (name, argb) => {
8
6
  const kebabCase = name.replaceAll("_", "-");
9
7
  const hex = argb.toString(16).slice(-6);
10
8
  return ` --m3c-${kebabCase}: #${hex};`;
11
9
  };
12
- const lightColors = colors
10
+ const lightColors = cs
13
11
  .map((color) => genColorVariable(color.name, color.getArgb(light)))
14
12
  .join("\n");
15
- const darkColors = colors
13
+ const darkColors = cs
16
14
  .map((color) => genColorVariable(color.name, color.getArgb(dark)))
17
15
  .join("\n");
18
16
  return `@media (prefers-color-scheme: light) {
@@ -20,6 +20,7 @@
20
20
  }[];
21
21
  } & HTMLAnchorAttributes = $props();
22
22
 
23
+ // svelte-ignore state_referenced_locally
23
24
  let prevTab = $state(tab);
24
25
  let wrapper: HTMLDivElement | undefined = $state();
25
26
  $effect(() => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "m3-svelte",
3
- "version": "6.0.2",
3
+ "version": "6.0.4",
4
4
  "license": "Apache-2.0 OR GPL-3.0-only",
5
5
  "repository": "KTibow/m3-svelte",
6
6
  "author": {
@@ -29,6 +29,10 @@
29
29
  },
30
30
  "./misc/tailwind-styles.css": {
31
31
  "style": "./package/misc/tailwind-styles.css"
32
+ },
33
+ "./misc/unocss-preset": {
34
+ "types": "./package/misc/unocss-preset.d.ts",
35
+ "import": "./package/misc/unocss-preset.js"
32
36
  }
33
37
  },
34
38
  "dependencies": {
@@ -38,29 +42,28 @@
38
42
  },
39
43
  "devDependencies": {
40
44
  "@eslint/compat": "^2.0.0",
41
- "@eslint/js": "^9.39.1",
45
+ "@eslint/js": "^9.39.2",
42
46
  "@sveltejs/adapter-static": "^3.0.10",
43
47
  "@sveltejs/kit": "^2.49.1",
44
48
  "@sveltejs/package": "^2.5.7",
45
49
  "@sveltejs/vite-plugin-svelte": "^6.2.1",
46
- "@types/flubber": "^0.4.0",
47
- "@types/node": "^24.10.1",
50
+ "@types/node": "^25.0.3",
48
51
  "eslint": "^9.39.1",
49
52
  "eslint-config-prettier": "^10.1.8",
50
53
  "eslint-plugin-svelte": "^3.13.1",
51
- "flubber": "^0.4.2",
52
54
  "globals": "^16.5.0",
53
55
  "m3-svelte": "link:",
54
56
  "prettier": "^3.7.4",
55
- "prettier-plugin-svelte": "^3.4.0",
56
- "publint": "^0.3.15",
57
- "svelte": "^5.45.6",
58
- "svelte-check": "^4.3.4",
57
+ "prettier-plugin-svelte": "^3.4.1",
58
+ "publint": "^0.3.16",
59
+ "svelte": "^5.46.0",
60
+ "svelte-check": "^4.3.5",
59
61
  "svelte-highlight": "^7.9.0",
60
62
  "tinyglobby": "^0.2.15",
61
63
  "typescript": "^5.9.3",
62
- "typescript-eslint": "^8.49.0",
63
- "vite": "^7.2.7",
64
+ "typescript-eslint": "^8.50.1",
65
+ "unocss": "^66.5.10",
66
+ "vite": "^7.3.0",
64
67
  "vite-plugin-functions-mixins": "^0.4.0",
65
68
  "vite-plugin-token-shaker": "^0.0.3"
66
69
  },