Skip to content

Commit c6cd1f2

Browse files
Add recipes packages (#348)
Co-authored-by: Mark Dalgleish <[email protected]>
1 parent d101b43 commit c6cd1f2

File tree

34 files changed

+962
-38
lines changed

34 files changed

+962
-38
lines changed

.changeset/hot-stingrays-mate.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@vanilla-extract/babel-plugin': minor
3+
---
4+
5+
Add debug IDs to `recipe` function

.changeset/nervous-ducks-dress.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---
2+
'@vanilla-extract/css': minor
3+
'@vanilla-extract/integration': minor
4+
---
5+
6+
Add `addFunctionSerializer` function
7+
8+
This also marks `addRecipe` as deprecated.

README.md

Lines changed: 111 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,8 @@ Want to work at a higher level while maximising style re-use? Check out 🍨 [S
102102
- [globalFontFace](#globalfontface)
103103
- [keyframes](#keyframes)
104104
- [globalKeyframes](#globalkeyframes)
105+
- [Recipes API](#recipes-api)
106+
- [recipe](#recipe)
105107
- [Dynamic API](#dynamic-api)
106108
- [assignInlineVars](#assigninlinevars)
107109
- [setElementVars](#setelementvars)
@@ -936,9 +938,117 @@ export const animated = style({
936938
});
937939
```
938940

941+
## Recipes API
942+
943+
Create multi-variant styles with a type-safe runtime API, heavily inspired by [Stitches.](https://stitches.dev)
944+
945+
As with the rest of vanilla-extract, all styles are generated at build time.
946+
947+
```bash
948+
$ npm install @vanilla-extract/recipes
949+
```
950+
951+
### recipe
952+
953+
Creates a multi-variant style function that can be used at runtime or statically in `.css.ts` files.
954+
955+
Accepts an optional set of `base` styles, `variants`, `compoundVariants` and `defaultVariants`.
956+
957+
```ts
958+
import { recipe } from '@vanilla-extract/recipes';
959+
960+
export const button = recipe({
961+
base: {
962+
borderRadius: 6
963+
},
964+
965+
variants: {
966+
color: {
967+
neutral: { background: 'whitesmoke' },
968+
brand: { background: 'blueviolet' },
969+
accent: { background: 'slateblue' }
970+
},
971+
size: {
972+
small: { padding: 12 },
973+
medium: { padding: 16 },
974+
large: { padding: 24 }
975+
},
976+
rounded: {
977+
true: { borderRadius: 999 }
978+
}
979+
},
980+
981+
// Applied when multiple variants are set at once
982+
compoundVariants: [
983+
{
984+
variants: {
985+
color: 'neutral',
986+
size: 'large'
987+
},
988+
style: {
989+
background: 'ghostwhite'
990+
}
991+
}
992+
],
993+
994+
defaultVariants: {
995+
color: 'accent',
996+
size: 'medium'
997+
}
998+
});
999+
```
1000+
1001+
With this recipe configured, you can now use it in your templates.
1002+
1003+
```ts
1004+
import { button } from './button.css.ts';
1005+
1006+
document.write(`
1007+
<button class="${button({
1008+
color: 'accent',
1009+
size: 'large',
1010+
rounded: true
1011+
})}">
1012+
Hello world
1013+
</button>
1014+
`);
1015+
```
1016+
1017+
Your recipe configuration can also make use of existing variables, classes and styles.
1018+
1019+
For example, you can use your `atoms` function from [Sprinkles.](https://github.com/seek-oss/vanilla-extract/tree/master/packages/sprinkles)
1020+
1021+
```ts
1022+
import { recipe } from '@vanilla-extract/recipes';
1023+
import { reset } from './reset.css.ts';
1024+
import { atoms } from './sprinkles.css.ts';
1025+
1026+
export const button = recipe({
1027+
base: [reset, atoms({ borderRadius: 'round' })],
1028+
1029+
variants: {
1030+
color: {
1031+
neutral: atoms({ background: 'neutral' }),
1032+
brand: atoms({ background: 'brand' }),
1033+
accent: atoms({ background: 'accent' })
1034+
},
1035+
size: {
1036+
small: atoms({ padding: 'small' }),
1037+
medium: atoms({ padding: 'medium' }),
1038+
large: atoms({ padding: 'large' })
1039+
}
1040+
},
1041+
1042+
defaultVariants: {
1043+
color: 'accent',
1044+
size: 'medium'
1045+
}
1046+
});
1047+
```
1048+
9391049
## Dynamic API
9401050

941-
We also provide a lightweight standalone package to support dynamic runtime theming.
1051+
Dynamically update theme variables at runtime.
9421052

9431053
```bash
9441054
npm install @vanilla-extract/dynamic

fixtures/recipes/index.html

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
6+
<title>Vite App</title>
7+
</head>
8+
<body>
9+
<div id="root"></div>
10+
<script type="module" src="/src/index.ts"></script>
11+
</body>
12+
</html>

fixtures/recipes/package.json

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"name": "@fixtures/recipes",
3+
"version": "0.0.0",
4+
"main": "src/index.ts",
5+
"sideEffects": true,
6+
"author": "SEEK",
7+
"private": true,
8+
"dependencies": {
9+
"@vanilla-extract/css": "1.4.1",
10+
"@vanilla-extract/recipes": "0.1.0"
11+
}
12+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
6+
<title>Snowpack App</title>
7+
</head>
8+
<body>
9+
<div id="root"></div>
10+
<script type="module" src="/dist/index.js"></script>
11+
</body>
12+
</html>

fixtures/recipes/src/index.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { button, stack } from './styles.css';
2+
3+
function render() {
4+
document.body.innerHTML = `
5+
<div class="${stack()}">
6+
<button class="${button()}">
7+
Standard calm button
8+
</button>
9+
<button class="${button({ size: 'small' })}">
10+
Small calm button
11+
</button>
12+
<button class="${button({ tone: 'angry' })}">
13+
Standard angry button
14+
</button>
15+
<button class="${button({
16+
size: 'small',
17+
tone: 'angry',
18+
bold: true,
19+
})}">
20+
Small angry button
21+
</button>
22+
</div>
23+
`;
24+
}
25+
26+
render();

fixtures/recipes/src/styles.css.ts

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
import { style, createThemeContract, createTheme } from '@vanilla-extract/css';
2+
import { recipe } from '@vanilla-extract/recipes';
3+
4+
const vars = createThemeContract({
5+
bg: null,
6+
fg: null,
7+
});
8+
9+
const calm = createTheme(vars, {
10+
bg: 'powderblue',
11+
fg: 'white',
12+
});
13+
14+
const angry = createTheme(vars, {
15+
bg: 'crimson',
16+
fg: 'black',
17+
});
18+
19+
export const reset = style({
20+
border: 0,
21+
});
22+
23+
export const button = recipe(
24+
{
25+
base: [
26+
reset,
27+
{
28+
borderRadius: '6px',
29+
background: vars.bg,
30+
color: vars.fg,
31+
transition: 'all 0.2s ease',
32+
33+
':hover': {
34+
transform: 'translateY(-3px)',
35+
},
36+
},
37+
],
38+
39+
variants: {
40+
size: {
41+
small: {
42+
fontSize: '16px',
43+
lineHeight: '24px',
44+
},
45+
standard: {
46+
fontSize: '24px',
47+
lineHeight: '40px',
48+
},
49+
},
50+
tone: {
51+
calm,
52+
angry: [
53+
angry,
54+
{
55+
':hover': {
56+
boxShadow: '0 10px 6px -6px #777',
57+
},
58+
},
59+
],
60+
},
61+
bold: {
62+
true: {
63+
fontWeight: 'bold',
64+
},
65+
},
66+
},
67+
68+
defaultVariants: {
69+
size: 'standard',
70+
tone: 'calm',
71+
},
72+
73+
compoundVariants: [
74+
{
75+
variants: {
76+
size: 'small',
77+
bold: true,
78+
},
79+
style: {
80+
'@media': {
81+
'only screen and (min-width: 600px)': {
82+
border: '2px green solid',
83+
},
84+
},
85+
},
86+
},
87+
],
88+
},
89+
'button',
90+
);
91+
92+
export const stack = recipe({
93+
base: {
94+
display: 'flex',
95+
flexDirection: 'column',
96+
alignItems: 'flex-start',
97+
},
98+
99+
variants: {
100+
space: {
101+
medium: {
102+
gap: 20,
103+
'@media': {
104+
'only screen and (min-width: 600px)': {
105+
gap: 30,
106+
},
107+
},
108+
},
109+
},
110+
},
111+
112+
defaultVariants: {
113+
space: 'medium',
114+
},
115+
});

jest.config.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,5 @@ module.exports = {
44
'\\.tsx?$': ['babel-jest', { configFile: './babel-jest.config.js' }],
55
},
66
testMatch: ['**/?(*.)+(test).[jt]s?(x)'],
7+
testTimeout: 10000,
78
};

packages/babel-plugin/src/index.test.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -455,6 +455,25 @@ describe('babel plugin', () => {
455455
`);
456456
});
457457

458+
it('should handle recipe assigned to const', () => {
459+
const source = `
460+
import { recipe } from '@vanilla-extract/recipes';
461+
462+
const button = recipe({});
463+
`;
464+
465+
expect(transform(source)).toMatchInlineSnapshot(`
466+
"import * as __vanilla_filescope__ from '@vanilla-extract/css/fileScope';
467+
468+
__vanilla_filescope__.setFileScope(\\"src/dir/mockFilename.css.ts\\", \\"@vanilla-extract/babel-plugin\\");
469+
470+
import { recipe } from '@vanilla-extract/recipes';
471+
const button = recipe({}, \\"button\\");
472+
473+
__vanilla_filescope__.endFileScope();"
474+
`);
475+
});
476+
458477
it('should ignore functions that already supply a debug name', () => {
459478
const source = `
460479
import { style, styleVariants } from '@vanilla-extract/css';

0 commit comments

Comments
 (0)