@kyralabs/m3e-tokens 1.0.0 → 1.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/README.md CHANGED
@@ -35,6 +35,27 @@ or from the command line:
35
35
  npx m3e-theme --seed "#6750A4" --variant expressive > theme.css
36
36
  ```
37
37
 
38
+ Optional core colors replace the palettes the variant would derive from the seed, like Material
39
+ Theme Builder's: `secondary`, `tertiary`, `neutral`, `neutralVariant` and `error`. Each palette
40
+ keeps the given color's hue and chroma (`TonalPalette.fromInt`); the variant still maps the roles
41
+ to tones. For example, a red primary on grey surfaces:
42
+
43
+ ```ts
44
+ createTheme({
45
+ seed: '#d84848',
46
+ variant: 'fidelity',
47
+ coreColors: { neutral: '#808080', neutralVariant: '#808080' },
48
+ });
49
+ ```
50
+
51
+ ```sh
52
+ npx m3e-theme --seed "#d84848" --variant fidelity --neutral "#808080" --neutral-variant "#808080"
53
+ ```
54
+
55
+ The CLI flags are `--secondary`, `--tertiary`, `--neutral`, `--neutral-variant` and `--error`.
56
+ With spec 2025 (the `tonalSpot`, `neutral`, `vibrant` and `expressive` variants) the outline and
57
+ surface-variant roles come from the neutral palette, so `neutralVariant` has no effect there.
58
+
38
59
  Other entries: `./components/<name>.css` (component tokens), `./material-symbols.css` (Rounded;
39
60
  `-outlined` and `-sharp` too), `./fonts-full.css` (every Google Sans Flex axis), and JS token
40
61
  objects from `.` and `./components`.
package/dist/cli/theme.js CHANGED
@@ -5089,13 +5089,55 @@ var COLOR_ROLES = Object.getOwnPropertyNames(
5089
5089
  var rolesFor = (specVersion) => specVersion === "2025" ? COLOR_ROLES : COLOR_ROLES.filter((r) => !SPEC_2025_ONLY_ROLES.includes(r));
5090
5090
  var HEX = /^#(?:[0-9a-f]{3}|[0-9a-f]{6})$/i;
5091
5091
  var normalizeHex = (hex) => hexFromArgb(argbFromHex(hex));
5092
- function schemeColors(seed, variant, isDark, contrast, specVersion) {
5093
- const scheme = new SCHEMES[variant](
5092
+ var CORE_COLORS = ["secondary", "tertiary", "neutral", "neutralVariant", "error"];
5093
+ function normalizeCoreColors(coreColors, caller) {
5094
+ const out = {};
5095
+ for (const key of Object.keys(coreColors)) {
5096
+ if (!CORE_COLORS.includes(key)) {
5097
+ throw new Error(`${caller}: unknown core color "${key}"`);
5098
+ }
5099
+ }
5100
+ for (const key of CORE_COLORS) {
5101
+ const hex = coreColors[key];
5102
+ if (hex === void 0) continue;
5103
+ if (typeof hex !== "string" || !HEX.test(hex)) {
5104
+ throw new Error(
5105
+ `${caller}: core color ${key} must be #rgb or #rrggbb, got ${JSON.stringify(hex)}`
5106
+ );
5107
+ }
5108
+ out[key] = normalizeHex(hex);
5109
+ }
5110
+ return out;
5111
+ }
5112
+ function withCoreColors(scheme, coreColors) {
5113
+ if (CORE_COLORS.every((key) => coreColors[key] === void 0)) return scheme;
5114
+ const palette = (key) => {
5115
+ const hex = coreColors[key];
5116
+ return hex === void 0 ? void 0 : TonalPalette.fromInt(argbFromHex(hex));
5117
+ };
5118
+ return new DynamicScheme({
5119
+ sourceColorHct: scheme.sourceColorHct,
5120
+ variant: scheme.variant,
5121
+ contrastLevel: scheme.contrastLevel,
5122
+ isDark: scheme.isDark,
5123
+ platform: scheme.platform,
5124
+ specVersion: scheme.specVersion,
5125
+ primaryPalette: scheme.primaryPalette,
5126
+ secondaryPalette: palette("secondary") ?? scheme.secondaryPalette,
5127
+ tertiaryPalette: palette("tertiary") ?? scheme.tertiaryPalette,
5128
+ neutralPalette: palette("neutral") ?? scheme.neutralPalette,
5129
+ neutralVariantPalette: palette("neutralVariant") ?? scheme.neutralVariantPalette,
5130
+ errorPalette: palette("error") ?? scheme.errorPalette
5131
+ });
5132
+ }
5133
+ function schemeColors(seed, variant, isDark, contrast, specVersion, coreColors = {}) {
5134
+ const variantScheme = new SCHEMES[variant](
5094
5135
  Hct.fromInt(argbFromHex(seed)),
5095
5136
  isDark,
5096
5137
  contrast,
5097
5138
  specVersion
5098
5139
  );
5140
+ const scheme = withCoreColors(variantScheme, normalizeCoreColors(coreColors, "schemeColors"));
5099
5141
  const out = {};
5100
5142
  for (const role of rolesFor(specVersion)) {
5101
5143
  const method = dynamicColors[role];
@@ -5127,9 +5169,11 @@ function createTheme(options) {
5127
5169
  `createTheme: contrast must be within [-1, 1], got ${String(options.contrast)}`
5128
5170
  );
5129
5171
  }
5172
+ const coreColors = normalizeCoreColors(options.coreColors ?? {}, "createTheme");
5173
+ const hasCoreColors = Object.keys(coreColors).length > 0;
5130
5174
  const roles = rolesFor(specVersion);
5131
5175
  const build = (isDark, extra) => {
5132
- const colors = schemeColors(seed, variant, isDark, contrast, specVersion);
5176
+ const colors = schemeColors(seed, variant, isDark, contrast, specVersion, coreColors);
5133
5177
  for (const [role, hex] of Object.entries(extra ?? {})) {
5134
5178
  if (!roles.includes(role)) throw new Error(`createTheme: unknown color role "${role}"`);
5135
5179
  if (!HEX.test(hex)) throw new Error(`createTheme: override ${role} must be a hex color`);
@@ -5150,6 +5194,7 @@ function createTheme(options) {
5150
5194
  cssText,
5151
5195
  tokens: {
5152
5196
  seed: normalizeHex(seed),
5197
+ ...hasCoreColors ? { coreColors } : {},
5153
5198
  variant,
5154
5199
  contrast,
5155
5200
  specVersion,
@@ -5159,18 +5204,30 @@ function createTheme(options) {
5159
5204
  }
5160
5205
  };
5161
5206
  }
5162
-
5163
- // src/cli/theme.ts
5164
5207
  var USAGE = `usage: m3e-theme --seed <#hex> [options]
5165
5208
 
5166
- --variant ${VARIANTS.join(" | ")} (default tonalSpot)
5167
- --mode light | dark | system (default system)
5168
- --contrast -1\u20261 or ${Object.keys(CONTRAST).join(" | ")} (default standard)
5169
- --spec ${SPEC_VERSIONS.join(" | ")} (default 2025)
5170
- --motion expressive | standard (default expressive)
5171
- --selector single compound selector (default :root)
5172
- --out file to write (default stdout)`;
5173
- function run(argv) {
5209
+ --variant ${VARIANTS.join(" | ")} (default tonalSpot)
5210
+ --mode light | dark | system (default system)
5211
+ --contrast -1\u20261 or ${Object.keys(CONTRAST).join(" | ")} (default standard)
5212
+ --spec ${SPEC_VERSIONS.join(" | ")} (default 2025)
5213
+ --motion expressive | standard (default expressive)
5214
+ --selector single compound selector (default :root)
5215
+ --out file to write (default stdout)
5216
+
5217
+ Core colors (optional; each replaces the palette derived from --seed):
5218
+ --secondary <#hex>
5219
+ --tertiary <#hex>
5220
+ --neutral <#hex> surfaces and backgrounds
5221
+ --neutral-variant <#hex> surface variants and outlines
5222
+ --error <#hex>`;
5223
+ var CORE_COLOR_FLAGS = {
5224
+ secondary: "secondary",
5225
+ tertiary: "tertiary",
5226
+ neutral: "neutral",
5227
+ "neutral-variant": "neutralVariant",
5228
+ error: "error"
5229
+ };
5230
+ function parseThemeArgs(argv) {
5174
5231
  const { values } = parseArgs({
5175
5232
  args: argv,
5176
5233
  options: {
@@ -5182,16 +5239,24 @@ function run(argv) {
5182
5239
  motion: { type: "string" },
5183
5240
  selector: { type: "string" },
5184
5241
  out: { type: "string" },
5242
+ secondary: { type: "string" },
5243
+ tertiary: { type: "string" },
5244
+ neutral: { type: "string" },
5245
+ "neutral-variant": { type: "string" },
5246
+ error: { type: "string" },
5185
5247
  help: { type: "boolean", short: "h" }
5186
5248
  }
5187
5249
  });
5188
- if (values.help || !values.seed) {
5189
- console.error(USAGE);
5190
- return values.help ? 0 : 2;
5250
+ if (values.help || !values.seed) return { help: true, exitCode: values.help ? 0 : 2 };
5251
+ const coreColors = {};
5252
+ for (const [flag, key] of Object.entries(CORE_COLOR_FLAGS)) {
5253
+ const value = values[flag];
5254
+ if (value !== void 0) coreColors[key] = value;
5191
5255
  }
5192
5256
  const contrast = values.contrast;
5193
5257
  const options = {
5194
5258
  seed: values.seed,
5259
+ ...Object.keys(coreColors).length > 0 ? { coreColors } : {},
5195
5260
  ...values.variant ? { variant: values.variant } : {},
5196
5261
  ...values.mode ? { mode: values.mode } : {},
5197
5262
  ...contrast === void 0 ? {} : { contrast: contrast in CONTRAST ? contrast : Number(contrast) },
@@ -5199,8 +5264,18 @@ function run(argv) {
5199
5264
  ...values.motion ? { motionScheme: values.motion } : {},
5200
5265
  ...values.selector ? { selector: values.selector } : {}
5201
5266
  };
5202
- const { cssText } = createTheme(options);
5203
- if (values.out) writeFileSync(values.out, cssText);
5267
+ return { help: false, options, out: values.out };
5268
+ }
5269
+
5270
+ // src/cli/theme.ts
5271
+ function run(argv) {
5272
+ const args = parseThemeArgs(argv);
5273
+ if (args.help) {
5274
+ console.error(USAGE);
5275
+ return args.exitCode;
5276
+ }
5277
+ const { cssText } = createTheme(args.options);
5278
+ if (args.out) writeFileSync(args.out, cssText);
5204
5279
  else process.stdout.write(cssText);
5205
5280
  return 0;
5206
5281
  }