purse-styles 0.1.2 → 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
@@ -50,6 +50,44 @@ Wrap your app in `PurseProvider`:
50
50
 
51
51
  That's all! No build step.
52
52
 
53
+ ## Variables
54
+
55
+ Define typed CSS custom properties with `defineVars`. Defaults are injected on `:root` when a `PurseProvider` is mounted.
56
+
57
+ ```tsx
58
+ import { PurseProvider, defineVars, style, useStyles } from "purse-styles"
59
+
60
+ const DARK = "@media (prefers-color-scheme: dark)"
61
+
62
+ export const colors = defineVars({
63
+ text: { default: "black", [DARK]: "white" },
64
+ background: { default: "white", [DARK]: "black" },
65
+ accent: "blue",
66
+ })
67
+
68
+ const container = style({
69
+ color: colors.text,
70
+ backgroundColor: colors.background,
71
+ borderColor: colors.accent,
72
+ })
73
+
74
+ function App() {
75
+ const className = useStyles(container)
76
+ return <main className={className}>...</main>
77
+ }
78
+
79
+ ;<PurseProvider>
80
+ <App />
81
+ </PurseProvider>
82
+ ```
83
+
84
+ Notes:
85
+
86
+ - Token names are generated (`var(--purse-<hash>-<key>)`). Identical definition objects share the same names.
87
+ - Keys cannot start with `--`; Purse always generates the custom property name.
88
+ - Conditional values use a required `default` plus CSS at-rule keys (`@media`, `@supports`, etc.), same family as nested keys in `style()`.
89
+ - Variables are root-level only. There is no `createTheme` / subtree theme API. App-wide non-media modes can still override the generated custom properties with root attributes or `useInjectGlobalStyles`.
90
+
53
91
  ## Exports
54
92
 
55
93
  ```ts
@@ -66,10 +104,15 @@ export declare function useStyles(
66
104
  // Inject global styles into the app, you may want to do this as an escape hatch
67
105
  export declare function useInjectGlobalStyles(
68
106
  selector: string,
69
- cssProperties: BaseCSSProperties & CSSVarDeclarations,
107
+ cssProperties: BaseCSSProperties & CustomProperties,
70
108
  deps: DependencyList,
71
109
  ): void
72
110
 
111
+ // Define a typed variable group; defaults are registered on :root via PurseProvider
112
+ export declare function defineVars<
113
+ const T extends Record<string, VariableDefinition>,
114
+ >(definitions: T): VariableGroup<T>
115
+
73
116
  // Wrap your app in this so Purse can inject styles into the app
74
117
  export declare function PurseProvider(props: {
75
118
  children?: React.ReactNode
@@ -82,6 +125,7 @@ export type InMemoryStyleApi = {
82
125
  }
83
126
  addStyleElement: StyleApi["addStyleElement"]
84
127
  addGlobalStyle: StyleApi["addGlobalStyle"]
128
+ detachVariableGroups: () => void
85
129
  }
86
130
  export declare function createInMemoryStyleApi(): InMemoryStyleApi
87
131
  ```
package/dist/cssVar.d.ts CHANGED
@@ -1,2 +1,32 @@
1
+ import type CSS from "csstype";
2
+ import { Destructor } from "./destructors";
1
3
  export type CSSVar = `var(--${string})`;
2
- export declare function createCssVar(name: string): CSSVar;
4
+ export type VariablePrimitive = string | number;
5
+ export type VariableCondition = `${CSS.AtRules}${string}`;
6
+ export type ConditionalVariableValue<T extends VariablePrimitive = VariablePrimitive> = {
7
+ default: T;
8
+ } & Partial<Record<VariableCondition, T>>;
9
+ export type VariableDefinition = VariablePrimitive | ConditionalVariableValue;
10
+ type NormalizedTokenDefinition = {
11
+ default: VariablePrimitive;
12
+ conditions: Record<string, VariablePrimitive>;
13
+ };
14
+ type NormalizedDefinitions = Record<string, NormalizedTokenDefinition>;
15
+ export type VariableGroupMetadata = {
16
+ groupId: string;
17
+ definitions: NormalizedDefinitions;
18
+ names: Record<string, `--${string}`>;
19
+ };
20
+ export type VariableGroup<T extends Record<string, unknown>> = {
21
+ readonly [K in keyof T]: CSSVar;
22
+ };
23
+ export type AnyVariableGroup = VariableGroup<Record<string, unknown>>;
24
+ export type VariableStyleSink = {
25
+ addGlobalStyle: (styleRule: string) => Destructor;
26
+ };
27
+ export declare function compileVariableGroupRules(metadata: VariableGroupMetadata): string[];
28
+ export declare function subscribeVariableGroups(listener: (metadata: VariableGroupMetadata) => void): Destructor;
29
+ export declare function attachVariableGroups(styleApi: VariableStyleSink): Destructor;
30
+ export declare function defineVars<const T extends Record<string, VariableDefinition>>(definitions: T): VariableGroup<T>;
31
+ export declare function getVariableGroupMetadata(group: AnyVariableGroup): VariableGroupMetadata;
32
+ export {};
package/dist/index.d.ts CHANGED
@@ -1 +1,2 @@
1
1
  export { CSSProperties, InMemoryStyleApi, PurseProvider, StyleApi, StyleArgument, StyleElement, createInMemoryStyleApi, style, useInjectGlobalStyles, useStyles, } from "./purse";
2
+ export { AnyVariableGroup, CSSVar, ConditionalVariableValue, VariableCondition, VariableDefinition, VariableGroup, VariableGroupMetadata, VariablePrimitive, defineVars, getVariableGroupMetadata, } from "./cssVar";
package/dist/index.js CHANGED
@@ -40,6 +40,134 @@ function toPhash(h, x) {
40
40
  }
41
41
  var hashObject = (value) => toAlphabeticName(toPhash(5381, JSON.stringify(value)) >>> 0);
42
42
 
43
+ // src/cssVar.ts
44
+ var VARIABLE_GROUP_META = Symbol("purse.variableGroup");
45
+ var registeredGroups = /* @__PURE__ */ new Map();
46
+ var listeners = /* @__PURE__ */ new Set();
47
+ function normalizeDefinition(value) {
48
+ if (typeof value !== "object" || value === null) {
49
+ return { default: value, conditions: {} };
50
+ }
51
+ const conditions = {};
52
+ for (const key of Object.keys(value).sort()) {
53
+ if (key === "default") continue;
54
+ conditions[key] = value[key];
55
+ }
56
+ return {
57
+ default: value.default,
58
+ conditions
59
+ };
60
+ }
61
+ function canonicalizeDefinitions(definitions) {
62
+ const canonical = {};
63
+ for (const key of Object.keys(definitions).sort()) {
64
+ canonical[key] = normalizeDefinition(definitions[key]);
65
+ }
66
+ return canonical;
67
+ }
68
+ function assertValidTokenKeys(definitions) {
69
+ for (const key of Object.keys(definitions)) {
70
+ if (key.startsWith("--")) {
71
+ throw new Error(
72
+ `defineVars keys cannot start with "--" (received "${key}"). Purse generates custom property names automatically.`
73
+ );
74
+ }
75
+ }
76
+ }
77
+ function registerVariableGroup(metadata) {
78
+ if (registeredGroups.has(metadata.groupId)) {
79
+ return;
80
+ }
81
+ registeredGroups.set(metadata.groupId, metadata);
82
+ for (const listener of listeners) {
83
+ listener(metadata);
84
+ }
85
+ }
86
+ function compileVariableGroupRules(metadata) {
87
+ let rootDeclarations = "";
88
+ const declarationsByCondition = /* @__PURE__ */ new Map();
89
+ for (const key of Object.keys(metadata.definitions)) {
90
+ const definition = metadata.definitions[key];
91
+ const customProperty = metadata.names[key];
92
+ rootDeclarations += `${customProperty}:${definition.default};`;
93
+ for (const condition of Object.keys(definition.conditions).sort()) {
94
+ const existing = declarationsByCondition.get(condition) ?? "";
95
+ declarationsByCondition.set(
96
+ condition,
97
+ `${existing}${customProperty}:${definition.conditions[condition]};`
98
+ );
99
+ }
100
+ }
101
+ const rules = [`:root{${rootDeclarations}}`];
102
+ for (const condition of [...declarationsByCondition.keys()].sort()) {
103
+ rules.push(
104
+ `${condition}{:root{${declarationsByCondition.get(condition)}}}`
105
+ );
106
+ }
107
+ return rules;
108
+ }
109
+ function subscribeVariableGroups(listener) {
110
+ listeners.add(listener);
111
+ for (const metadata of registeredGroups.values()) {
112
+ listener(metadata);
113
+ }
114
+ return () => {
115
+ listeners.delete(listener);
116
+ };
117
+ }
118
+ function attachVariableGroups(styleApi) {
119
+ const insertedGroups = /* @__PURE__ */ new Map();
120
+ function ensureGroup(metadata) {
121
+ if (insertedGroups.has(metadata.groupId)) {
122
+ return;
123
+ }
124
+ const destructor = joinDestructors(
125
+ compileVariableGroupRules(metadata).map(
126
+ (rule) => styleApi.addGlobalStyle(rule)
127
+ )
128
+ );
129
+ insertedGroups.set(metadata.groupId, destructor);
130
+ }
131
+ const unsubscribe = subscribeVariableGroups(ensureGroup);
132
+ return () => {
133
+ unsubscribe();
134
+ for (const destructor of insertedGroups.values()) {
135
+ destructor();
136
+ }
137
+ insertedGroups.clear();
138
+ };
139
+ }
140
+ function defineVars(definitions) {
141
+ assertValidTokenKeys(definitions);
142
+ const canonicalDefinitions = canonicalizeDefinitions(definitions);
143
+ const groupId = hashObject(canonicalDefinitions);
144
+ const names = {};
145
+ const references = {};
146
+ for (const key of Object.keys(canonicalDefinitions)) {
147
+ const customProperty = `--purse-${groupId}-${key}`;
148
+ names[key] = customProperty;
149
+ references[key] = `var(${customProperty})`;
150
+ }
151
+ const metadata = {
152
+ groupId,
153
+ definitions: canonicalDefinitions,
154
+ names
155
+ };
156
+ Object.defineProperty(references, VARIABLE_GROUP_META, {
157
+ value: metadata,
158
+ enumerable: false
159
+ });
160
+ registerVariableGroup(metadata);
161
+ return references;
162
+ }
163
+ function getVariableGroupMetadata(group) {
164
+ const metadata = group[VARIABLE_GROUP_META];
165
+ if (!metadata) {
166
+ throw new Error("Expected a variable group created by defineVars");
167
+ }
168
+ return metadata;
169
+ }
170
+
43
171
  // src/hyphenateStyleName.ts
44
172
  var uppercasePattern = /[A-Z]/g;
45
173
  var msPattern = /^ms-/;
@@ -186,9 +314,13 @@ function createInMemoryStyleApi() {
186
314
  }
187
315
  return () => removeStyleElement(styleElement);
188
316
  }
189
- return {
317
+ const styleApi = {
190
318
  addStyleElement,
191
- addGlobalStyle: addStyleRule,
319
+ addGlobalStyle: addStyleRule
320
+ };
321
+ return {
322
+ ...styleApi,
323
+ detachVariableGroups: attachVariableGroups(styleApi),
192
324
  styleRulesRef: {
193
325
  get current() {
194
326
  return styleRules;
@@ -285,16 +417,23 @@ function PurseProvider(props) {
285
417
  }
286
418
  return { addStyleElement, addGlobalStyle: addStyleRule };
287
419
  }, []);
420
+ useInsertionEffect(() => attachVariableGroups(styleApi), [styleApi]);
288
421
  return /* @__PURE__ */ jsx(PurseContext.Provider, { value: styleApi, children: props.children });
289
422
  }
423
+ function isCustomProperty(name) {
424
+ return name.startsWith("--");
425
+ }
290
426
  function compileDeclarations(declarations) {
291
- return entries(declarations).map(([camelCaseProperty, value]) => {
292
- const kebabProperty = hyphenateStyleName(camelCaseProperty);
293
- const needToAddPixelsUnit = typeof value === "number" && !isUnitlessNumberProperty(kebabProperty);
427
+ return entries(declarations).map(([name, value]) => {
428
+ if (value === void 0 || value === null) {
429
+ return "";
430
+ }
431
+ const property = isCustomProperty(name) ? name : hyphenateStyleName(name);
432
+ const needToAddPixelsUnit = typeof value === "number" && !isCustomProperty(name) && !isUnitlessNumberProperty(property);
294
433
  if (needToAddPixelsUnit) {
295
- return `${kebabProperty}:${value}px;`;
434
+ return `${property}:${value}px;`;
296
435
  } else {
297
- return `${kebabProperty}:${value};`;
436
+ return `${property}:${value};`;
298
437
  }
299
438
  }).join("");
300
439
  }
@@ -421,6 +560,8 @@ function useStyles(...styleElementsOrCSS) {
421
560
  export {
422
561
  PurseProvider,
423
562
  createInMemoryStyleApi,
563
+ defineVars,
564
+ getVariableGroupMetadata,
424
565
  style,
425
566
  useInjectGlobalStyles,
426
567
  useStyles
package/dist/index.js.map CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "version": 3,
3
- "sources": ["../src/purse.tsx", "../src/clsx.ts", "../src/destructors.ts", "../src/hashObject.ts", "../src/hyphenateStyleName.ts", "../src/useRequiredContext.tsx"],
4
- "mappings": "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",
3
+ "sources": ["../src/purse.tsx", "../src/clsx.ts", "../src/destructors.ts", "../src/hashObject.ts", "../src/cssVar.ts", "../src/hyphenateStyleName.ts", "../src/useRequiredContext.tsx"],
4
+ "mappings": "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",
5
5
  "names": ["rule", "styleRules", "value", "composed"]
6
6
  }
package/dist/purse.d.ts CHANGED
@@ -1,14 +1,16 @@
1
1
  import CSS from "csstype";
2
2
  import React, { DependencyList } from "react";
3
- import { CSSVar } from "./cssVar";
4
3
  import { Destructor } from "./destructors";
5
4
  type BaseCSSProperties = CSS.Properties<number | string, string & {}>;
6
- type CSSVarDeclarations = Record<CSSVar, string>;
5
+ type CustomProperties = {
6
+ [name: `--${string}`]: string | number | undefined;
7
+ };
8
+ type Declarations = BaseCSSProperties & CustomProperties;
7
9
  type NestedRuleKey = `&${CSS.SimplePseudos}` | `${CSS.AtRules}${string}` | `.${string}` | `&${string}`;
8
10
  export type CSSProperties = {
9
11
  [Key in keyof BaseCSSProperties]?: BaseCSSProperties[Key];
10
- } & {
11
- [Key in NestedRuleKey]?: BaseCSSProperties;
12
+ } & CustomProperties & {
13
+ [Key in NestedRuleKey]?: Declarations;
12
14
  };
13
15
  export type StyleApi = {
14
16
  addStyleElement: (styleElement: StyleElement) => Destructor;
@@ -31,6 +33,7 @@ export type InMemoryStyleApi = {
31
33
  };
32
34
  addStyleElement: StyleApi["addStyleElement"];
33
35
  addGlobalStyle: StyleApi["addGlobalStyle"];
36
+ detachVariableGroups: Destructor;
34
37
  };
35
38
  export declare function createInMemoryStyleApi(): InMemoryStyleApi;
36
39
  export declare function PurseProvider(props: {
@@ -38,6 +41,6 @@ export declare function PurseProvider(props: {
38
41
  }): import("react/jsx-runtime").JSX.Element;
39
42
  export type StyleArgument = CSSProperties | StyleElement | false | null | undefined;
40
43
  export declare function style(...styleElementsOrCSS: StyleArgument[]): StyleElement;
41
- export declare function useInjectGlobalStyles(selector: string, cssProperties: BaseCSSProperties & CSSVarDeclarations, deps: DependencyList): void;
44
+ export declare function useInjectGlobalStyles(selector: string, cssProperties: Declarations, deps: DependencyList): void;
42
45
  export declare function useStyles(...styleElementsOrCSS: StyleArgument[]): string;
43
46
  export {};
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "purse-styles",
3
3
  "type": "module",
4
4
  "main": "./dist/index.js",
5
- "version": "0.1.2",
5
+ "version": "0.2.0",
6
6
  "author": "Tanishq Kancharla <tanishqkancharla3@gmail.com>",
7
7
  "description": "A basic styling solution for web apps with an emphasis on simplicity and purity.",
8
8
  "keywords": [],
@@ -0,0 +1,238 @@
1
+ import assert from "assert"
2
+ import { describe, expect, expectTypeOf, it } from "vitest"
3
+ import {
4
+ CSSVar,
5
+ compileVariableGroupRules,
6
+ defineVars,
7
+ getVariableGroupMetadata,
8
+ } from "./cssVar"
9
+ import { createInMemoryStyleApi, style } from "./purse"
10
+
11
+ function expectInjectedRules(
12
+ api: ReturnType<typeof createInMemoryStyleApi>,
13
+ metadata: ReturnType<typeof getVariableGroupMetadata>,
14
+ ) {
15
+ for (const rule of compileVariableGroupRules(metadata)) {
16
+ expect(api.styleRulesRef.current).toContain(rule)
17
+ }
18
+ }
19
+
20
+ describe("defineVars", () => {
21
+ it("returns typed var() references for each key", () => {
22
+ const colors = defineVars({
23
+ text: "black",
24
+ accent: "blue",
25
+ })
26
+
27
+ expectTypeOf(colors.text).toEqualTypeOf<CSSVar>()
28
+ expectTypeOf(colors.accent).toEqualTypeOf<CSSVar>()
29
+ expect(colors.text).toMatch(/^var\(--purse-[A-Za-z]+-text\)$/)
30
+ expect(colors.accent).toMatch(/^var\(--purse-[A-Za-z]+-accent\)$/)
31
+ })
32
+
33
+ it("generates deterministic names for the same definitions", () => {
34
+ const first = defineVars({
35
+ text: "black",
36
+ accent: "blue",
37
+ })
38
+ const second = defineVars({
39
+ text: "black",
40
+ accent: "blue",
41
+ })
42
+
43
+ expect(first.text).toBe(second.text)
44
+ expect(first.accent).toBe(second.accent)
45
+ expect(getVariableGroupMetadata(first).groupId).toBe(
46
+ getVariableGroupMetadata(second).groupId,
47
+ )
48
+ })
49
+
50
+ it("ignores object key insertion order when hashing", () => {
51
+ const first = defineVars({
52
+ text: "black",
53
+ accent: "blue",
54
+ })
55
+ const second = defineVars({
56
+ accent: "blue",
57
+ text: "black",
58
+ })
59
+
60
+ expect(first.text).toBe(second.text)
61
+ expect(first.accent).toBe(second.accent)
62
+ })
63
+
64
+ it("rejects keys that start with --", () => {
65
+ expect(() =>
66
+ defineVars({
67
+ "--brand": "blue",
68
+ }),
69
+ ).toThrow(/cannot start with "--"/)
70
+ })
71
+
72
+ it("can be used as style values", () => {
73
+ const colors = defineVars({
74
+ text: "black",
75
+ })
76
+
77
+ const test = style({
78
+ color: colors.text,
79
+ })
80
+
81
+ assert(test.owned !== undefined)
82
+ expect(test.owned.styleRules).toContain(
83
+ `.${test.className}{color:${colors.text};}`,
84
+ )
85
+ })
86
+
87
+ it("stores canonical metadata for later registration", () => {
88
+ const colors = defineVars({
89
+ accent: "blue",
90
+ text: "black",
91
+ })
92
+
93
+ const metadata = getVariableGroupMetadata(colors)
94
+ expect(Object.keys(metadata.definitions)).toEqual(["accent", "text"])
95
+ expect(metadata.names.text).toBe(
96
+ colors.text.slice("var(".length, -1),
97
+ )
98
+ })
99
+ })
100
+
101
+ describe("variable group injection", () => {
102
+ it("compiles scalar defaults onto :root", () => {
103
+ const colors = defineVars({
104
+ accent: "blue",
105
+ text: "black",
106
+ space: 2,
107
+ })
108
+ const metadata = getVariableGroupMetadata(colors)
109
+
110
+ expect(compileVariableGroupRules(metadata)).toEqual([
111
+ `:root{${metadata.names.accent}:blue;${metadata.names.space}:2;${metadata.names.text}:black;}`,
112
+ ])
113
+ })
114
+
115
+ it("injects already-registered groups when a style api attaches", () => {
116
+ const colors = defineVars({
117
+ phase3Existing: "navy",
118
+ })
119
+
120
+ const api = createInMemoryStyleApi()
121
+ expectInjectedRules(api, getVariableGroupMetadata(colors))
122
+ })
123
+
124
+ it("injects groups registered after a style api attaches", () => {
125
+ const api = createInMemoryStyleApi()
126
+ const colors = defineVars({
127
+ phase3Late: "teal",
128
+ })
129
+
130
+ expectInjectedRules(api, getVariableGroupMetadata(colors))
131
+ })
132
+
133
+ it("deduplicates identical variable groups into one root rule", () => {
134
+ const api = createInMemoryStyleApi()
135
+ const first = defineVars({
136
+ phase3Deduped: "purple",
137
+ })
138
+ defineVars({
139
+ phase3Deduped: "purple",
140
+ })
141
+
142
+ const [expectedRule] = compileVariableGroupRules(
143
+ getVariableGroupMetadata(first),
144
+ )
145
+ expect(
146
+ api.styleRulesRef.current.filter((rule) => rule === expectedRule),
147
+ ).toHaveLength(1)
148
+ })
149
+
150
+ it("removes injected variable rules when detached", () => {
151
+ const colors = defineVars({
152
+ phase3Cleanup: "orange",
153
+ })
154
+ const expectedRules = compileVariableGroupRules(
155
+ getVariableGroupMetadata(colors),
156
+ )
157
+ const api = createInMemoryStyleApi()
158
+
159
+ expectInjectedRules(api, getVariableGroupMetadata(colors))
160
+ api.detachVariableGroups()
161
+ for (const rule of expectedRules) {
162
+ expect(api.styleRulesRef.current).not.toContain(rule)
163
+ }
164
+ })
165
+ })
166
+
167
+ describe("conditional variable values", () => {
168
+ const DARK = "@media (prefers-color-scheme: dark)"
169
+
170
+ it("compiles defaults and at-rule overrides", () => {
171
+ const colors = defineVars({
172
+ accent: "blue",
173
+ text: {
174
+ default: "black",
175
+ [DARK]: "white",
176
+ },
177
+ })
178
+ const metadata = getVariableGroupMetadata(colors)
179
+
180
+ expect(compileVariableGroupRules(metadata)).toEqual([
181
+ `:root{${metadata.names.accent}:blue;${metadata.names.text}:black;}`,
182
+ `${DARK}{:root{${metadata.names.text}:white;}}`,
183
+ ])
184
+ })
185
+
186
+ it("groups multiple tokens that share a condition", () => {
187
+ const colors = defineVars({
188
+ background: {
189
+ default: "white",
190
+ [DARK]: "black",
191
+ },
192
+ text: {
193
+ default: "black",
194
+ [DARK]: "white",
195
+ },
196
+ })
197
+ const metadata = getVariableGroupMetadata(colors)
198
+
199
+ expect(compileVariableGroupRules(metadata)).toEqual([
200
+ `:root{${metadata.names.background}:white;${metadata.names.text}:black;}`,
201
+ `${DARK}{:root{${metadata.names.background}:black;${metadata.names.text}:white;}}`,
202
+ ])
203
+ })
204
+
205
+ it("ignores condition key order when hashing", () => {
206
+ const first = defineVars({
207
+ text: {
208
+ default: "black",
209
+ "@supports (color: color(display-p3 1 1 1))": "red",
210
+ [DARK]: "white",
211
+ },
212
+ })
213
+ const second = defineVars({
214
+ text: {
215
+ [DARK]: "white",
216
+ "@supports (color: color(display-p3 1 1 1))": "red",
217
+ default: "black",
218
+ },
219
+ })
220
+
221
+ expect(first.text).toBe(second.text)
222
+ expect(getVariableGroupMetadata(first).groupId).toBe(
223
+ getVariableGroupMetadata(second).groupId,
224
+ )
225
+ })
226
+
227
+ it("injects conditional rules through the style api", () => {
228
+ const api = createInMemoryStyleApi()
229
+ const colors = defineVars({
230
+ phase4Conditional: {
231
+ default: "black",
232
+ [DARK]: "white",
233
+ },
234
+ })
235
+
236
+ expectInjectedRules(api, getVariableGroupMetadata(colors))
237
+ })
238
+ })
package/src/cssVar.ts CHANGED
@@ -1,5 +1,219 @@
1
+ import type CSS from "csstype"
2
+ import { Destructor, joinDestructors } from "./destructors"
3
+ import { hashObject } from "./hashObject"
4
+
1
5
  export type CSSVar = `var(--${string})`
2
6
 
3
- export function createCssVar(name: string): CSSVar {
4
- return `var(--${name})`
7
+ export type VariablePrimitive = string | number
8
+
9
+ export type VariableCondition = `${CSS.AtRules}${string}`
10
+
11
+ export type ConditionalVariableValue<
12
+ T extends VariablePrimitive = VariablePrimitive,
13
+ > = {
14
+ default: T
15
+ } & Partial<Record<VariableCondition, T>>
16
+
17
+ export type VariableDefinition =
18
+ | VariablePrimitive
19
+ | ConditionalVariableValue
20
+
21
+ type NormalizedTokenDefinition = {
22
+ default: VariablePrimitive
23
+ conditions: Record<string, VariablePrimitive>
24
+ }
25
+
26
+ type NormalizedDefinitions = Record<string, NormalizedTokenDefinition>
27
+
28
+ export type VariableGroupMetadata = {
29
+ groupId: string
30
+ definitions: NormalizedDefinitions
31
+ names: Record<string, `--${string}`>
32
+ }
33
+
34
+ export type VariableGroup<T extends Record<string, unknown>> = {
35
+ readonly [K in keyof T]: CSSVar
36
+ }
37
+
38
+ export type AnyVariableGroup = VariableGroup<Record<string, unknown>>
39
+
40
+ export type VariableStyleSink = {
41
+ addGlobalStyle: (styleRule: string) => Destructor
42
+ }
43
+
44
+ const VARIABLE_GROUP_META = Symbol("purse.variableGroup")
45
+
46
+ type VariableGroupWithMeta<T extends Record<string, unknown>> =
47
+ VariableGroup<T> & {
48
+ readonly [VARIABLE_GROUP_META]: VariableGroupMetadata
49
+ }
50
+
51
+ const registeredGroups = new Map<string, VariableGroupMetadata>()
52
+ const listeners = new Set<(metadata: VariableGroupMetadata) => void>()
53
+
54
+ function normalizeDefinition(
55
+ value: VariableDefinition,
56
+ ): NormalizedTokenDefinition {
57
+ if (typeof value !== "object" || value === null) {
58
+ return { default: value, conditions: {} }
59
+ }
60
+
61
+ const conditions: Record<string, VariablePrimitive> = {}
62
+ for (const key of Object.keys(value).sort()) {
63
+ if (key === "default") continue
64
+ conditions[key] = value[key as VariableCondition]!
65
+ }
66
+
67
+ return {
68
+ default: value.default,
69
+ conditions,
70
+ }
71
+ }
72
+
73
+ function canonicalizeDefinitions(
74
+ definitions: Record<string, VariableDefinition>,
75
+ ): NormalizedDefinitions {
76
+ const canonical: NormalizedDefinitions = {}
77
+ for (const key of Object.keys(definitions).sort()) {
78
+ canonical[key] = normalizeDefinition(definitions[key]!)
79
+ }
80
+ return canonical
81
+ }
82
+
83
+ function assertValidTokenKeys(definitions: Record<string, VariableDefinition>) {
84
+ for (const key of Object.keys(definitions)) {
85
+ if (key.startsWith("--")) {
86
+ throw new Error(
87
+ `defineVars keys cannot start with "--" (received "${key}"). Purse generates custom property names automatically.`,
88
+ )
89
+ }
90
+ }
91
+ }
92
+
93
+ function registerVariableGroup(metadata: VariableGroupMetadata) {
94
+ if (registeredGroups.has(metadata.groupId)) {
95
+ return
96
+ }
97
+
98
+ registeredGroups.set(metadata.groupId, metadata)
99
+ for (const listener of listeners) {
100
+ listener(metadata)
101
+ }
102
+ }
103
+
104
+ export function compileVariableGroupRules(
105
+ metadata: VariableGroupMetadata,
106
+ ): string[] {
107
+ let rootDeclarations = ""
108
+ const declarationsByCondition = new Map<string, string>()
109
+
110
+ for (const key of Object.keys(metadata.definitions)) {
111
+ const definition = metadata.definitions[key]!
112
+ const customProperty = metadata.names[key]!
113
+ rootDeclarations += `${customProperty}:${definition.default};`
114
+
115
+ for (const condition of Object.keys(definition.conditions).sort()) {
116
+ const existing = declarationsByCondition.get(condition) ?? ""
117
+ declarationsByCondition.set(
118
+ condition,
119
+ `${existing}${customProperty}:${definition.conditions[condition]};`,
120
+ )
121
+ }
122
+ }
123
+
124
+ const rules = [`:root{${rootDeclarations}}`]
125
+ for (const condition of [...declarationsByCondition.keys()].sort()) {
126
+ rules.push(
127
+ `${condition}{:root{${declarationsByCondition.get(condition)}}}`,
128
+ )
129
+ }
130
+ return rules
131
+ }
132
+
133
+ export function subscribeVariableGroups(
134
+ listener: (metadata: VariableGroupMetadata) => void,
135
+ ): Destructor {
136
+ listeners.add(listener)
137
+
138
+ for (const metadata of registeredGroups.values()) {
139
+ listener(metadata)
140
+ }
141
+
142
+ return () => {
143
+ listeners.delete(listener)
144
+ }
145
+ }
146
+
147
+ export function attachVariableGroups(
148
+ styleApi: VariableStyleSink,
149
+ ): Destructor {
150
+ const insertedGroups = new Map<string, Destructor>()
151
+
152
+ function ensureGroup(metadata: VariableGroupMetadata) {
153
+ if (insertedGroups.has(metadata.groupId)) {
154
+ return
155
+ }
156
+
157
+ const destructor = joinDestructors(
158
+ compileVariableGroupRules(metadata).map((rule) =>
159
+ styleApi.addGlobalStyle(rule),
160
+ ),
161
+ )
162
+ insertedGroups.set(metadata.groupId, destructor)
163
+ }
164
+
165
+ const unsubscribe = subscribeVariableGroups(ensureGroup)
166
+
167
+ return () => {
168
+ unsubscribe()
169
+ for (const destructor of insertedGroups.values()) {
170
+ destructor()
171
+ }
172
+ insertedGroups.clear()
173
+ }
174
+ }
175
+
176
+ export function defineVars<
177
+ const T extends Record<string, VariableDefinition>,
178
+ >(definitions: T): VariableGroup<T> {
179
+ assertValidTokenKeys(definitions)
180
+
181
+ const canonicalDefinitions = canonicalizeDefinitions(definitions)
182
+ const groupId = hashObject(canonicalDefinitions)
183
+ const names: Record<string, `--${string}`> = {}
184
+ const references = {} as VariableGroupWithMeta<T>
185
+
186
+ for (const key of Object.keys(canonicalDefinitions)) {
187
+ const customProperty = `--purse-${groupId}-${key}` as const
188
+ names[key] = customProperty
189
+ ;(references as Record<string, CSSVar>)[key] =
190
+ `var(${customProperty})`
191
+ }
192
+
193
+ const metadata = {
194
+ groupId,
195
+ definitions: canonicalDefinitions,
196
+ names,
197
+ } satisfies VariableGroupMetadata
198
+
199
+ Object.defineProperty(references, VARIABLE_GROUP_META, {
200
+ value: metadata,
201
+ enumerable: false,
202
+ })
203
+
204
+ registerVariableGroup(metadata)
205
+
206
+ return references
207
+ }
208
+
209
+ export function getVariableGroupMetadata(
210
+ group: AnyVariableGroup,
211
+ ): VariableGroupMetadata {
212
+ const metadata = (group as VariableGroupWithMeta<Record<string, unknown>>)[
213
+ VARIABLE_GROUP_META
214
+ ]
215
+ if (!metadata) {
216
+ throw new Error("Expected a variable group created by defineVars")
217
+ }
218
+ return metadata
5
219
  }
package/src/index.ts CHANGED
@@ -10,3 +10,15 @@ export {
10
10
  useInjectGlobalStyles,
11
11
  useStyles,
12
12
  } from "./purse"
13
+ export {
14
+ AnyVariableGroup,
15
+ CSSVar,
16
+ ConditionalVariableValue,
17
+ VariableCondition,
18
+ VariableDefinition,
19
+ VariableGroup,
20
+ VariableGroupMetadata,
21
+ VariablePrimitive,
22
+ defineVars,
23
+ getVariableGroupMetadata,
24
+ } from "./cssVar"
package/src/purse.test.ts CHANGED
@@ -125,6 +125,28 @@ describe("Purse", () => {
125
125
  )
126
126
  })
127
127
 
128
+ it("Compiles custom property declarations without adding px", () => {
129
+ const test = style({
130
+ "--space": 2,
131
+ })
132
+
133
+ assert(test.owned !== undefined)
134
+ expect(test.owned.styleRules).toContain(
135
+ `.${test.className}{--space:2;}`,
136
+ )
137
+ })
138
+
139
+ it("Compiles custom property references as ordinary values", () => {
140
+ const test = style({
141
+ color: "var(--color)",
142
+ })
143
+
144
+ assert(test.owned !== undefined)
145
+ expect(test.owned.styleRules).toContain(
146
+ `.${test.className}{color:var(--color);}`,
147
+ )
148
+ })
149
+
128
150
  it.skip("Inserts into given style element correctly", () => {})
129
151
  it.skip("Works if you pass style rule or object to useStyles", () => {})
130
152
  it.skip("Works if you pass multiple style rule or object to useStyles", () => {})
package/src/purse.tsx CHANGED
@@ -8,7 +8,7 @@ import React, {
8
8
  useMemo,
9
9
  } from "react"
10
10
  import { clsx } from "./clsx"
11
- import { CSSVar } from "./cssVar"
11
+ import { attachVariableGroups } from "./cssVar"
12
12
  import { Destructor, joinDestructors } from "./destructors"
13
13
  import { hashObject } from "./hashObject"
14
14
  import { hyphenateStyleName } from "./hyphenateStyleName"
@@ -80,7 +80,12 @@ function isUnitlessNumberProperty(kebabProperty: string): boolean {
80
80
  }
81
81
 
82
82
  type BaseCSSProperties = CSS.Properties<number | string, string & {}>
83
- type CSSVarDeclarations = Record<CSSVar, string>
83
+
84
+ type CustomProperties = {
85
+ [name: `--${string}`]: string | number | undefined
86
+ }
87
+
88
+ type Declarations = BaseCSSProperties & CustomProperties
84
89
 
85
90
  type NestedRuleKey =
86
91
  | `&${CSS.SimplePseudos}`
@@ -90,8 +95,8 @@ type NestedRuleKey =
90
95
 
91
96
  export type CSSProperties = {
92
97
  [Key in keyof BaseCSSProperties]?: BaseCSSProperties[Key]
93
- } & {
94
- [Key in NestedRuleKey]?: BaseCSSProperties
98
+ } & CustomProperties & {
99
+ [Key in NestedRuleKey]?: Declarations
95
100
  }
96
101
 
97
102
  const PurseContext = createContext<StyleApi | undefined>(undefined)
@@ -130,6 +135,7 @@ export type InMemoryStyleApi = {
130
135
  styleRulesRef: { current: string[] }
131
136
  addStyleElement: StyleApi["addStyleElement"]
132
137
  addGlobalStyle: StyleApi["addGlobalStyle"]
138
+ detachVariableGroups: Destructor
133
139
  }
134
140
 
135
141
  export function createInMemoryStyleApi(): InMemoryStyleApi {
@@ -208,9 +214,14 @@ export function createInMemoryStyleApi(): InMemoryStyleApi {
208
214
  return () => removeStyleElement(styleElement)
209
215
  }
210
216
 
211
- return {
217
+ const styleApi = {
212
218
  addStyleElement,
213
219
  addGlobalStyle: addStyleRule,
220
+ }
221
+
222
+ return {
223
+ ...styleApi,
224
+ detachVariableGroups: attachVariableGroups(styleApi),
214
225
  styleRulesRef: {
215
226
  get current() {
216
227
  return styleRules
@@ -344,6 +355,8 @@ export function PurseProvider(props: { children?: React.ReactNode }) {
344
355
  return { addStyleElement, addGlobalStyle: addStyleRule }
345
356
  }, [])
346
357
 
358
+ useInsertionEffect(() => attachVariableGroups(styleApi), [styleApi])
359
+
347
360
  return (
348
361
  <PurseContext.Provider value={styleApi}>
349
362
  {props.children}
@@ -351,19 +364,30 @@ export function PurseProvider(props: { children?: React.ReactNode }) {
351
364
  )
352
365
  }
353
366
 
354
- function compileDeclarations(declarations: BaseCSSProperties) {
367
+ function isCustomProperty(name: string): name is `--${string}` {
368
+ return name.startsWith("--")
369
+ }
370
+
371
+ function compileDeclarations(declarations: Declarations) {
355
372
  // return entries(prefix(declarations))
356
373
  return entries(declarations)
357
- .map(([camelCaseProperty, value]) => {
358
- const kebabProperty = hyphenateStyleName(camelCaseProperty)
374
+ .map(([name, value]) => {
375
+ if (value === undefined || value === null) {
376
+ return ""
377
+ }
378
+
379
+ const property = isCustomProperty(name)
380
+ ? name
381
+ : hyphenateStyleName(name)
359
382
  const needToAddPixelsUnit =
360
383
  typeof value === "number" &&
361
- !isUnitlessNumberProperty(kebabProperty)
384
+ !isCustomProperty(name) &&
385
+ !isUnitlessNumberProperty(property)
362
386
 
363
387
  if (needToAddPixelsUnit) {
364
- return `${kebabProperty}:${value}px;`
388
+ return `${property}:${value}px;`
365
389
  } else {
366
- return `${kebabProperty}:${value};`
390
+ return `${property}:${value};`
367
391
  }
368
392
  })
369
393
  .join("")
@@ -394,9 +418,9 @@ function isObjectEmpty(obj: {}): boolean {
394
418
  }
395
419
 
396
420
  type CSSPropertiesGroup = {
397
- "": BaseCSSProperties
421
+ "": Declarations
398
422
  } & {
399
- [Key in NestedRuleKey]?: BaseCSSProperties
423
+ [Key in NestedRuleKey]?: Declarations
400
424
  }
401
425
 
402
426
  function mergeCSSProperties(...groups: CSSProperties[]): CSSProperties {
@@ -535,7 +559,7 @@ function styleElementToRules({ owned, composed }: StyleElement): StyleRule[] {
535
559
 
536
560
  export function useInjectGlobalStyles(
537
561
  selector: string,
538
- cssProperties: BaseCSSProperties & CSSVarDeclarations,
562
+ cssProperties: Declarations,
539
563
  deps: DependencyList,
540
564
  ) {
541
565
  const styleApi = useRequiredContext(PurseContext)