Skip to content

Commit 4abfc0b

Browse files
authored
createTheme, createGlobalTheme: Add support for assigning themes to a layer (#1512)
1 parent dde9bf2 commit 4abfc0b

16 files changed

+515
-55
lines changed

.changeset/nasty-wasps-tie.md

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
---
2+
'@vanilla-extract/css': minor
3+
---
4+
5+
`createTheme`, `createGlobalTheme`: Add support for assigning themes to a layer
6+
7+
Themes can now be assigned to a layer by name using the `@layer` key at the top-level of the theme definition.
8+
9+
**EXAMPLE USAGE**:
10+
11+
```ts
12+
// themes.css.ts
13+
import { createTheme, createGlobalTheme, layer } from '@vanilla-extract/css';
14+
15+
export const themeLayer = layer();
16+
17+
export const [themeA, vars] = createTheme({
18+
'@layer': themeLayer,
19+
color: {
20+
brand: 'blue'
21+
},
22+
font: {
23+
body: 'arial'
24+
}
25+
});
26+
27+
export const vars = createGlobalTheme(':root', {
28+
'@layer': themeLayer,
29+
space: {
30+
small: '10px',
31+
large: '20px',
32+
}
33+
});
34+
```
35+
36+
This will generate the following CSS:
37+
38+
```css
39+
@layer themes_themeLayer__1k6oxph0;
40+
@layer themes_themeLayer__1k6oxph0 {
41+
.themes_themeA__1k6oxph1 {
42+
--color-brand__1k6oxph2: blue;
43+
--font-body__1k6oxph3: arial;
44+
}
45+
46+
:root {
47+
--space-small__z05zdf1: 10px;
48+
--space-large__z05zdf2: 20px;
49+
}
50+
}
51+
```

fixtures/themed/src/themes.css.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import {
33
createTheme,
44
assignVars,
55
style,
6+
layer,
67
} from '@vanilla-extract/css';
78

89
export const theme = style({});
@@ -31,6 +32,36 @@ export const altTheme = createTheme(vars, {
3132
},
3233
});
3334

35+
const themeLayer = layer();
36+
37+
// Not tested visually, exported for CSS output testing
38+
export const [altTheme2Class, altTheme2Contract] = createTheme({
39+
'@layer': themeLayer,
40+
colors: {
41+
backgroundColor: 'green',
42+
text: 'white',
43+
},
44+
space: {
45+
1: '8px',
46+
2: '12px',
47+
3: '16px',
48+
},
49+
});
50+
51+
// Not tested visually, exported for CSS output testing
52+
export const altTheme3 = createGlobalTheme(':root', altTheme2Contract, {
53+
'@layer': 'globalThemeLayer',
54+
colors: {
55+
backgroundColor: 'green',
56+
text: 'white',
57+
},
58+
space: {
59+
1: '8px',
60+
2: '12px',
61+
3: '16px',
62+
},
63+
});
64+
3465
export const responsiveTheme = style({
3566
vars: assignVars(vars, {
3667
colors: {

packages/css/src/theme.ts

Lines changed: 52 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,55 +1,77 @@
11
import type { Contract, MapLeafNodes } from '@vanilla-extract/private';
2-
import type { ThemeVars, Tokens } from './types';
2+
import type { GlobalStyleRule, Resolve, ThemeVars, Tokens } from './types';
33
import { appendCss, registerClassName } from './adapter';
44
import { getFileScope } from './fileScope';
55
import { generateIdentifier } from './identifier';
66
import { createThemeContract, assignVars } from './vars';
77

8+
type WithOptionalLayer<T extends Tokens> = T & {
9+
'@layer'?: string;
10+
};
11+
12+
type WithoutLayer<T> = Omit<T, '@layer'>;
13+
814
export function createGlobalTheme<ThemeTokens extends Tokens>(
915
selector: string,
10-
tokens: ThemeTokens,
11-
): ThemeVars<ThemeTokens>;
16+
tokens: WithOptionalLayer<ThemeTokens>,
17+
): Resolve<WithoutLayer<ThemeVars<ThemeTokens>>>;
1218
export function createGlobalTheme<ThemeContract extends Contract>(
1319
selector: string,
1420
themeContract: ThemeContract,
15-
tokens: MapLeafNodes<ThemeContract, string>,
21+
tokens: WithOptionalLayer<MapLeafNodes<ThemeContract, string>>,
1622
): void;
1723
export function createGlobalTheme(
1824
selector: string,
1925
arg2: any,
2026
arg3?: any,
2127
): any {
22-
const shouldCreateVars = Boolean(!arg3);
28+
const themeContractProvided = Boolean(arg3);
29+
30+
const tokenArg = (
31+
themeContractProvided ? arg3 : arg2
32+
) as WithOptionalLayer<Tokens>;
2333

24-
const themeVars = shouldCreateVars
25-
? createThemeContract(arg2)
26-
: (arg2 as ThemeVars<any>);
34+
const { layerName, tokens } = extractLayerFromTokens(tokenArg);
2735

28-
const tokens = shouldCreateVars ? arg2 : arg3;
36+
const themeContract = themeContractProvided
37+
? (arg2 as ThemeVars<any>)
38+
: createThemeContract(tokens);
39+
40+
let rule: GlobalStyleRule = {
41+
vars: assignVars(themeContract, tokens),
42+
};
43+
44+
if (layerName) {
45+
rule = {
46+
'@layer': {
47+
[layerName]: rule,
48+
},
49+
};
50+
}
2951

3052
appendCss(
3153
{
3254
type: 'global',
3355
selector: selector,
34-
rule: { vars: assignVars(themeVars, tokens) },
56+
rule,
3557
},
3658
getFileScope(),
3759
);
3860

39-
if (shouldCreateVars) {
40-
return themeVars;
61+
if (!themeContractProvided) {
62+
return themeContract;
4163
}
4264
}
4365

4466
export function createTheme<ThemeContract extends Contract>(
4567
themeContract: ThemeContract,
46-
tokens: MapLeafNodes<ThemeContract, string>,
68+
tokens: WithOptionalLayer<MapLeafNodes<ThemeContract, string>>,
4769
debugId?: string,
4870
): string;
4971
export function createTheme<ThemeTokens extends Tokens>(
50-
tokens: ThemeTokens,
72+
tokens: WithOptionalLayer<ThemeTokens>,
5173
debugId?: string,
52-
): [className: string, vars: ThemeVars<ThemeTokens>];
74+
): [className: string, vars: Resolve<WithoutLayer<ThemeVars<ThemeTokens>>>];
5375
export function createTheme(arg1: any, arg2?: any, arg3?: string): any {
5476
const themeClassName = generateIdentifier(
5577
typeof arg2 === 'object' ? arg3 : arg2,
@@ -64,3 +86,18 @@ export function createTheme(arg1: any, arg2?: any, arg3?: string): any {
6486

6587
return vars ? [themeClassName, vars] : themeClassName;
6688
}
89+
90+
function extractLayerFromTokens(
91+
tokens: WithOptionalLayer<MapLeafNodes<any, string>>,
92+
): {
93+
layerName?: string;
94+
tokens: MapLeafNodes<any, string>;
95+
} {
96+
if ('@layer' in tokens) {
97+
const { '@layer': layerName, ...rest } = tokens;
98+
99+
return { layerName, tokens: rest };
100+
}
101+
102+
return { tokens };
103+
}

packages/css/src/types.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@ import type { AtRule, Properties } from 'csstype';
33

44
import type { SimplePseudos } from './simplePseudos';
55

6+
export type Resolve<T> = {
7+
[Key in keyof T]: T[Key];
8+
} & {};
9+
610
// csstype is yet to ship container property types as they are not in
711
// the output MDN spec files yet. Remove this once that's done.
812
// https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Container_Queries

0 commit comments

Comments
 (0)