@paganaye/stylets 0.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.
Files changed (70) hide show
  1. package/README.md +13 -0
  2. package/dist/BaseStyles.d.ts +44 -0
  3. package/dist/BaseStyles.js +166 -0
  4. package/dist/Button.d.ts +69 -0
  5. package/dist/Button.js +56 -0
  6. package/dist/CssColor.d.ts +44 -0
  7. package/dist/CssColor.js +196 -0
  8. package/dist/CssColor.test.d.ts +1 -0
  9. package/dist/CssColor.test.js +68 -0
  10. package/dist/CssExpr.d.ts +9 -0
  11. package/dist/CssExpr.js +35 -0
  12. package/dist/CssFilter.d.ts +18 -0
  13. package/dist/CssFilter.js +40 -0
  14. package/dist/CssNum.d.ts +30 -0
  15. package/dist/CssNum.js +106 -0
  16. package/dist/CssNum.test.d.ts +1 -0
  17. package/dist/CssNum.test.js +30 -0
  18. package/dist/CssReset.d.ts +1 -0
  19. package/dist/CssReset.js +15 -0
  20. package/dist/CssShadow.d.ts +19 -0
  21. package/dist/CssShadow.js +42 -0
  22. package/dist/DefaultTheme.d.ts +14 -0
  23. package/dist/DefaultTheme.js +6 -0
  24. package/dist/EmptyTheme.d.ts +14 -0
  25. package/dist/EmptyTheme.js +2 -0
  26. package/dist/HTML.d.ts +9 -0
  27. package/dist/HTML.jsx +7 -0
  28. package/dist/ScopeStyles.d.ts +24 -0
  29. package/dist/ScopeStyles.js +67 -0
  30. package/dist/State.d.ts +36 -0
  31. package/dist/State.js +107 -0
  32. package/dist/State.test.d.ts +1 -0
  33. package/dist/State.test.js +41 -0
  34. package/dist/StyleWriter.d.ts +25 -0
  35. package/dist/StyleWriter.js +156 -0
  36. package/dist/Theme.d.ts +56 -0
  37. package/dist/Theme.js +29 -0
  38. package/dist/Tone.d.ts +1 -0
  39. package/dist/Tone.js +1 -0
  40. package/dist/index.d.ts +1 -0
  41. package/dist/index.js +1 -0
  42. package/dist/index.test.d.ts +1 -0
  43. package/dist/index.test.js +9 -0
  44. package/dist/props.d.ts +7 -0
  45. package/dist/props.js +104 -0
  46. package/dist/types.d.ts +239 -0
  47. package/dist/types.js +1 -0
  48. package/package.json +36 -0
  49. package/src/BaseStyles.ts +192 -0
  50. package/src/Button.ts +68 -0
  51. package/src/CssColor.test.ts +82 -0
  52. package/src/CssColor.ts +175 -0
  53. package/src/CssExpr.ts +25 -0
  54. package/src/CssFilter.ts +44 -0
  55. package/src/CssNum.test.ts +37 -0
  56. package/src/CssNum.ts +93 -0
  57. package/src/CssReset.ts +17 -0
  58. package/src/CssShadow.ts +46 -0
  59. package/src/DefaultTheme.ts +8 -0
  60. package/src/HTML.tsx +17 -0
  61. package/src/ScopeStyles.ts +100 -0
  62. package/src/State.test.ts +47 -0
  63. package/src/State.ts +164 -0
  64. package/src/StyleWriter.ts +163 -0
  65. package/src/Theme.ts +95 -0
  66. package/src/Tone.ts +1 -0
  67. package/src/index.test.ts +10 -0
  68. package/src/index.ts +1 -0
  69. package/src/props.ts +118 -0
  70. package/src/types.ts +311 -0
package/README.md ADDED
@@ -0,0 +1,13 @@
1
+ Stylets (local package)
2
+
3
+ Usage in demos:
4
+
5
+ Install workspace deps from repo root:
6
+
7
+ pnpm -w install
8
+
9
+ Import from source (recommended for dev):
10
+
11
+ import { styled } from 'stylets'
12
+
13
+ If you want Vite to use the source files directly, add a Vite alias to point `stylets` to `packages/stylets/src`.
@@ -0,0 +1,44 @@
1
+ import { CssColor } from "./CssColor";
2
+ import { StyleWriter } from "./StyleWriter";
3
+ import { ITheme, CssProperties, IThemeParts, ThemeArgs, Identity, BorderProperties, MarginProperties, PaddingProperties, ShadowProperties, BackgroundProperties, TypographyProperties, LayoutProperties } from "./types";
4
+ export declare abstract class BaseStyles<A extends ThemeArgs> implements ITheme<A> {
5
+ protected _name: string;
6
+ protected readonly _variants: Record<string, Record<string, CssProperties>>;
7
+ protected readonly _colors: Record<string, CssColor>;
8
+ protected readonly _sizes: Record<string, any>;
9
+ protected readonly _paddings: Record<string, PaddingProperties>;
10
+ protected readonly _margins: Record<string, MarginProperties>;
11
+ protected readonly _borders: Record<string, BorderProperties>;
12
+ protected readonly _shadows: Record<string, ShadowProperties>;
13
+ protected readonly _backgrounds: Record<string, BackgroundProperties>;
14
+ protected readonly _typography: Record<string, TypographyProperties>;
15
+ protected readonly _layouts: Record<string, LayoutProperties>;
16
+ protected readonly _vars: Record<string, string>;
17
+ protected readonly _classes: Record<string, CssProperties>;
18
+ protected readonly _styles: Record<string, CssProperties>;
19
+ readonly variants: {
20
+ [K in keyof A['variants'] as K extends string ? K : never]: {
21
+ [V in keyof A['variants'][K] & string]: V;
22
+ };
23
+ };
24
+ readonly colors: Record<A['colors'], `var(--${A['colors']})`>;
25
+ readonly vars: Record<A['vars'], `var(--${A['vars']})`>;
26
+ readonly paddings: Record<A['paddings'], `padding-${A['paddings']}`>;
27
+ readonly margins: Record<A['margins'], `margin-${A['margins']}`>;
28
+ readonly borders: Record<A['borders'], `border-${A['borders']}`>;
29
+ readonly shadows: Record<A['shadows'], `shadow-${A['shadows']}`>;
30
+ readonly backgrounds: Record<A['backgrounds'], `background-${A['backgrounds']}`>;
31
+ readonly typography: Record<A['typography'], `typography-${A['typography']}`>;
32
+ readonly layouts: Record<A['layouts'], `layouts-${A['layouts']}`>;
33
+ readonly classes: Identity<A['classes']>;
34
+ protected get name(): string;
35
+ protected readonly builder: StyleWriter;
36
+ constructor(themeParts: IThemeParts<A>);
37
+ protected add(parts: IThemeParts<A> & {
38
+ name?: string;
39
+ }): void;
40
+ protected preProcess(): void;
41
+ protected renderHeader(): void;
42
+ protected render(): void;
43
+ protected renderFooter(): void;
44
+ }
@@ -0,0 +1,166 @@
1
+ import { StyleWriter } from "./StyleWriter";
2
+ export class BaseStyles {
3
+ _name = "";
4
+ _variants = {};
5
+ _colors = {};
6
+ _sizes = {};
7
+ _paddings = {};
8
+ _margins = {};
9
+ _borders = {};
10
+ _shadows = {};
11
+ _backgrounds = {};
12
+ _typography = {};
13
+ _layouts = {};
14
+ _vars = {};
15
+ _classes = {};
16
+ _styles = {};
17
+ // this is a record of record but probably not totally rightly typed
18
+ variants = {};
19
+ colors = {};
20
+ vars = {};
21
+ paddings = {};
22
+ margins = {};
23
+ borders = {};
24
+ shadows = {};
25
+ backgrounds = {};
26
+ typography = {};
27
+ layouts = {};
28
+ classes = {};
29
+ get name() { return this._name; }
30
+ builder;
31
+ constructor(themeParts) {
32
+ let name = themeParts.name ?? "theme";
33
+ this.builder = StyleWriter.byName(name);
34
+ this.add(themeParts);
35
+ }
36
+ add(parts) {
37
+ if (parts.name)
38
+ this._name = parts.name;
39
+ if (parts.variants)
40
+ Object.assign(this._variants, parts.variants);
41
+ if (parts.colors)
42
+ Object.assign(this._colors, parts.colors);
43
+ if (parts.vars)
44
+ Object.assign(this._vars, parts.vars);
45
+ if (parts.paddings)
46
+ Object.assign(this._paddings, parts.paddings);
47
+ if (parts.margins)
48
+ Object.assign(this._margins, parts.margins);
49
+ if (parts.borders)
50
+ Object.assign(this._borders, parts.borders);
51
+ if (parts.layouts)
52
+ Object.assign(this._layouts, parts.layouts);
53
+ if (parts.shadows)
54
+ Object.assign(this._shadows, parts.shadows);
55
+ if (parts.classes)
56
+ Object.assign(this._classes, parts.classes);
57
+ if (parts.styles)
58
+ Object.assign(this._styles, parts.styles);
59
+ this.preProcess();
60
+ }
61
+ preProcess() {
62
+ // Build root variables from multiple sources in a single pass
63
+ const preProcessVars = (src, type, output) => {
64
+ let entries = Object.entries(src);
65
+ for (let [key, value] of Object.entries(src)) {
66
+ output[key] = `var(--${key})`;
67
+ }
68
+ };
69
+ preProcessVars(this._colors, 'colors', this.colors);
70
+ preProcessVars(this._vars, 'vars', this.vars);
71
+ // Process variants
72
+ for (const [groupKey, groupValue] of Object.entries(this._variants)) {
73
+ const variantGroup = {};
74
+ for (const variantKey of Object.keys(groupValue)) {
75
+ variantGroup[variantKey] = variantKey;
76
+ }
77
+ this.variants[groupKey] = variantGroup;
78
+ }
79
+ const preProcessRules = (src, label, output, prefix) => {
80
+ const entries = Object.entries(src);
81
+ let isClass = (label === 'classes');
82
+ for (const [k, v] of entries) {
83
+ output[k] = prefix ? (prefix + k) : k;
84
+ }
85
+ };
86
+ preProcessRules(this._paddings, 'paddings', this.paddings, 'padding-');
87
+ preProcessRules(this._margins, 'margins', this.margins, 'margin-');
88
+ preProcessRules(this._borders, 'borders', this.borders, 'border-');
89
+ preProcessRules(this._backgrounds, 'backgrounds', this.backgrounds, 'background-');
90
+ preProcessRules(this._typography, 'typography', this.typography, 'typography-');
91
+ preProcessRules(this._layouts, 'layouts', this.layouts, 'layouts-');
92
+ preProcessRules(this._shadows, 'shadows', this.shadows, 'shadow-');
93
+ preProcessRules(this._classes, 'classes', this.classes);
94
+ }
95
+ renderHeader() {
96
+ // this.builder.addLine(`/* ${this.name} start */`);
97
+ }
98
+ render() {
99
+ this.builder.clear();
100
+ this.renderHeader();
101
+ // Build root variables from multiple sources in a single pass
102
+ const processVars = (src, type) => {
103
+ let entries = Object.entries(src);
104
+ if (entries.length === 0)
105
+ return;
106
+ this.builder.addLine(`/* ${this.name} ${type} */`);
107
+ this.builder.addLine(`:root {`);
108
+ this.builder.indentLevel++;
109
+ for (let [key, value] of Object.entries(src)) {
110
+ switch (typeof value) {
111
+ case 'number':
112
+ value = `${value}rem`;
113
+ break;
114
+ case 'object':
115
+ value = value.toString();
116
+ break;
117
+ }
118
+ this.builder.addLine(`--${key}: ${value};`);
119
+ }
120
+ this.builder.indentLevel--;
121
+ this.builder.addLine(`}`);
122
+ };
123
+ processVars(this._colors, 'colors');
124
+ processVars(this._sizes, 'sizes');
125
+ processVars(this._vars, 'vars');
126
+ // Helper to emit classes/styles to avoid repetition
127
+ const processRules = (src, label, prefix) => {
128
+ const entries = Object.entries(src);
129
+ if (entries.length === 0)
130
+ return;
131
+ this.builder.addLine(`/* ${this.name} ${label} */`);
132
+ for (const [k, v] of entries) {
133
+ const selector = prefix ? `${prefix}${k}` : k;
134
+ this.builder.addRule(selector, v);
135
+ }
136
+ };
137
+ processRules(this._paddings, 'paddings', ".padding-");
138
+ processRules(this._margins, 'margins', ".margin-");
139
+ processRules(this._borders, 'borders', ".border-");
140
+ processRules(this._backgrounds, 'backgrounds', ".background-");
141
+ processRules(this._typography, 'typography', ".typography-");
142
+ processRules(this._layouts, 'layouts', ".layouts-");
143
+ processRules(this._shadows, 'shadows', ".shadow-");
144
+ processRules(this._classes, 'classes', ".");
145
+ processRules(this._styles, 'styles', "");
146
+ // Process variants with attribute selectors
147
+ const processVariants = () => {
148
+ const entries = Object.entries(this._variants);
149
+ if (entries.length === 0)
150
+ return;
151
+ this.builder.addLine(`/* ${this.name} variants */`);
152
+ for (const [groupKey, groupValue] of entries) {
153
+ for (const [variantKey, variantStyles] of Object.entries(groupValue)) {
154
+ const selector = `[${groupKey}="${variantKey}"]`;
155
+ this.builder.addRule(selector, variantStyles);
156
+ }
157
+ }
158
+ };
159
+ processVariants();
160
+ this.renderFooter();
161
+ this.builder.render();
162
+ }
163
+ renderFooter() {
164
+ this.builder.addLine(`/* ${this.name} end */`);
165
+ }
166
+ }
@@ -0,0 +1,69 @@
1
+ import { IDivProps } from "./HTML";
2
+ import { Tone } from "./Tone";
3
+ import { VariantProps } from "./types";
4
+ declare let styles: import("./ScopeStyles").ScopeStylesPublic<{
5
+ colors: "primary";
6
+ vars: never;
7
+ paddings: never;
8
+ margins: never;
9
+ borders: never;
10
+ backgrounds: never;
11
+ typography: never;
12
+ shadows: never;
13
+ layouts: never;
14
+ classes: "customButton";
15
+ variants: {
16
+ [x: string]: never;
17
+ buttonType: {
18
+ outline: {
19
+ borderColor: string;
20
+ };
21
+ normal: {
22
+ borderColor: string;
23
+ };
24
+ default: {
25
+ borderColor: string;
26
+ };
27
+ };
28
+ buttonSize: {
29
+ small: {
30
+ width: string;
31
+ };
32
+ large: {
33
+ width: string;
34
+ };
35
+ default: {
36
+ width: string;
37
+ };
38
+ };
39
+ };
40
+ }, {
41
+ [x: string]: never;
42
+ buttonType: {
43
+ outline: {
44
+ borderColor: string;
45
+ };
46
+ normal: {
47
+ borderColor: string;
48
+ };
49
+ default: {
50
+ borderColor: string;
51
+ };
52
+ };
53
+ buttonSize: {
54
+ small: {
55
+ width: string;
56
+ };
57
+ large: {
58
+ width: string;
59
+ };
60
+ default: {
61
+ width: string;
62
+ };
63
+ };
64
+ }>;
65
+ export interface IButtonProps extends IDivProps, VariantProps<typeof styles> {
66
+ tone?: Tone;
67
+ }
68
+ export declare function Button(props: IButtonProps, ...children: any[]): string;
69
+ export {};
package/dist/Button.js ADDED
@@ -0,0 +1,56 @@
1
+ import { prepareProps, Elt } from "./props";
2
+ import { theme as theme } from "./DefaultTheme"; // --- IGNORE ---
3
+ import { alter } from "./CssColor";
4
+ import { v } from "./CssExpr";
5
+ let styles = theme.declareScope({
6
+ class: 'btn',
7
+ scopeEnd: 'img',
8
+ colors: {},
9
+ sizes: {},
10
+ vars: {},
11
+ classes: {
12
+ customButton: { padding: '10px 15px' }
13
+ },
14
+ styles: {
15
+ ':scope': {
16
+ transition: 'all 0.25s ease',
17
+ borderRadius: `var(--buttonRadius)`,
18
+ borderColor: alter(v('tone'), {
19
+ l: { min: v('BdLum'), max: v('BdLum') },
20
+ c: { mul: v('BdChroma') }
21
+ }),
22
+ },
23
+ ':scope:hover': {
24
+ '--BgLum': '0.45',
25
+ '--BdLum': '0.95',
26
+ outline: '1px solid yellow',
27
+ },
28
+ ':scope:active': {
29
+ '--BgLum': '0',
30
+ '--BdLum': '0.3',
31
+ transform: 'translate(.1rem,.2rem)',
32
+ outline: '1px solid',
33
+ outlineColor: theme.colors.primary
34
+ }
35
+ },
36
+ variants: {
37
+ buttonType: {
38
+ outline: { borderColor: 'red' },
39
+ normal: { borderColor: 'blue' },
40
+ default: { borderColor: 'gray' }
41
+ },
42
+ buttonSize: {
43
+ small: { width: '200px' },
44
+ large: { width: '400px' },
45
+ default: { width: '300px' }
46
+ }
47
+ },
48
+ }).render();
49
+ export function Button(props, ...children) {
50
+ const newProps = prepareProps(props, styles);
51
+ if (props.tone) {
52
+ newProps.class.tone = true;
53
+ newProps.class[props.tone] = true;
54
+ }
55
+ return Elt('button', newProps, children);
56
+ }
@@ -0,0 +1,44 @@
1
+ import { type Num } from "./CssNum";
2
+ import { CssExpr } from "./CssExpr";
3
+ type IPropAlteration = Num | {
4
+ expr?: Num;
5
+ mul?: Num;
6
+ add?: Num;
7
+ min?: Num;
8
+ max?: Num;
9
+ };
10
+ type IColorAlterations = {
11
+ l?: IPropAlteration;
12
+ c?: IPropAlteration;
13
+ h?: IPropAlteration;
14
+ };
15
+ export declare abstract class CssColor extends CssExpr {
16
+ abstract toString(): string;
17
+ static alpha: typeof alpha;
18
+ static contrast: typeof contrast;
19
+ static complement: typeof complement;
20
+ static triadic: typeof triadic;
21
+ static analogous: typeof analogous;
22
+ static alter: typeof alter;
23
+ static lch: typeof lch;
24
+ static mix: typeof mix;
25
+ }
26
+ declare class LchColor extends CssColor {
27
+ readonly l: number;
28
+ readonly c: number;
29
+ readonly h: number;
30
+ constructor(l: number, c: number, h: number);
31
+ toString(): string;
32
+ }
33
+ export declare function alpha(color: CssExpr, opacity: number | CssExpr): string;
34
+ export declare function contrast(color: CssExpr): string;
35
+ export declare function complement(color: CssExpr): string;
36
+ export declare function triadic(color: CssExpr, index?: 0 | 1 | 2): string;
37
+ export declare function analogous(color: CssExpr, angle?: number): string;
38
+ export declare function alter(base: CssExpr, alterations: IColorAlterations): string;
39
+ export declare function lch(l: number, c: number, h: number): LchColor;
40
+ export declare function mix(base: CssExpr, other: CssExpr, ratio: number | CssExpr): string;
41
+ export declare const l: CssExpr;
42
+ export declare const c: CssExpr;
43
+ export declare const h: CssExpr;
44
+ export {};
@@ -0,0 +1,196 @@
1
+ import { CssExpr, customCss } from "./CssExpr";
2
+ export class CssColor extends CssExpr {
3
+ static alpha = alpha;
4
+ static contrast = contrast;
5
+ static complement = complement;
6
+ static triadic = triadic;
7
+ static analogous = analogous;
8
+ static alter = alter;
9
+ static lch = lch;
10
+ static mix = mix;
11
+ }
12
+ class CssColorVar extends CssColor {
13
+ name;
14
+ constructor(name) {
15
+ super('none', 'color');
16
+ this.name = name;
17
+ }
18
+ toString() { return `var(--${this.name})`; }
19
+ }
20
+ class CssCustomColorExpr extends CssColor {
21
+ expr;
22
+ constructor(expr) {
23
+ super('none', 'color');
24
+ this.expr = expr;
25
+ }
26
+ toString() { return this.expr; }
27
+ }
28
+ class LchColor extends CssColor {
29
+ l;
30
+ c;
31
+ h;
32
+ constructor(l, c, h) {
33
+ super('none', 'color');
34
+ this.l = l;
35
+ this.c = c;
36
+ this.h = h;
37
+ }
38
+ toString() { return `oklch(${this.l} ${this.c} ${this.h})`; }
39
+ }
40
+ class PropAlteration extends CssColor {
41
+ prop;
42
+ alt;
43
+ constructor(prop, alt) {
44
+ super('none', 'color');
45
+ this.prop = prop;
46
+ this.alt = alt;
47
+ }
48
+ toString() {
49
+ let alt = this.alt;
50
+ if (!alt)
51
+ return this.prop;
52
+ if (typeof alt === 'number')
53
+ return String(alt);
54
+ if (alt instanceof CssExpr || alt instanceof CssColor)
55
+ return alt.toString();
56
+ let p = (val) => {
57
+ if (val === undefined)
58
+ return undefined;
59
+ if (typeof val == 'number')
60
+ return String(val);
61
+ return val.toString();
62
+ };
63
+ let expr = p(alt.expr) ?? this.prop;
64
+ const mul = p(alt.mul);
65
+ const add = p(alt.add);
66
+ const min = p(alt.min);
67
+ const max = p(alt.max);
68
+ // Handle multiplication and addition
69
+ if (mul && add) {
70
+ expr = `calc((${expr} * ${mul}) + ${add})`;
71
+ }
72
+ else if (mul) {
73
+ expr = `calc(${expr} * ${mul})`;
74
+ }
75
+ else if (add) {
76
+ expr = `calc(${expr} + ${add})`;
77
+ }
78
+ // Handle clamping
79
+ if ('min' in alt && 'max' in alt) {
80
+ expr = `clamp(${min}, ${expr}, ${max})`;
81
+ }
82
+ else if ('min' in alt) {
83
+ expr = `max(${min}, ${expr})`;
84
+ }
85
+ else if ('max' in alt) {
86
+ expr = `min(${expr}, ${max})`;
87
+ }
88
+ return expr;
89
+ }
90
+ }
91
+ class AlterColor extends CssColor {
92
+ base;
93
+ l;
94
+ c;
95
+ h;
96
+ constructor(base, alterations) {
97
+ super('none', 'color');
98
+ this.base = base;
99
+ this.l = new PropAlteration('l', alterations.l);
100
+ this.c = new PropAlteration('c', alterations.c);
101
+ this.h = new PropAlteration('h', alterations.h);
102
+ }
103
+ toString() {
104
+ const l = this.l.toString();
105
+ const c = this.c.toString();
106
+ const h = this.h.toString();
107
+ return `oklch(from ${this.base.toString()} ${l} ${c} ${h})`;
108
+ }
109
+ }
110
+ class MixColor extends CssColor {
111
+ base;
112
+ other;
113
+ ratio;
114
+ constructor(base, other, ratio) {
115
+ super('none', 'color');
116
+ this.base = base;
117
+ this.other = other;
118
+ this.ratio = ratio;
119
+ }
120
+ toString() {
121
+ let percentA;
122
+ let percentB;
123
+ if (typeof this.ratio === 'number') {
124
+ if (this.ratio <= 0)
125
+ return this.base.toString();
126
+ if (this.ratio >= 1)
127
+ return this.other.toString();
128
+ percentA = `${Math.round(100 - this.ratio * 100)}%`;
129
+ percentB = `${Math.round(this.ratio * 100)}%`;
130
+ }
131
+ else {
132
+ percentA = `calc(100% - ${this.ratio})`;
133
+ percentB = this.ratio.toString();
134
+ }
135
+ return `mix(${this.base.toString()} ${percentA}, ${this.other.toString()}, ${percentB})`;
136
+ }
137
+ }
138
+ class AlphaColor extends CssColor {
139
+ color;
140
+ opacity;
141
+ constructor(color, opacity) { super('none', 'color'); this.color = color; this.opacity = opacity; }
142
+ toString() { const op = typeof this.opacity === 'number' ? String(this.opacity) : this.opacity.toString(); return `color(${this.color.toString()} / ${op})`; }
143
+ }
144
+ class ContrastColor extends CssColor {
145
+ color;
146
+ constructor(color) { super('none', 'color'); this.color = color; }
147
+ toString() { if (this.color instanceof LchColor) {
148
+ return this.color.l > 0.5 ? 'black' : 'white';
149
+ } return `color-contrast(${this.color.toString()} vs(black, white))`; }
150
+ }
151
+ class ComplementColor extends CssColor {
152
+ color;
153
+ constructor(color) { super('none', 'color'); this.color = color; }
154
+ toString() { if (this.color instanceof LchColor) {
155
+ const newH = (this.color.h + 180) % 360;
156
+ return lch(this.color.l, this.color.c, newH).toString();
157
+ } return `oklch(from ${this.color.toString()} l c calc((h + 180) % 360))`; }
158
+ }
159
+ class TriadicColor extends CssColor {
160
+ color;
161
+ index;
162
+ constructor(color, index = 0) { super('none', 'color'); this.color = color; this.index = index; }
163
+ toString() { const offset = this.index === 0 ? 0 : (this.index === 1 ? 120 : 240); if (this.color instanceof LchColor) {
164
+ const newH = (this.color.h + offset) % 360;
165
+ return lch(this.color.l, this.color.c, newH).toString();
166
+ } return `oklch(from ${this.color.toString()} l c calc((h + ${offset}) % 360))`; }
167
+ }
168
+ class AnalogousColor extends CssExpr {
169
+ color;
170
+ angle;
171
+ constructor(color, angle = 30) { super('none', 'color'); this.color = color; this.angle = angle; }
172
+ toString() {
173
+ if (this.color instanceof LchColor) {
174
+ if (typeof this.angle === 'number') {
175
+ const h1 = (this.color.h + this.angle) % 360;
176
+ const h2 = (this.color.h + 360 - this.angle) % 360;
177
+ return `${lch(this.color.l, this.color.c, h1).toString()}, ${lch(this.color.l, this.color.c, h2).toString()}`;
178
+ }
179
+ const aStr = this.angle.toString();
180
+ return `${this.color.toString()}, oklch(l c calc((h + ${aStr}) % 360)), oklch(l c calc((h - ${aStr} + 360) % 360))`;
181
+ }
182
+ const aStr = typeof this.angle === 'number' ? String(this.angle) : this.angle.toString();
183
+ return `${this.color.toString()}, oklch(l c calc((h + ${aStr}) % 360)), oklch(l c calc((h - ${aStr} + 360) % 360))`;
184
+ }
185
+ }
186
+ export function alpha(color, opacity) { return new AlphaColor(color, opacity).toString(); }
187
+ export function contrast(color) { return new ContrastColor(color).toString(); }
188
+ export function complement(color) { return new ComplementColor(color).toString(); }
189
+ export function triadic(color, index = 0) { return new TriadicColor(color, index).toString(); }
190
+ export function analogous(color, angle = 30) { return new AnalogousColor(color, angle).toString(); }
191
+ export function alter(base, alterations) { return new AlterColor(base, alterations).toString(); }
192
+ export function lch(l, c, h) { return new LchColor(l, c, h); }
193
+ export function mix(base, other, ratio) { return new MixColor(base, other, ratio).toString(); }
194
+ export const l = customCss('l', 'none', 'length');
195
+ export const c = customCss('c', 'none', 'length');
196
+ export const h = customCss('h', 'none', 'angle');
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,68 @@
1
+ import { describe, it } from 'vitest';
2
+ // TODO: Update imports once CssColor is fully integrated
3
+ // import { alpha, contrast, complement, triadic, analogous, mix, alter, lch } from './CssColor';
4
+ // import { v } from './CssExpr';
5
+ describe('colorUtils', () => {
6
+ it.skip('cssVar returns a CSS variable string', () => {
7
+ // expect(v('primary').toString()).toBe('var(--primary)');
8
+ });
9
+ it.skip('col creates a Color instance representing a CSS var', () => {
10
+ // const val = v('accent');
11
+ // expect(String(val)).toBe('var(--accent)');
12
+ });
13
+ it.skip('mixColors returns base when ratio <= 0', () => {
14
+ // const base = lch(0.5, 0.2, 200);
15
+ // const other = lch(0.7, 0.3, 100);
16
+ // expect(mix(base, other, 0)).toBe('oklch(0.5 0.2 200)');
17
+ });
18
+ it.skip('mixColors returns other when ratio >= 1', () => {
19
+ // const base = lch(0.5, 0.2, 200);
20
+ // const other = lch(0.7, 0.3, 100);
21
+ // expect(mix(base, other, 1)).toBe('oklch(0.7 0.3 100)');
22
+ });
23
+ it.skip('mixColors returns mix string for intermediate ratio', () => {
24
+ // const base = lch(0.5, 0.2, 200);
25
+ // const other = lch(0.7, 0.3, 100);
26
+ // expect(mix(base, other, 0.25)).toBe('mix(oklch(0.5 0.2 200) 75%, oklch(0.7 0.3 100), 25%)');
27
+ });
28
+ it.skip('alterColor with numeric alterations', () => {
29
+ // const base = lch(0.5, 0.2, 200);
30
+ // const out = alter(base, { l: 0.3, c: { mul: 2 }, h: { add: 10 } });
31
+ // expect(out).toBe('oklch(from oklch(0.5 0.2 200) 0.3 calc(c * 2) calc(h + 10))');
32
+ });
33
+ it.skip('alterColor with clamp alterations', () => {
34
+ // const base = lch(0.5, 0.2, 200);
35
+ // const out = alter(base, { l: { min: 0, max: 1 }, c: { min: v('cMin') as any, max: v('cMax') as any } });
36
+ // expect(out).toBe('oklch(from oklch(0.5 0.2 200) clamp(0, l, 1) clamp(var(--cMin), c, var(--cMax)) h)');
37
+ });
38
+ it.skip('alterColor with multiple alterations', () => {
39
+ // const base = lch(0.6, 0.4, 120);
40
+ // const out = alter(base, { l: { mul: 0.5, add: 0.1, min: 0, max: 1 }, c: { mul: v('scale'), add: 0.02 }, h: 180 });
41
+ // expect(out).toBe('oklch(from oklch(0.6 0.4 120) clamp(0, calc((l * 0.5) + 0.1), 1) calc((c * var(--scale)) + 0.02) 180)');
42
+ });
43
+ it.skip('alpha wraps color with opacity', () => {
44
+ // const base = lch(0.5, 0.2, 200);
45
+ // expect(alpha(base, 0.4)).toBe('color(oklch(0.5 0.2 200) / 0.4)');
46
+ });
47
+ it.skip('contrast returns black for light lch', () => {
48
+ // const light = lch(0.9, 0.1, 10);
49
+ // expect(contrast(light)).toBe('black');
50
+ });
51
+ it.skip('contrast returns white for dark lch', () => {
52
+ // const dark = lch(0.2, 0.2, 10);
53
+ // expect(contrast(dark)).toBe('white');
54
+ });
55
+ it.skip('complement rotates hue by 180', () => {
56
+ // const c = lch(0.5, 0.2, 30);
57
+ // expect(complement(c)).toBe('oklch(0.5 0.2 210)');
58
+ });
59
+ it.skip('triadic produces rotated hues', () => {
60
+ // const c = lch(0.5, 0.2, 30);
61
+ // expect(triadic(c, 1)).toBe('oklch(0.5 0.2 150)');
62
+ // expect(triadic(c, 2)).toBe('oklch(0.5 0.2 270)');
63
+ });
64
+ it.skip('analogous returns two nearby hues', () => {
65
+ // const c = lch(0.5, 0.2, 60);
66
+ // expect(analogous(c, 20)).toBe('oklch(0.5 0.2 80), oklch(0.5 0.2 40)');
67
+ });
68
+ });
@@ -0,0 +1,9 @@
1
+ import { CssUnit, CssType } from "./CssNum";
2
+ export declare abstract class CssExpr {
3
+ readonly unit: CssUnit;
4
+ readonly type: CssType;
5
+ constructor(unit: CssUnit, type: CssType);
6
+ }
7
+ export declare function customCss(expr: string, unit?: CssUnit, type?: CssType): CssExpr;
8
+ export declare function arg(name: string, unit?: CssUnit, type?: CssType): CssExpr;
9
+ export declare function v(name: string, unit?: CssUnit, type?: CssType): CssExpr;