@symblight/md-colors 0.1.0 → 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 +72 -25
- package/dist/cli.js +157 -0
- package/dist/client.cjs +6492 -0
- package/dist/client.cjs.map +7 -0
- package/dist/client.esm.js +98 -673
- package/dist/client.esm.js.map +4 -4
- package/dist/client.js +3 -3
- package/dist/client.js.map +4 -4
- package/dist/index.js +108 -20
- package/dist/index.js.map +3 -3
- package/package.json +9 -4
package/README.md
CHANGED
|
@@ -10,44 +10,61 @@ Built on [`@material/material-color-utilities`](https://github.com/material-foun
|
|
|
10
10
|
npm install @symblight/md-colors
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
-
##
|
|
13
|
+
## Builds
|
|
14
14
|
|
|
15
15
|
| Build | Entry | Format | Use case |
|
|
16
16
|
|---|---|---|---|
|
|
17
|
-
| Node / bundler | `dist/index.js` | ESM |
|
|
18
|
-
| Browser (bundler) | `dist/client.esm.js` | ESM | Vite, Storybook
|
|
19
|
-
| Browser (
|
|
17
|
+
| Node / bundler | `dist/index.js` | ESM | `generateTokens`, `generateCSSFile` |
|
|
18
|
+
| Browser (bundler) | `dist/client.esm.js` | ESM | Vite, Storybook — named import |
|
|
19
|
+
| Browser (CJS) | `dist/client.cjs` | CJS | `require("@symblight/md-colors/client")` |
|
|
20
|
+
| Browser (script) | `dist/client.js` | IIFE | Plain `<script>` tag — `window.MdColors` |
|
|
21
|
+
| CLI | `dist/cli.js` | ESM | Terminal usage via `md-colors` |
|
|
20
22
|
|
|
21
23
|
---
|
|
22
24
|
|
|
23
25
|
## Node — programmatic API
|
|
24
26
|
|
|
25
|
-
Import `generateTokens` to build token maps in Node.js scripts or server-side tooling.
|
|
26
|
-
|
|
27
27
|
```js
|
|
28
|
-
import { generateTokens } from "@symblight/md-colors";
|
|
28
|
+
import { generateTokens, generateCSSFile } from "@symblight/md-colors";
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### `generateTokens(config)`
|
|
32
|
+
|
|
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`.
|
|
29
38
|
|
|
39
|
+
```js
|
|
30
40
|
const tokens = generateTokens({ sourceColor: "#6750A4", scheme: "dark" });
|
|
31
41
|
// { "--md-sys-color-primary": "oklch(…)", … }
|
|
32
42
|
```
|
|
33
43
|
|
|
34
|
-
### `generateTokens(config)`
|
|
35
|
-
|
|
36
44
|
| Param | Type | Default | Description |
|
|
37
45
|
|---|---|---|---|
|
|
38
46
|
| `config.sourceColor` | `string` | `"#1D5D78"` | Seed color as a hex string |
|
|
39
47
|
| `config.scheme` | `"light" \| "dark"` | `"light"` | Color scheme variant |
|
|
40
48
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
### Generate a CSS file
|
|
49
|
+
### `generateCSSFile(config)`
|
|
44
50
|
|
|
45
|
-
|
|
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.
|
|
46
53
|
|
|
47
|
-
```
|
|
48
|
-
|
|
54
|
+
```js
|
|
55
|
+
await generateCSSFile({
|
|
56
|
+
sourceColor: "#6750A4",
|
|
57
|
+
scheme: "dark",
|
|
58
|
+
output: "./theme/colors.css",
|
|
59
|
+
});
|
|
49
60
|
```
|
|
50
61
|
|
|
62
|
+
| Param | Type | Default | Description |
|
|
63
|
+
|---|---|---|---|
|
|
64
|
+
| `config.sourceColor` | `string` | `"#1D5D78"` | Seed color as a hex string |
|
|
65
|
+
| `config.scheme` | `"light" \| "dark"` | `"light"` | Color scheme variant |
|
|
66
|
+
| `config.output` | `string` | `./colors.css` (next to module) | Output file path |
|
|
67
|
+
|
|
51
68
|
Output:
|
|
52
69
|
|
|
53
70
|
```css
|
|
@@ -59,47 +76,77 @@ Output:
|
|
|
59
76
|
}
|
|
60
77
|
```
|
|
61
78
|
|
|
62
|
-
Source color and scheme are configured directly in `create-theme-file.mjs`.
|
|
63
|
-
|
|
64
79
|
---
|
|
65
80
|
|
|
66
81
|
## Browser client — runtime theming
|
|
67
82
|
|
|
68
|
-
|
|
83
|
+
Import `generateTheme` and call it directly — no globals, no window assignment.
|
|
69
84
|
|
|
70
|
-
###
|
|
85
|
+
### Named import (Vite, Storybook, bundler)
|
|
71
86
|
|
|
72
87
|
```js
|
|
73
|
-
import "@symblight/md-colors/client";
|
|
88
|
+
import { generateTheme } from "@symblight/md-colors/client";
|
|
74
89
|
|
|
75
|
-
|
|
90
|
+
generateTheme({ sourceColor: "#6750A4", scheme: "dark" });
|
|
76
91
|
```
|
|
77
92
|
|
|
78
93
|
### Via `<script>` tag (no bundler)
|
|
79
94
|
|
|
95
|
+
```html
|
|
96
|
+
<script type="module">
|
|
97
|
+
import { generateTheme } from "./node_modules/@symblight/md-colors/dist/client.esm.js";
|
|
98
|
+
generateTheme({ sourceColor: "#6750A4", scheme: "dark" });
|
|
99
|
+
</script>
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Or with the self-contained IIFE build:
|
|
103
|
+
|
|
80
104
|
```html
|
|
81
105
|
<script src="node_modules/@symblight/md-colors/dist/client.js"></script>
|
|
82
106
|
<script>
|
|
83
|
-
|
|
107
|
+
MdColors.generateTheme({ sourceColor: "#6750A4", scheme: "dark" });
|
|
84
108
|
</script>
|
|
85
109
|
```
|
|
86
110
|
|
|
87
|
-
### `
|
|
111
|
+
### `generateTheme(options)`
|
|
88
112
|
|
|
89
113
|
| Param | Type | Default | Description |
|
|
90
114
|
|---|---|---|---|
|
|
91
|
-
| `options.sourceColor` | `string` |
|
|
115
|
+
| `options.sourceColor` | `string` | `"#1D5D78"` | Seed color as a hex string |
|
|
92
116
|
| `options.scheme` | `"light" \| "dark"` | `"light"` | Color scheme variant |
|
|
93
117
|
|
|
94
118
|
Sets every `--md-sys-color-*` variable on `document.documentElement` via `style.setProperty`.
|
|
95
119
|
|
|
96
120
|
---
|
|
97
121
|
|
|
122
|
+
## CLI
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
# npx (no install)
|
|
126
|
+
npx md-colors --sourceColor="#6750A4" --scheme=dark --output=./theme/colors.css
|
|
127
|
+
|
|
128
|
+
# global install
|
|
129
|
+
npm install -g @symblight/md-colors
|
|
130
|
+
md-colors --sourceColor="#6750A4" --scheme=light --output=./colors.css
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### Options
|
|
134
|
+
|
|
135
|
+
| Flag | Short | Default | Description |
|
|
136
|
+
|---|---|---|---|
|
|
137
|
+
| `--sourceColor` | `-c` | `#1D5D78` | Seed color as a hex string |
|
|
138
|
+
| `--scheme` | `-s` | `light` | `light` or `dark` |
|
|
139
|
+
| `--output` | `-o` | `./colors.css` | Output file path |
|
|
140
|
+
|
|
141
|
+
Exits with code 1 on an invalid color, invalid scheme or failed write.
|
|
142
|
+
|
|
143
|
+
---
|
|
144
|
+
|
|
98
145
|
## Development
|
|
99
146
|
|
|
100
147
|
```bash
|
|
101
148
|
pnpm build # Build all dist outputs
|
|
102
|
-
pnpm generate-theme # Write colors.css
|
|
149
|
+
pnpm generate-theme # Write colors.css via CLI (uses defaults)
|
|
103
150
|
pnpm test # Run Jest tests
|
|
104
151
|
```
|
|
105
152
|
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// cli.mjs
|
|
4
|
+
import { parseArgs } from "node:util";
|
|
5
|
+
|
|
6
|
+
// create-theme-file.mjs
|
|
7
|
+
import path from "node:path";
|
|
8
|
+
import fs from "node:fs/promises";
|
|
9
|
+
import { fileURLToPath } from "node:url";
|
|
10
|
+
|
|
11
|
+
// tokens.mjs
|
|
12
|
+
import {
|
|
13
|
+
argbFromHex,
|
|
14
|
+
hexFromArgb,
|
|
15
|
+
MaterialDynamicColors,
|
|
16
|
+
SchemeTonalSpot,
|
|
17
|
+
Hct
|
|
18
|
+
} from "@material/material-color-utilities";
|
|
19
|
+
import { converter, formatCss } from "culori";
|
|
20
|
+
var oklch = converter("oklch");
|
|
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 = "") {
|
|
90
|
+
let result = {};
|
|
91
|
+
for (const [key, value] of Object.entries(scheme)) {
|
|
92
|
+
const token = key.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase();
|
|
93
|
+
const color = hexFromArgb(value);
|
|
94
|
+
const converted = oklch(color);
|
|
95
|
+
const name = formatCss(converted);
|
|
96
|
+
const variableName = `--md-sys-color-${token}${suffix}`;
|
|
97
|
+
result[variableName] = name;
|
|
98
|
+
}
|
|
99
|
+
return result;
|
|
100
|
+
}
|
|
101
|
+
function generateTokens(config = {}) {
|
|
102
|
+
const { sourceColor, scheme } = resolveConfig(config);
|
|
103
|
+
const dynamicScheme = new SchemeTonalSpot(
|
|
104
|
+
Hct.fromInt(argbFromHex(sourceColor)),
|
|
105
|
+
scheme === "dark",
|
|
106
|
+
0
|
|
107
|
+
);
|
|
108
|
+
let palette = {};
|
|
109
|
+
for (const role of COLOR_ROLES) {
|
|
110
|
+
palette[role] = MaterialDynamicColors[role].getArgb(dynamicScheme);
|
|
111
|
+
}
|
|
112
|
+
return setSchemeProperties(palette);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// create-theme-file.mjs
|
|
116
|
+
function template(variables) {
|
|
117
|
+
return `:root {
|
|
118
|
+
${variables}
|
|
119
|
+
}`;
|
|
120
|
+
}
|
|
121
|
+
function normalizeCSSVariablesContent(variables = {}) {
|
|
122
|
+
let str = "";
|
|
123
|
+
for (const cssVar in variables) {
|
|
124
|
+
str += `${cssVar}: ${variables[cssVar]};
|
|
125
|
+
`;
|
|
126
|
+
}
|
|
127
|
+
return str;
|
|
128
|
+
}
|
|
129
|
+
async function generateCSSFile(config = {}) {
|
|
130
|
+
const { output: output2, ...tokenConfig } = config;
|
|
131
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
132
|
+
const __dirname = path.dirname(__filename);
|
|
133
|
+
const outputPath = output2 ? path.resolve(output2) : path.join(__dirname, "colors.css");
|
|
134
|
+
const tokens = generateTokens(tokenConfig);
|
|
135
|
+
const stringTokens = normalizeCSSVariablesContent(tokens);
|
|
136
|
+
const themeBody = template(stringTokens);
|
|
137
|
+
await fs.writeFile(outputPath, themeBody);
|
|
138
|
+
return outputPath;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// cli.mjs
|
|
142
|
+
var { values } = parseArgs({
|
|
143
|
+
options: {
|
|
144
|
+
sourceColor: { type: "string", short: "c", default: DEFAULT_SOURCE_COLOR },
|
|
145
|
+
scheme: { type: "string", short: "s", default: "light" },
|
|
146
|
+
output: { type: "string", short: "o", default: "./colors.css" }
|
|
147
|
+
}
|
|
148
|
+
});
|
|
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}`);
|
|
156
|
+
process.exit(1);
|
|
157
|
+
}
|