purse-styles 0.1.1 → 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-/;
@@ -111,6 +239,20 @@ var UNITLESS_NUMBER_PROPS = [
111
239
  "stroke-opacity",
112
240
  "stroke-width"
113
241
  ];
242
+ var VENDOR_PREFIXES = ["-webkit-", "-moz-", "-ms-", "-o-"];
243
+ function isUnitlessNumberProperty(kebabProperty) {
244
+ if (UNITLESS_NUMBER_PROPS.includes(kebabProperty)) {
245
+ return true;
246
+ }
247
+ for (const prefix of VENDOR_PREFIXES) {
248
+ if (!kebabProperty.startsWith(prefix)) continue;
249
+ const unprefixed = kebabProperty.slice(prefix.length);
250
+ if (UNITLESS_NUMBER_PROPS.includes(unprefixed)) {
251
+ return true;
252
+ }
253
+ }
254
+ return false;
255
+ }
114
256
  var PurseContext = createContext(void 0);
115
257
  var __style__ = Symbol("style-element");
116
258
  function isStyleElement(o) {
@@ -172,9 +314,13 @@ function createInMemoryStyleApi() {
172
314
  }
173
315
  return () => removeStyleElement(styleElement);
174
316
  }
175
- return {
317
+ const styleApi = {
176
318
  addStyleElement,
177
- addGlobalStyle: addStyleRule,
319
+ addGlobalStyle: addStyleRule
320
+ };
321
+ return {
322
+ ...styleApi,
323
+ detachVariableGroups: attachVariableGroups(styleApi),
178
324
  styleRulesRef: {
179
325
  get current() {
180
326
  return styleRules;
@@ -271,16 +417,23 @@ function PurseProvider(props) {
271
417
  }
272
418
  return { addStyleElement, addGlobalStyle: addStyleRule };
273
419
  }, []);
420
+ useInsertionEffect(() => attachVariableGroups(styleApi), [styleApi]);
274
421
  return /* @__PURE__ */ jsx(PurseContext.Provider, { value: styleApi, children: props.children });
275
422
  }
423
+ function isCustomProperty(name) {
424
+ return name.startsWith("--");
425
+ }
276
426
  function compileDeclarations(declarations) {
277
- return entries(declarations).map(([camelCaseProperty, value]) => {
278
- const kebabProperty = hyphenateStyleName(camelCaseProperty);
279
- const needToAddPixelsUnit = typeof value === "number" && !UNITLESS_NUMBER_PROPS.includes(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);
280
433
  if (needToAddPixelsUnit) {
281
- return `${kebabProperty}:${value}px;`;
434
+ return `${property}:${value}px;`;
282
435
  } else {
283
- return `${kebabProperty}:${value};`;
436
+ return `${property}:${value};`;
284
437
  }
285
438
  }).join("");
286
439
  }
@@ -407,6 +560,8 @@ function useStyles(...styleElementsOrCSS) {
407
560
  export {
408
561
  PurseProvider,
409
562
  createInMemoryStyleApi,
563
+ defineVars,
564
+ getVariableGroupMetadata,
410
565
  style,
411
566
  useInjectGlobalStyles,
412
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.1",
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
@@ -101,6 +101,52 @@ describe("Purse", () => {
101
101
  assert.ok(composed.className.includes(composed.owned.className))
102
102
  })
103
103
 
104
+ it("Keeps vendor-prefixed unitless number properties dimensionless", () => {
105
+ const test = style({
106
+ WebkitLineClamp: 3,
107
+ MozLineClamp: 2,
108
+ lineClamp: 1,
109
+ })
110
+
111
+ assert(test.owned !== undefined)
112
+ expect(test.owned.styleRules).toContain(
113
+ `.${test.className}{-webkit-line-clamp:3;-moz-line-clamp:2;line-clamp:1;}`,
114
+ )
115
+ })
116
+
117
+ it("Still adds px to vendor-prefixed dimensional properties", () => {
118
+ const test = style({
119
+ WebkitTransform: 10,
120
+ })
121
+
122
+ assert(test.owned !== undefined)
123
+ expect(test.owned.styleRules).toContain(
124
+ `.${test.className}{-webkit-transform:10px;}`,
125
+ )
126
+ })
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
+
104
150
  it.skip("Inserts into given style element correctly", () => {})
105
151
  it.skip("Works if you pass style rule or object to useStyles", () => {})
106
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"
@@ -60,8 +60,32 @@ const UNITLESS_NUMBER_PROPS = [
60
60
  "stroke-width",
61
61
  ]
62
62
 
63
+ const VENDOR_PREFIXES = ["-webkit-", "-moz-", "-ms-", "-o-"] as const
64
+
65
+ /** True when a numeric value should compile without an implicit `px` suffix. */
66
+ function isUnitlessNumberProperty(kebabProperty: string): boolean {
67
+ if (UNITLESS_NUMBER_PROPS.includes(kebabProperty)) {
68
+ return true
69
+ }
70
+
71
+ for (const prefix of VENDOR_PREFIXES) {
72
+ if (!kebabProperty.startsWith(prefix)) continue
73
+ const unprefixed = kebabProperty.slice(prefix.length)
74
+ if (UNITLESS_NUMBER_PROPS.includes(unprefixed)) {
75
+ return true
76
+ }
77
+ }
78
+
79
+ return false
80
+ }
81
+
63
82
  type BaseCSSProperties = CSS.Properties<number | string, string & {}>
64
- type CSSVarDeclarations = Record<CSSVar, string>
83
+
84
+ type CustomProperties = {
85
+ [name: `--${string}`]: string | number | undefined
86
+ }
87
+
88
+ type Declarations = BaseCSSProperties & CustomProperties
65
89
 
66
90
  type NestedRuleKey =
67
91
  | `&${CSS.SimplePseudos}`
@@ -71,8 +95,8 @@ type NestedRuleKey =
71
95
 
72
96
  export type CSSProperties = {
73
97
  [Key in keyof BaseCSSProperties]?: BaseCSSProperties[Key]
74
- } & {
75
- [Key in NestedRuleKey]?: BaseCSSProperties
98
+ } & CustomProperties & {
99
+ [Key in NestedRuleKey]?: Declarations
76
100
  }
77
101
 
78
102
  const PurseContext = createContext<StyleApi | undefined>(undefined)
@@ -111,6 +135,7 @@ export type InMemoryStyleApi = {
111
135
  styleRulesRef: { current: string[] }
112
136
  addStyleElement: StyleApi["addStyleElement"]
113
137
  addGlobalStyle: StyleApi["addGlobalStyle"]
138
+ detachVariableGroups: Destructor
114
139
  }
115
140
 
116
141
  export function createInMemoryStyleApi(): InMemoryStyleApi {
@@ -189,9 +214,14 @@ export function createInMemoryStyleApi(): InMemoryStyleApi {
189
214
  return () => removeStyleElement(styleElement)
190
215
  }
191
216
 
192
- return {
217
+ const styleApi = {
193
218
  addStyleElement,
194
219
  addGlobalStyle: addStyleRule,
220
+ }
221
+
222
+ return {
223
+ ...styleApi,
224
+ detachVariableGroups: attachVariableGroups(styleApi),
195
225
  styleRulesRef: {
196
226
  get current() {
197
227
  return styleRules
@@ -325,6 +355,8 @@ export function PurseProvider(props: { children?: React.ReactNode }) {
325
355
  return { addStyleElement, addGlobalStyle: addStyleRule }
326
356
  }, [])
327
357
 
358
+ useInsertionEffect(() => attachVariableGroups(styleApi), [styleApi])
359
+
328
360
  return (
329
361
  <PurseContext.Provider value={styleApi}>
330
362
  {props.children}
@@ -332,19 +364,30 @@ export function PurseProvider(props: { children?: React.ReactNode }) {
332
364
  )
333
365
  }
334
366
 
335
- function compileDeclarations(declarations: BaseCSSProperties) {
367
+ function isCustomProperty(name: string): name is `--${string}` {
368
+ return name.startsWith("--")
369
+ }
370
+
371
+ function compileDeclarations(declarations: Declarations) {
336
372
  // return entries(prefix(declarations))
337
373
  return entries(declarations)
338
- .map(([camelCaseProperty, value]) => {
339
- 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)
340
382
  const needToAddPixelsUnit =
341
383
  typeof value === "number" &&
342
- !UNITLESS_NUMBER_PROPS.includes(kebabProperty)
384
+ !isCustomProperty(name) &&
385
+ !isUnitlessNumberProperty(property)
343
386
 
344
387
  if (needToAddPixelsUnit) {
345
- return `${kebabProperty}:${value}px;`
388
+ return `${property}:${value}px;`
346
389
  } else {
347
- return `${kebabProperty}:${value};`
390
+ return `${property}:${value};`
348
391
  }
349
392
  })
350
393
  .join("")
@@ -375,9 +418,9 @@ function isObjectEmpty(obj: {}): boolean {
375
418
  }
376
419
 
377
420
  type CSSPropertiesGroup = {
378
- "": BaseCSSProperties
421
+ "": Declarations
379
422
  } & {
380
- [Key in NestedRuleKey]?: BaseCSSProperties
423
+ [Key in NestedRuleKey]?: Declarations
381
424
  }
382
425
 
383
426
  function mergeCSSProperties(...groups: CSSProperties[]): CSSProperties {
@@ -516,7 +559,7 @@ function styleElementToRules({ owned, composed }: StyleElement): StyleRule[] {
516
559
 
517
560
  export function useInjectGlobalStyles(
518
561
  selector: string,
519
- cssProperties: BaseCSSProperties & CSSVarDeclarations,
562
+ cssProperties: Declarations,
520
563
  deps: DependencyList,
521
564
  ) {
522
565
  const styleApi = useRequiredContext(PurseContext)