Skip to content

Commit 26832f1

Browse files
Add createGlobalThemeContract function (#319)
Co-authored-by: Mark Dalgleish <[email protected]>
1 parent c0c4b24 commit 26832f1

File tree

6 files changed

+444
-5
lines changed

6 files changed

+444
-5
lines changed

.changeset/shiny-bees-rest.md

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
---
2+
'@vanilla-extract/css': minor
3+
---
4+
5+
Add `createGlobalThemeContract` function
6+
7+
Creates a contract of globally scoped variable names for themes to implement.
8+
9+
> 💡 This is useful if you want to make your theme contract available to non-JavaScript environments.
10+
11+
```ts
12+
// themes.css.ts
13+
import {
14+
createGlobalThemeContract,
15+
createGlobalTheme
16+
} from '@vanilla-extract/css';
17+
18+
export const vars = createGlobalThemeContract({
19+
color: {
20+
brand: 'color-brand'
21+
},
22+
font: {
23+
body: 'font-body'
24+
}
25+
});
26+
27+
createGlobalTheme(':root', vars, {
28+
color: {
29+
brand: 'blue'
30+
},
31+
font: {
32+
body: 'arial'
33+
}
34+
});
35+
```
36+
37+
You can also provide a map function as the second argument which has access to the value and the object path.
38+
39+
For example, you can automatically prefix all variable names.
40+
41+
```ts
42+
// themes.css.ts
43+
import {
44+
createGlobalThemeContract,
45+
createGlobalTheme
46+
} from '@vanilla-extract/css';
47+
48+
export const vars = createGlobalThemeContract(
49+
{
50+
color: {
51+
brand: 'color-brand'
52+
},
53+
font: {
54+
body: 'font-body'
55+
}
56+
},
57+
(value) => `prefix-${value}`
58+
);
59+
```
60+
61+
You can also use the map function to automatically generate names from the object path, joining keys with a hyphen.
62+
63+
```ts
64+
// themes.css.ts
65+
import {
66+
createGlobalThemeContract,
67+
createGlobalTheme
68+
} from '@vanilla-extract/css';
69+
70+
export const vars = createGlobalThemeContract(
71+
{
72+
color: {
73+
brand: null
74+
},
75+
font: {
76+
body: null
77+
}
78+
},
79+
(_value, path) => `prefix-${path.join('-')}`
80+
);
81+
```

README.md

Lines changed: 76 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -581,7 +581,7 @@ createGlobalTheme(':root', vars, {
581581

582582
### createThemeContract
583583

584-
Creates a contract for themes to implement.
584+
Creates a contract of locally scoped variable names for themes to implement.
585585

586586
**Ensure this function is called within a `.css.ts` context, otherwise variable names will be mismatched between files.**
587587

@@ -623,6 +623,81 @@ export const themeB = createTheme(vars, {
623623
});
624624
```
625625

626+
### createGlobalThemeContract
627+
628+
Creates a contract of globally scoped variable names for themes to implement.
629+
630+
> 💡 This is useful if you want to make your theme contract available to non-JavaScript environments.
631+
632+
```ts
633+
// themes.css.ts
634+
635+
import {
636+
createGlobalThemeContract,
637+
createGlobalTheme
638+
} from '@vanilla-extract/css';
639+
640+
export const vars = createGlobalThemeContract({
641+
color: {
642+
brand: 'color-brand'
643+
},
644+
font: {
645+
body: 'font-body'
646+
}
647+
});
648+
649+
createGlobalTheme(':root', vars, {
650+
color: {
651+
brand: 'blue'
652+
},
653+
font: {
654+
body: 'arial'
655+
}
656+
});
657+
```
658+
659+
You can also provide a map function as the second argument which has access to the value and the object path.
660+
661+
For example, you can automatically prefix all variable names.
662+
663+
```ts
664+
// themes.css.ts
665+
666+
import {
667+
createGlobalThemeContract,
668+
createGlobalTheme
669+
} from '@vanilla-extract/css';
670+
671+
export const vars = createGlobalThemeContract({
672+
color: {
673+
brand: 'color-brand'
674+
},
675+
font: {
676+
body: 'font-body'
677+
}
678+
}, (value) => `prefix-${value}`);
679+
```
680+
681+
You can also use the map function to automatically generate names from the object path, joining keys with a hyphen.
682+
683+
```ts
684+
// themes.css.ts
685+
686+
import {
687+
createGlobalThemeContract,
688+
createGlobalTheme
689+
} from '@vanilla-extract/css';
690+
691+
export const vars = createGlobalThemeContract({
692+
color: {
693+
brand: null
694+
},
695+
font: {
696+
body: null
697+
}
698+
}, (_value, path) => `prefix-${path.join('-')}`);
699+
```
700+
626701
### assignVars
627702

628703
Assigns a collection of CSS Variables anywhere within a style block.

packages/css/src/vars.test.ts

Lines changed: 173 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { fallbackVar } from './vars';
1+
import { fallbackVar, createGlobalThemeContract } from './vars';
22

33
describe('fallbackVar', () => {
44
it('supports a single string fallback', () => {
@@ -67,3 +67,175 @@ describe('fallbackVar', () => {
6767
}).toThrowErrorMatchingInlineSnapshot(`"Invalid variable name: INVALID"`);
6868
});
6969
});
70+
71+
describe('createGlobalThemeContract', () => {
72+
it('supports defining css vars via object properties', () => {
73+
expect(
74+
createGlobalThemeContract({
75+
color: {
76+
red: 'color-red',
77+
blue: 'color-blue',
78+
green: 'color-green',
79+
},
80+
}),
81+
).toMatchInlineSnapshot(`
82+
Object {
83+
"color": Object {
84+
"blue": "var(--color-blue)",
85+
"green": "var(--color-green)",
86+
"red": "var(--color-red)",
87+
},
88+
}
89+
`);
90+
});
91+
92+
it('ignores leading double hyphen', () => {
93+
expect(
94+
createGlobalThemeContract({
95+
color: {
96+
red: '--color-red',
97+
blue: '--color-blue',
98+
green: '--color-green',
99+
},
100+
}),
101+
).toMatchInlineSnapshot(`
102+
Object {
103+
"color": Object {
104+
"blue": "var(--color-blue)",
105+
"green": "var(--color-green)",
106+
"red": "var(--color-red)",
107+
},
108+
}
109+
`);
110+
});
111+
112+
it('supports adding a prefix', () => {
113+
expect(
114+
createGlobalThemeContract(
115+
{
116+
color: {
117+
red: 'color-red',
118+
blue: 'color-blue',
119+
green: 'color-green',
120+
},
121+
},
122+
(value) => `prefix-${value}`,
123+
),
124+
).toMatchInlineSnapshot(`
125+
Object {
126+
"color": Object {
127+
"blue": "var(--prefix-color-blue)",
128+
"green": "var(--prefix-color-green)",
129+
"red": "var(--prefix-color-red)",
130+
},
131+
}
132+
`);
133+
});
134+
135+
it('ignores leading double hyphen when adding a prefix', () => {
136+
expect(
137+
createGlobalThemeContract(
138+
{
139+
color: {
140+
red: 'color-red',
141+
blue: 'color-blue',
142+
green: 'color-green',
143+
},
144+
},
145+
(value) => `--prefix-${value}`,
146+
),
147+
).toMatchInlineSnapshot(`
148+
Object {
149+
"color": Object {
150+
"blue": "var(--prefix-color-blue)",
151+
"green": "var(--prefix-color-green)",
152+
"red": "var(--prefix-color-red)",
153+
},
154+
}
155+
`);
156+
});
157+
158+
it('supports path based names', () => {
159+
expect(
160+
createGlobalThemeContract(
161+
{
162+
color: {
163+
red: null,
164+
blue: null,
165+
green: null,
166+
},
167+
},
168+
(_, path) => `prefix-${path.join('-')}`,
169+
),
170+
).toMatchInlineSnapshot(`
171+
Object {
172+
"color": Object {
173+
"blue": "var(--prefix-color-blue)",
174+
"green": "var(--prefix-color-green)",
175+
"red": "var(--prefix-color-red)",
176+
},
177+
}
178+
`);
179+
});
180+
181+
it('errors when invalid property value', () => {
182+
expect(() =>
183+
createGlobalThemeContract({
184+
color: {
185+
// @ts-expect-error
186+
red: null,
187+
blue: 'color-blue',
188+
green: 'color-green',
189+
},
190+
}),
191+
).toThrowErrorMatchingInlineSnapshot(
192+
`"Invalid variable name for \\"color.red\\": null"`,
193+
);
194+
});
195+
196+
it('errors when escaped property value', () => {
197+
expect(() =>
198+
createGlobalThemeContract({
199+
color: {
200+
red: 'color-red',
201+
blue: "color'blue",
202+
green: 'color-green',
203+
},
204+
}),
205+
).toThrowErrorMatchingInlineSnapshot(
206+
`"Invalid variable name for \\"color.blue\\": color'blue"`,
207+
);
208+
});
209+
210+
it('errors when property value starts with a number', () => {
211+
expect(() =>
212+
createGlobalThemeContract({
213+
color: {
214+
red: 'color-red',
215+
blue: 'color-blue',
216+
green: '123-color-green',
217+
},
218+
}),
219+
).toThrowErrorMatchingInlineSnapshot(
220+
`"Invalid variable name for \\"color.green\\": 123-color-green"`,
221+
);
222+
});
223+
224+
it('errors when invalid map value', () => {
225+
expect(() =>
226+
createGlobalThemeContract(
227+
{
228+
color: {
229+
red: 'color-red',
230+
blue: 'color-blue',
231+
green: 'color-green',
232+
},
233+
},
234+
// @ts-expect-error
235+
() => null,
236+
),
237+
).toThrowErrorMatchingInlineSnapshot(
238+
`"Invalid variable name for \\"color.red\\": null"`,
239+
);
240+
});
241+
});

0 commit comments

Comments
 (0)