Skip to content

Commit

Permalink
createTheme, createGlobalTheme: Add support for assigning themes …
Browse files Browse the repository at this point in the history
…to a layer (#1512)
  • Loading branch information
askoufis authored Dec 20, 2024
1 parent dde9bf2 commit 4abfc0b
Show file tree
Hide file tree
Showing 16 changed files with 515 additions and 55 deletions.
51 changes: 51 additions & 0 deletions .changeset/nasty-wasps-tie.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
---
'@vanilla-extract/css': minor
---

`createTheme`, `createGlobalTheme`: Add support for assigning themes to a layer

Themes can now be assigned to a layer by name using the `@layer` key at the top-level of the theme definition.

**EXAMPLE USAGE**:

```ts
// themes.css.ts
import { createTheme, createGlobalTheme, layer } from '@vanilla-extract/css';

export const themeLayer = layer();

export const [themeA, vars] = createTheme({
'@layer': themeLayer,
color: {
brand: 'blue'
},
font: {
body: 'arial'
}
});

export const vars = createGlobalTheme(':root', {
'@layer': themeLayer,
space: {
small: '10px',
large: '20px',
}
});
```

This will generate the following CSS:

```css
@layer themes_themeLayer__1k6oxph0;
@layer themes_themeLayer__1k6oxph0 {
.themes_themeA__1k6oxph1 {
--color-brand__1k6oxph2: blue;
--font-body__1k6oxph3: arial;
}

:root {
--space-small__z05zdf1: 10px;
--space-large__z05zdf2: 20px;
}
}
```
31 changes: 31 additions & 0 deletions fixtures/themed/src/themes.css.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
createTheme,
assignVars,
style,
layer,
} from '@vanilla-extract/css';

export const theme = style({});
Expand Down Expand Up @@ -31,6 +32,36 @@ export const altTheme = createTheme(vars, {
},
});

const themeLayer = layer();

// Not tested visually, exported for CSS output testing
export const [altTheme2Class, altTheme2Contract] = createTheme({
'@layer': themeLayer,
colors: {
backgroundColor: 'green',
text: 'white',
},
space: {
1: '8px',
2: '12px',
3: '16px',
},
});

// Not tested visually, exported for CSS output testing
export const altTheme3 = createGlobalTheme(':root', altTheme2Contract, {
'@layer': 'globalThemeLayer',
colors: {
backgroundColor: 'green',
text: 'white',
},
space: {
1: '8px',
2: '12px',
3: '16px',
},
});

export const responsiveTheme = style({
vars: assignVars(vars, {
colors: {
Expand Down
67 changes: 52 additions & 15 deletions packages/css/src/theme.ts
Original file line number Diff line number Diff line change
@@ -1,55 +1,77 @@
import type { Contract, MapLeafNodes } from '@vanilla-extract/private';
import type { ThemeVars, Tokens } from './types';
import type { GlobalStyleRule, Resolve, ThemeVars, Tokens } from './types';
import { appendCss, registerClassName } from './adapter';
import { getFileScope } from './fileScope';
import { generateIdentifier } from './identifier';
import { createThemeContract, assignVars } from './vars';

type WithOptionalLayer<T extends Tokens> = T & {
'@layer'?: string;
};

type WithoutLayer<T> = Omit<T, '@layer'>;

export function createGlobalTheme<ThemeTokens extends Tokens>(
selector: string,
tokens: ThemeTokens,
): ThemeVars<ThemeTokens>;
tokens: WithOptionalLayer<ThemeTokens>,
): Resolve<WithoutLayer<ThemeVars<ThemeTokens>>>;
export function createGlobalTheme<ThemeContract extends Contract>(
selector: string,
themeContract: ThemeContract,
tokens: MapLeafNodes<ThemeContract, string>,
tokens: WithOptionalLayer<MapLeafNodes<ThemeContract, string>>,
): void;
export function createGlobalTheme(
selector: string,
arg2: any,
arg3?: any,
): any {
const shouldCreateVars = Boolean(!arg3);
const themeContractProvided = Boolean(arg3);

const tokenArg = (
themeContractProvided ? arg3 : arg2
) as WithOptionalLayer<Tokens>;

const themeVars = shouldCreateVars
? createThemeContract(arg2)
: (arg2 as ThemeVars<any>);
const { layerName, tokens } = extractLayerFromTokens(tokenArg);

const tokens = shouldCreateVars ? arg2 : arg3;
const themeContract = themeContractProvided
? (arg2 as ThemeVars<any>)
: createThemeContract(tokens);

let rule: GlobalStyleRule = {
vars: assignVars(themeContract, tokens),
};

if (layerName) {
rule = {
'@layer': {
[layerName]: rule,
},
};
}

appendCss(
{
type: 'global',
selector: selector,
rule: { vars: assignVars(themeVars, tokens) },
rule,
},
getFileScope(),
);

if (shouldCreateVars) {
return themeVars;
if (!themeContractProvided) {
return themeContract;
}
}

export function createTheme<ThemeContract extends Contract>(
themeContract: ThemeContract,
tokens: MapLeafNodes<ThemeContract, string>,
tokens: WithOptionalLayer<MapLeafNodes<ThemeContract, string>>,
debugId?: string,
): string;
export function createTheme<ThemeTokens extends Tokens>(
tokens: ThemeTokens,
tokens: WithOptionalLayer<ThemeTokens>,
debugId?: string,
): [className: string, vars: ThemeVars<ThemeTokens>];
): [className: string, vars: Resolve<WithoutLayer<ThemeVars<ThemeTokens>>>];
export function createTheme(arg1: any, arg2?: any, arg3?: string): any {
const themeClassName = generateIdentifier(
typeof arg2 === 'object' ? arg3 : arg2,
Expand All @@ -64,3 +86,18 @@ export function createTheme(arg1: any, arg2?: any, arg3?: string): any {

return vars ? [themeClassName, vars] : themeClassName;
}

function extractLayerFromTokens(
tokens: WithOptionalLayer<MapLeafNodes<any, string>>,
): {
layerName?: string;
tokens: MapLeafNodes<any, string>;
} {
if ('@layer' in tokens) {
const { '@layer': layerName, ...rest } = tokens;

return { layerName, tokens: rest };
}

return { tokens };
}
4 changes: 4 additions & 0 deletions packages/css/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ import type { AtRule, Properties } from 'csstype';

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

export type Resolve<T> = {
[Key in keyof T]: T[Key];
} & {};

// csstype is yet to ship container property types as they are not in
// the output MDN spec files yet. Remove this once that's done.
// https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Container_Queries
Expand Down
Loading

0 comments on commit 4abfc0b

Please sign in to comment.