@diskette/palette 0.12.0 → 0.13.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/dist/cli.js CHANGED
@@ -64,8 +64,6 @@ async function main() {
64
64
  cancel();
65
65
  const outputDir = await askOutputDir('Output directory:');
66
66
  // --- Generate Files ---
67
- const s = p.spinner();
68
- s.start('Generating CSS files...');
69
67
  const outputPath = resolve(process.cwd(), outputDir);
70
68
  mkdirSync(outputPath, { recursive: true });
71
69
  for (const name of selectedColors) {
@@ -84,7 +82,18 @@ async function main() {
84
82
  const grays = css.grays(selectedGrayColors, 'diskette-palette');
85
83
  writeFileSync(`${outputPath}/accents.css`, `${accents}\n${grays}`);
86
84
  }
87
- s.stop();
85
+ // Generate Tailwind theme with imports
86
+ const imports = [
87
+ ...selectedColors.flatMap((name) => [
88
+ `./${name}.css`,
89
+ `./${name}-dark.css`,
90
+ ]),
91
+ './black-alpha.css',
92
+ './white-alpha.css',
93
+ ]
94
+ .map((path) => `@import '${path}';`)
95
+ .join('\n');
96
+ writeFileSync(`${outputPath}/index.css`, `${imports}\n\n${css.tailwind(['white', 'black', 'accent', ...selectedColors])}`);
88
97
  p.outro(`CSS saved to ${outputPath}`);
89
98
  }
90
99
  main().catch(console.error);
package/dist/css.d.ts CHANGED
@@ -9,22 +9,31 @@ type PaletteOptions = {
9
9
  /** Include alpha scale variables (e.g., --amber-a1). Defaults to false */
10
10
  alpha?: boolean;
11
11
  };
12
+ declare function palette(name: string, config: AnyPalette, options?: PaletteOptions): string;
13
+ declare function alpha(config: AlphaConfig): string;
14
+ declare function accents(colorNames: string[]): string;
15
+ declare function grays(names: readonly string[], className: string): string;
16
+ declare function tailwind(colorNames: string[]): string;
12
17
  export declare const css: {
13
18
  /**
14
19
  * Generate combined CSS for scale variables and semantic tokens
15
20
  */
16
- palette(name: string, config: AnyPalette, options?: PaletteOptions): string;
21
+ palette: typeof palette;
17
22
  /**
18
23
  * Generate CSS for alpha-only color scales
19
24
  */
20
- alpha(config: AlphaConfig): string;
25
+ alpha: typeof alpha;
21
26
  /**
22
27
  * Generate CSS for accent color data attribute selectors
23
28
  */
24
- accents(colorNames: string[]): string;
29
+ accents: typeof accents;
25
30
  /**
26
31
  * Generate CSS for gray color data attribute selectors
27
32
  */
28
- grays(names: readonly string[], className: string): string;
33
+ grays: typeof grays;
34
+ /**
35
+ * Generate Tailwind v4 @theme inline CSS mapping palette variables
36
+ */
37
+ tailwind: typeof tailwind;
29
38
  };
30
39
  export {};
package/dist/css.js CHANGED
@@ -1,126 +1,145 @@
1
+ import { formatNestedBlock, formatP3, formatRule, schemeSelector, toVarName, } from "./utils.js";
1
2
  const steps = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
2
- const LIGHT_SELECTOR = ':root, .light, .light-theme';
3
- const DARK_SELECTOR = '.dark, .dark-theme';
4
- const schemeSelector = (scheme) => scheme === 'light' ? LIGHT_SELECTOR : DARK_SELECTOR;
5
- /** Convert "amber9" to "--amber-9" or "amberA9" to "--amber-a9" */
6
- const toVarName = (key) => `--${key.replace(/A?(\d+)/, (m, n) => (m.startsWith('A') ? `-a${n}` : `-${n}`))}`;
7
- const formatRule = (selector, vars) => `${selector} {\n ${vars.join(';\n ')};\n}`;
8
- const formatP3 = (selector, vars) => `@supports (color: color(display-p3 1 1 1)) {\n @media (color-gamut: p3) {\n ${selector} {\n ${vars.join(';\n ')};\n }\n }\n}`;
9
- const formatNestedBlock = (selector, vars) => `&:where(${selector}) {\n ${vars.join(';\n ')};\n }`;
10
- export const css = {
11
- /**
12
- * Generate combined CSS for scale variables and semantic tokens
13
- */
14
- palette(name, config, options = {}) {
15
- const { schemes = ['light'], alpha = false } = options;
16
- const output = [];
17
- if (schemes.includes('light')) {
18
- output.push(formatRule(':root', [
19
- `--${name}-contrast: ${config.contrast}`,
20
- `--${name}-indicator: var(${toVarName(config.indicator)})`,
21
- `--${name}-track: var(${toVarName(config.track)})`,
22
- ]));
3
+ function palette(name, config, options = {}) {
4
+ const { schemes = ['light'], alpha = false } = options;
5
+ const output = [];
6
+ if (schemes.includes('light')) {
7
+ output.push(formatRule(':root', [
8
+ `--${name}-contrast: ${config.contrast}`,
9
+ `--${name}-indicator: var(${toVarName(config.indicator)})`,
10
+ `--${name}-track: var(${toVarName(config.track)})`,
11
+ ]));
12
+ }
13
+ for (const scheme of schemes) {
14
+ const isLight = scheme === 'light';
15
+ const selector = schemeSelector(scheme);
16
+ const srgbScale = isLight ? config.srgb.light : config.srgb.dark;
17
+ const p3Scale = isLight ? config.p3.light : config.p3.dark;
18
+ const srgbSurface = isLight
19
+ ? config.surface.srgb.light
20
+ : config.surface.srgb.dark;
21
+ const p3Surface = isLight ? config.surface.p3.light : config.surface.p3.dark;
22
+ const srgb = [];
23
+ const p3 = [];
24
+ for (const n of steps) {
25
+ srgb.push(`--${name}-${n}: ${srgbScale[`${name}${n}`]}`);
26
+ p3.push(`--${name}-${n}: ${p3Scale[`${name}${n}`]}`);
23
27
  }
24
- for (const scheme of schemes) {
25
- const isLight = scheme === 'light';
26
- const selector = schemeSelector(scheme);
27
- const srgbScale = isLight ? config.srgb.light : config.srgb.dark;
28
- const p3Scale = isLight ? config.p3.light : config.p3.dark;
29
- const srgbSurface = isLight
30
- ? config.surface.srgb.light
31
- : config.surface.srgb.dark;
32
- const p3Surface = isLight
33
- ? config.surface.p3.light
34
- : config.surface.p3.dark;
35
- const srgb = [];
36
- const p3 = [];
28
+ if (alpha) {
37
29
  for (const n of steps) {
38
- srgb.push(`--${name}-${n}: ${srgbScale[`${name}${n}`]}`);
39
- p3.push(`--${name}-${n}: ${p3Scale[`${name}${n}`]}`);
30
+ srgb.push(`--${name}-a${n}: ${srgbScale[`${name}A${n}`]}`);
31
+ p3.push(`--${name}-a${n}: ${p3Scale[`${name}A${n}`]}`);
40
32
  }
41
- if (alpha) {
42
- for (const n of steps) {
43
- srgb.push(`--${name}-a${n}: ${srgbScale[`${name}A${n}`]}`);
44
- p3.push(`--${name}-a${n}: ${p3Scale[`${name}A${n}`]}`);
45
- }
46
- }
47
- srgb.push(`--${name}-surface: ${srgbSurface}`);
48
- p3.push(`--${name}-surface: ${p3Surface}`);
49
- output.push(formatRule(selector, srgb));
50
- output.push(formatP3(selector, p3));
51
33
  }
52
- return output.join('\n\n') + '\n';
53
- },
54
- /**
55
- * Generate CSS for alpha-only color scales
56
- */
57
- alpha(config) {
34
+ srgb.push(`--${name}-surface: ${srgbSurface}`);
35
+ p3.push(`--${name}-surface: ${p3Surface}`);
36
+ output.push(formatRule(selector, srgb));
37
+ output.push(formatP3(selector, p3));
38
+ }
39
+ return output.join('\n\n') + '\n';
40
+ }
41
+ function alpha(config) {
42
+ const srgb = [];
43
+ const p3 = [];
44
+ for (const key in config.srgb) {
45
+ srgb.push(`${toVarName(key)}: ${config.srgb[key]}`);
46
+ }
47
+ for (const key in config.p3) {
48
+ p3.push(`${toVarName(key)}: ${config.p3[key]}`);
49
+ }
50
+ return [formatRule(':root', srgb), formatP3(':root', p3)].join('\n\n') + '\n';
51
+ }
52
+ function accents(colorNames) {
53
+ const output = colorNames.map((colorName) => {
58
54
  const srgb = [];
59
- const p3 = [];
60
- for (const key in config.srgb) {
61
- srgb.push(`${toVarName(key)}: ${config.srgb[key]}`);
55
+ const alpha = [];
56
+ for (const n of steps) {
57
+ srgb.push(`--accent-${n}: var(--${colorName}-${n})`);
58
+ alpha.push(`--accent-a${n}: var(--${colorName}-a${n})`);
62
59
  }
63
- for (const key in config.p3) {
64
- p3.push(`${toVarName(key)}: ${config.p3[key]}`);
60
+ const semantic = [
61
+ `--accent-contrast: var(--${colorName}-contrast)`,
62
+ `--accent-surface: var(--${colorName}-surface)`,
63
+ `--accent-indicator: var(--${colorName}-indicator)`,
64
+ `--accent-track: var(--${colorName}-track)`,
65
+ ];
66
+ return formatRule(`[data-accent-color='${colorName}']`, [
67
+ ...srgb,
68
+ ...alpha,
69
+ ...semantic,
70
+ ]);
71
+ });
72
+ const focus = [];
73
+ const focusAlpha = [];
74
+ for (const n of steps) {
75
+ focus.push(`--focus-${n}: var(--accent-${n})`);
76
+ focusAlpha.push(`--focus-a${n}: var(--accent-a${n})`);
77
+ }
78
+ output.push(formatRule(`[data-accent-color]:where(:not([data-accent-color=''], [data-accent-color='gray']))`, [...focus, ...focusAlpha]));
79
+ return output.join('\n\n') + '\n';
80
+ }
81
+ function grays(names, className) {
82
+ const grays = names.filter((n) => n !== 'gray');
83
+ const blocks = grays.map((colorName) => {
84
+ const srgb = [];
85
+ const alpha = [];
86
+ for (const n of steps) {
87
+ srgb.push(`--gray-${n}: var(--${colorName}-${n})`);
88
+ alpha.push(`--gray-a${n}: var(--${colorName}-a${n})`);
65
89
  }
66
- return ([formatRule(':root', srgb), formatP3(':root', p3)].join('\n\n') + '\n');
67
- },
68
- /**
69
- * Generate CSS for accent color data attribute selectors
70
- */
71
- accents(colorNames) {
72
- const output = colorNames.map((colorName) => {
73
- const srgb = [];
74
- const alpha = [];
90
+ const semantic = [
91
+ `--gray-contrast: var(--${colorName}-contrast)`,
92
+ `--gray-surface: var(--${colorName}-surface)`,
93
+ `--gray-indicator: var(--${colorName}-indicator)`,
94
+ `--gray-track: var(--${colorName}-track)`,
95
+ ];
96
+ return formatNestedBlock(`[data-gray-color='${colorName}']`, [
97
+ ...srgb,
98
+ ...alpha,
99
+ ...semantic,
100
+ ]);
101
+ });
102
+ return `.${className} {\n ${blocks.join('\n\n ')}\n}\n`;
103
+ }
104
+ function tailwind(colorNames) {
105
+ const blocks = [];
106
+ for (const name of colorNames) {
107
+ const declarations = [];
108
+ const isAlphaOnly = name === 'white' || name === 'black';
109
+ if (!isAlphaOnly) {
75
110
  for (const n of steps) {
76
- srgb.push(`--accent-${n}: var(--${colorName}-${n})`);
77
- alpha.push(`--accent-a${n}: var(--${colorName}-a${n})`);
111
+ declarations.push(`--color-${name}-${n}: var(--${name}-${n})`);
78
112
  }
79
- const semantic = [
80
- `--accent-contrast: var(--${colorName}-contrast)`,
81
- `--accent-surface: var(--${colorName}-surface)`,
82
- `--accent-indicator: var(--${colorName}-indicator)`,
83
- `--accent-track: var(--${colorName}-track)`,
84
- ];
85
- return formatRule(`[data-accent-color='${colorName}']`, [
86
- ...srgb,
87
- ...alpha,
88
- ...semantic,
89
- ]);
90
- });
91
- const focus = [];
92
- const focusAlpha = [];
113
+ }
93
114
  for (const n of steps) {
94
- focus.push(`--focus-${n}: var(--accent-${n})`);
95
- focusAlpha.push(`--focus-a${n}: var(--accent-a${n})`);
115
+ declarations.push(`--color-${name}-a${n}: var(--${name}-a${n})`);
116
+ }
117
+ if (!isAlphaOnly) {
118
+ declarations.push(`--color-${name}-contrast: var(--${name}-contrast)`, `--color-${name}-surface: var(--${name}-surface)`, `--color-${name}-track: var(--${name}-track)`, `--color-${name}-indicator: var(--${name}-indicator)`);
96
119
  }
97
- output.push(formatRule(`[data-accent-color]:where(:not([data-accent-color=''], [data-accent-color='gray']))`, [...focus, ...focusAlpha]));
98
- return output.join('\n\n') + '\n';
99
- },
120
+ blocks.push(declarations.join(';\n '));
121
+ }
122
+ return `@theme inline {\n ${blocks.join(';\n\n ')};\n}\n`;
123
+ }
124
+ export const css = {
125
+ /**
126
+ * Generate combined CSS for scale variables and semantic tokens
127
+ */
128
+ palette,
129
+ /**
130
+ * Generate CSS for alpha-only color scales
131
+ */
132
+ alpha,
133
+ /**
134
+ * Generate CSS for accent color data attribute selectors
135
+ */
136
+ accents,
100
137
  /**
101
138
  * Generate CSS for gray color data attribute selectors
102
139
  */
103
- grays(names, className) {
104
- const grays = names.filter((n) => n !== 'gray');
105
- const blocks = grays.map((colorName) => {
106
- const srgb = [];
107
- const alpha = [];
108
- for (const n of steps) {
109
- srgb.push(`--gray-${n}: var(--${colorName}-${n})`);
110
- alpha.push(`--gray-a${n}: var(--${colorName}-a${n})`);
111
- }
112
- const semantic = [
113
- `--gray-contrast: var(--${colorName}-contrast)`,
114
- `--gray-surface: var(--${colorName}-surface)`,
115
- `--gray-indicator: var(--${colorName}-indicator)`,
116
- `--gray-track: var(--${colorName}-track)`,
117
- ];
118
- return formatNestedBlock(`[data-gray-color='${colorName}']`, [
119
- ...srgb,
120
- ...alpha,
121
- ...semantic,
122
- ]);
123
- });
124
- return `.${className} {\n ${blocks.join('\n\n ')}\n}\n`;
125
- },
140
+ grays,
141
+ /**
142
+ * Generate Tailwind v4 @theme inline CSS mapping palette variables
143
+ */
144
+ tailwind,
126
145
  };
package/dist/utils.d.ts CHANGED
@@ -1,2 +1,10 @@
1
1
  import type { AccentColor, GrayColor } from './types.ts';
2
2
  export declare function getMatchingGrayColor(accentColor: AccentColor): GrayColor;
3
+ export declare const LIGHT_SELECTOR = ":root, .light, .light-theme";
4
+ export declare const DARK_SELECTOR = ".dark, .dark-theme";
5
+ export declare const schemeSelector: (scheme: "light" | "dark") => string;
6
+ /** Convert "amber9" to "--amber-9" or "amberA9" to "--amber-a9" */
7
+ export declare const toVarName: (key: string) => string;
8
+ export declare const formatRule: (selector: string, vars: string[]) => string;
9
+ export declare const formatP3: (selector: string, vars: string[]) => string;
10
+ export declare const formatNestedBlock: (selector: string, vars: string[]) => string;
package/dist/utils.js CHANGED
@@ -35,3 +35,11 @@ export function getMatchingGrayColor(accentColor) {
35
35
  return 'gray';
36
36
  }
37
37
  }
38
+ export const LIGHT_SELECTOR = ':root, .light, .light-theme';
39
+ export const DARK_SELECTOR = '.dark, .dark-theme';
40
+ export const schemeSelector = (scheme) => scheme === 'light' ? LIGHT_SELECTOR : DARK_SELECTOR;
41
+ /** Convert "amber9" to "--amber-9" or "amberA9" to "--amber-a9" */
42
+ export const toVarName = (key) => `--${key.replace(/A?(\d+)/, (m, n) => (m.startsWith('A') ? `-a${n}` : `-${n}`))}`;
43
+ export const formatRule = (selector, vars) => `${selector} {\n ${vars.join(';\n ')};\n}`;
44
+ export const formatP3 = (selector, vars) => `@supports (color: color(display-p3 1 1 1)) {\n @media (color-gamut: p3) {\n ${selector} {\n ${vars.join(';\n ')};\n }\n }\n}`;
45
+ export const formatNestedBlock = (selector, vars) => `&:where(${selector}) {\n ${vars.join(';\n ')};\n }`;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@diskette/palette",
3
3
  "type": "module",
4
- "version": "0.12.0",
4
+ "version": "0.13.0",
5
5
  "bin": {
6
6
  "palette": "./dist/cli.js"
7
7
  },