@symblight/md-colors 0.1.1 → 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.
package/README.md CHANGED
@@ -16,7 +16,8 @@ npm install @symblight/md-colors
16
16
  |---|---|---|---|
17
17
  | Node / bundler | `dist/index.js` | ESM | `generateTokens`, `generateCSSFile` |
18
18
  | Browser (bundler) | `dist/client.esm.js` | ESM | Vite, Storybook — named import |
19
- | Browser (script) | `dist/client.js` | IIFE | Plain `<script>` tag |
19
+ | Browser (CJS) | `dist/client.cjs` | CJS | `require("@symblight/md-colors/client")` |
20
+ | Browser (script) | `dist/client.js` | IIFE | Plain `<script>` tag — `window.MdColors` |
20
21
  | CLI | `dist/cli.js` | ESM | Terminal usage via `md-colors` |
21
22
 
22
23
  ---
@@ -29,7 +30,11 @@ import { generateTokens, generateCSSFile } from "@symblight/md-colors";
29
30
 
30
31
  ### `generateTokens(config)`
31
32
 
32
- Returns a token map — use it to apply tokens however you like.
33
+ Returns a token map — use it to apply tokens however you like. All 49 MD3
34
+ color roles are resolved from one tonal-spot scheme (the M3 default), including
35
+ `surface-container`, `surface-tint` and the `*-fixed` / `*-fixed-dim` /
36
+ `on-*-fixed` / `on-*-fixed-variant` roles. Missing config fields fall back to
37
+ their defaults; an invalid `sourceColor` or `scheme` throws a `TypeError`.
33
38
 
34
39
  ```js
35
40
  const tokens = generateTokens({ sourceColor: "#6750A4", scheme: "dark" });
@@ -43,10 +48,11 @@ const tokens = generateTokens({ sourceColor: "#6750A4", scheme: "dark" });
43
48
 
44
49
  ### `generateCSSFile(config)`
45
50
 
46
- Writes a `colors.css` file with `:root`-scoped tokens.
51
+ Writes a `colors.css` file with `:root`-scoped tokens. Returns a promise that
52
+ resolves with the absolute output path and rejects if the write fails.
47
53
 
48
54
  ```js
49
- generateCSSFile({
55
+ await generateCSSFile({
50
56
  sourceColor: "#6750A4",
51
57
  scheme: "dark",
52
58
  output: "./theme/colors.css",
@@ -98,7 +104,7 @@ Or with the self-contained IIFE build:
98
104
  ```html
99
105
  <script src="node_modules/@symblight/md-colors/dist/client.js"></script>
100
106
  <script>
101
- // client.js exposes nothing on window — use it as an ES module instead (see above)
107
+ MdColors.generateTheme({ sourceColor: "#6750A4", scheme: "dark" });
102
108
  </script>
103
109
  ```
104
110
 
@@ -106,7 +112,7 @@ Or with the self-contained IIFE build:
106
112
 
107
113
  | Param | Type | Default | Description |
108
114
  |---|---|---|---|
109
- | `options.sourceColor` | `string` | — | Seed color as a hex string |
115
+ | `options.sourceColor` | `string` | `"#1D5D78"` | Seed color as a hex string |
110
116
  | `options.scheme` | `"light" \| "dark"` | `"light"` | Color scheme variant |
111
117
 
112
118
  Sets every `--md-sys-color-*` variable on `document.documentElement` via `style.setProperty`.
@@ -132,6 +138,8 @@ md-colors --sourceColor="#6750A4" --scheme=light --output=./colors.css
132
138
  | `--scheme` | `-s` | `light` | `light` or `dark` |
133
139
  | `--output` | `-o` | `./colors.css` | Output file path |
134
140
 
141
+ Exits with code 1 on an invalid color, invalid scheme or failed write.
142
+
135
143
  ---
136
144
 
137
145
  ## Development
package/dist/cli.js CHANGED
@@ -1,36 +1,94 @@
1
1
  #!/usr/bin/env node
2
- #!/usr/bin/env node
3
2
 
4
3
  // cli.mjs
5
4
  import { parseArgs } from "node:util";
6
5
 
7
6
  // create-theme-file.mjs
8
7
  import path from "node:path";
9
- import fs from "node:fs";
8
+ import fs from "node:fs/promises";
10
9
  import { fileURLToPath } from "node:url";
11
10
 
12
11
  // tokens.mjs
13
12
  import {
14
13
  argbFromHex,
15
- themeFromSourceColor,
16
14
  hexFromArgb,
17
15
  MaterialDynamicColors,
18
- SchemeContent,
16
+ SchemeTonalSpot,
19
17
  Hct
20
18
  } from "@material/material-color-utilities";
21
19
  import { converter, formatCss } from "culori";
22
20
  var oklch = converter("oklch");
23
- var materialColors = {
24
- "surface-container-highest": MaterialDynamicColors.surfaceContainerHighest,
25
- "surface-container-high": MaterialDynamicColors.surfaceContainerHigh,
26
- "surface-dim": MaterialDynamicColors.surfaceDim,
27
- "surface-bright": MaterialDynamicColors.surfaceBright,
28
- "surface-container-lowest": MaterialDynamicColors.surfaceContainerLowest,
29
- "surface-container-low": MaterialDynamicColors.surfaceContainerLow
30
- };
31
- function setSchemeProperties(scheme2, suffix = "") {
21
+ var DEFAULT_SOURCE_COLOR = "#1D5D78";
22
+ var HEX_RE = /^#(?:[0-9a-f]{3}|[0-9a-f]{6})$/i;
23
+ var COLOR_ROLES = (
24
+ /** @type {const} */
25
+ [
26
+ "primary",
27
+ "onPrimary",
28
+ "primaryContainer",
29
+ "onPrimaryContainer",
30
+ "inversePrimary",
31
+ "primaryFixed",
32
+ "primaryFixedDim",
33
+ "onPrimaryFixed",
34
+ "onPrimaryFixedVariant",
35
+ "secondary",
36
+ "onSecondary",
37
+ "secondaryContainer",
38
+ "onSecondaryContainer",
39
+ "secondaryFixed",
40
+ "secondaryFixedDim",
41
+ "onSecondaryFixed",
42
+ "onSecondaryFixedVariant",
43
+ "tertiary",
44
+ "onTertiary",
45
+ "tertiaryContainer",
46
+ "onTertiaryContainer",
47
+ "tertiaryFixed",
48
+ "tertiaryFixedDim",
49
+ "onTertiaryFixed",
50
+ "onTertiaryFixedVariant",
51
+ "error",
52
+ "onError",
53
+ "errorContainer",
54
+ "onErrorContainer",
55
+ "surface",
56
+ "onSurface",
57
+ "surfaceVariant",
58
+ "onSurfaceVariant",
59
+ "surfaceDim",
60
+ "surfaceBright",
61
+ "surfaceContainerLowest",
62
+ "surfaceContainerLow",
63
+ "surfaceContainer",
64
+ "surfaceContainerHigh",
65
+ "surfaceContainerHighest",
66
+ "surfaceTint",
67
+ "inverseSurface",
68
+ "inverseOnSurface",
69
+ "outline",
70
+ "outlineVariant",
71
+ "shadow",
72
+ "scrim",
73
+ "background",
74
+ "onBackground"
75
+ ]
76
+ );
77
+ function resolveConfig({ sourceColor = DEFAULT_SOURCE_COLOR, scheme = "light" } = {}) {
78
+ if (typeof sourceColor !== "string" || !HEX_RE.test(sourceColor)) {
79
+ throw new TypeError(`sourceColor must be a hex color like "#6750A4", got ${JSON.stringify(sourceColor)}`);
80
+ }
81
+ if (scheme !== "light" && scheme !== "dark") {
82
+ throw new TypeError(`scheme must be "light" or "dark", got ${JSON.stringify(scheme)}`);
83
+ }
84
+ return { sourceColor, scheme: (
85
+ /** @type {ColorScheme} */
86
+ scheme
87
+ ) };
88
+ }
89
+ function setSchemeProperties(scheme, suffix = "") {
32
90
  let result = {};
33
- for (const [key, value] of Object.entries(scheme2)) {
91
+ for (const [key, value] of Object.entries(scheme)) {
34
92
  const token = key.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase();
35
93
  const color = hexFromArgb(value);
36
94
  const converted = oklch(color);
@@ -40,20 +98,18 @@ function setSchemeProperties(scheme2, suffix = "") {
40
98
  }
41
99
  return result;
42
100
  }
43
- function generateTokens(config = { scheme: "light", sourceColor: "#1D5D78" }) {
44
- const theme = themeFromSourceColor(argbFromHex(config.sourceColor), []);
45
- const scheme2 = new SchemeContent(
46
- Hct.fromInt(argbFromHex(config.sourceColor)),
47
- config.scheme === "dark",
101
+ function generateTokens(config = {}) {
102
+ const { sourceColor, scheme } = resolveConfig(config);
103
+ const dynamicScheme = new SchemeTonalSpot(
104
+ Hct.fromInt(argbFromHex(sourceColor)),
105
+ scheme === "dark",
48
106
  0
49
107
  );
50
108
  let palette = {};
51
- for (const [key, value] of Object.entries(materialColors)) {
52
- palette[key] = value.getArgb(scheme2);
109
+ for (const role of COLOR_ROLES) {
110
+ palette[role] = MaterialDynamicColors[role].getArgb(dynamicScheme);
53
111
  }
54
- const currentTheme = theme.schemes[config.scheme].toJSON();
55
- const tokens = setSchemeProperties({ ...currentTheme, ...palette });
56
- return tokens;
112
+ return setSchemeProperties(palette);
57
113
  }
58
114
 
59
115
  // create-theme-file.mjs
@@ -70,33 +126,32 @@ function normalizeCSSVariablesContent(variables = {}) {
70
126
  }
71
127
  return str;
72
128
  }
73
- function generateCSSFile(config = {}) {
74
- const { scheme: scheme2 = "light", sourceColor: sourceColor2 = "#1D5D78", output: output2 } = config;
129
+ async function generateCSSFile(config = {}) {
130
+ const { output: output2, ...tokenConfig } = config;
75
131
  const __filename = fileURLToPath(import.meta.url);
76
132
  const __dirname = path.dirname(__filename);
77
133
  const outputPath = output2 ? path.resolve(output2) : path.join(__dirname, "colors.css");
78
- const tokens = generateTokens({ scheme: scheme2, sourceColor: sourceColor2 });
134
+ const tokens = generateTokens(tokenConfig);
79
135
  const stringTokens = normalizeCSSVariablesContent(tokens);
80
136
  const themeBody = template(stringTokens);
81
- fs.writeFile(outputPath, themeBody, (err) => {
82
- if (err) {
83
- console.error(err);
84
- }
85
- });
137
+ await fs.writeFile(outputPath, themeBody);
138
+ return outputPath;
86
139
  }
87
140
 
88
141
  // cli.mjs
89
142
  var { values } = parseArgs({
90
143
  options: {
91
- sourceColor: { type: "string", short: "c", default: "#1D5D78" },
144
+ sourceColor: { type: "string", short: "c", default: DEFAULT_SOURCE_COLOR },
92
145
  scheme: { type: "string", short: "s", default: "light" },
93
146
  output: { type: "string", short: "o", default: "./colors.css" }
94
147
  }
95
148
  });
96
- var { sourceColor, scheme, output } = values;
97
- if (scheme !== "light" && scheme !== "dark") {
98
- console.error(`Error: --scheme must be "light" or "dark", got "${scheme}"`);
149
+ var { output } = values;
150
+ try {
151
+ const { sourceColor, scheme } = resolveConfig(values);
152
+ const outputPath = await generateCSSFile({ sourceColor, scheme, output });
153
+ console.log(`Generated ${outputPath} (sourceColor: ${sourceColor}, scheme: ${scheme})`);
154
+ } catch (err) {
155
+ console.error(`Error: ${err instanceof Error ? err.message : err}`);
99
156
  process.exit(1);
100
157
  }
101
- generateCSSFile({ sourceColor, scheme, output });
102
- console.log(`Generated ${output} (sourceColor: ${sourceColor}, scheme: ${scheme})`);