Skip to content

Commit fd673f6

Browse files
z4o4zmarkdalgleishaskoufis
authored
add support for css @property (#1092)
Co-authored-by: markdalgleish <[email protected]> Co-authored-by: Adam Skoufis <[email protected]>
1 parent 4abfc0b commit fd673f6

25 files changed

+543
-35
lines changed

.changeset/angry-rules-tease.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-debug-ids": minor
3+
---
4+
5+
Add support for `createVar` calls that declare `@property` rules

.changeset/modern-cobras-sort.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
---
2+
"@vanilla-extract/css": minor
3+
---
4+
5+
`keyframes`: Add support for a `vars` property to steps within `keyframes` declarations
6+
7+
Example usage:
8+
9+
```ts
10+
import { createVar, keyframes } from '@vanilla-extract/css';
11+
12+
const angle = createVar({
13+
syntax: '<angle>',
14+
inherits: false,
15+
initialValue: '0deg'
16+
});
17+
18+
export const angleKeyframes = keyframes({
19+
'0%': {
20+
vars: {
21+
[angle]: '0deg'
22+
}
23+
},
24+
'100%': {
25+
vars: {
26+
[angle]: '360deg'
27+
}
28+
}
29+
});
30+
```

.changeset/poor-mirrors-care.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
---
2+
"@vanilla-extract/css": minor
3+
---
4+
5+
`createVar`: Add support for defining [`@property`] rules
6+
7+
Example usage:
8+
9+
```ts
10+
import { createVar } from '@vanilla-extract/css';
11+
12+
export const myVar = createVar({
13+
syntax: '<number>',
14+
inherits: false,
15+
initialValue: '0.5',
16+
});
17+
```
18+
19+
This will generate the following CSS:
20+
21+
```css
22+
@property --myVar__jteyb14 {
23+
syntax: "<number>";
24+
inherits: false;
25+
initial-value: 0.5;
26+
}
27+
```
28+
29+
[`@property`]: https://developer.mozilla.org/en-US/docs/Web/CSS/@property
Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,34 @@
1-
import { style } from '@vanilla-extract/css';
1+
import { style, createVar, keyframes, fallbackVar } from '@vanilla-extract/css';
2+
3+
const color = createVar();
4+
const angle = createVar({
5+
syntax: '<angle>',
6+
inherits: false,
7+
initialValue: '0deg',
8+
});
9+
10+
const angleKeyframes = keyframes({
11+
'100%': {
12+
vars: {
13+
[angle]: '360deg',
14+
},
15+
},
16+
});
217

318
export const root = style({
419
background: 'pink',
520
color: 'blue',
621
padding: '16px',
7-
transition: 'opacity .1s ease', // Testing autoprefixer
22+
transition: `opacity .1s ease, color .1s ease`, // Testing autoprefixer
23+
backgroundImage: `linear-gradient(${angle}, rgba(153, 70, 198, 0.35) 0%, rgba(28, 56, 240, 0.46) 100%)`,
24+
animation: `${angleKeyframes} 7s infinite ease-in-out both`,
825
':hover': {
926
opacity: 0.8,
27+
color: color,
28+
},
29+
30+
vars: {
31+
[color]: '#fef',
32+
[angle]: fallbackVar(angle, '138deg'),
1033
},
1134
});

fixtures/themed/src/styles.css.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,12 +64,16 @@ globalStyle(`body ${iDunno}:after`, {
6464
content: "'I am content'",
6565
});
6666

67-
const blankVar1 = createVar();
67+
const blankVar1 = createVar({
68+
syntax: '<number>',
69+
inherits: false,
70+
initialValue: '0.5',
71+
});
6872
const blankVar2 = createVar();
6973

7074
export const opacity = styleVariants(
7175
{
72-
'1/2': fallbackVar(blankVar1, '0.5'),
76+
'1/2': blankVar1,
7377
'1/4': fallbackVar(blankVar1, blankVar2, '0.25'),
7478
},
7579
(value) => ({

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

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,22 @@ describe('babel plugin', () => {
217217
`);
218218
});
219219

220+
it('should handle typed createVar assigned to const', () => {
221+
const source = `
222+
import { createVar } from '@vanilla-extract/css';
223+
224+
const myVar = createVar({ syntax: '*', inherits: true });
225+
`;
226+
227+
expect(transform(source)).toMatchInlineSnapshot(`
228+
import { createVar } from '@vanilla-extract/css';
229+
const myVar = createVar({
230+
syntax: '*',
231+
inherits: true
232+
}, "myVar");
233+
`);
234+
});
235+
220236
it('should handle createContainer assigned to const', () => {
221237
const source = `
222238
import { createContainer } from '@vanilla-extract/css';

packages/babel-plugin-debug-ids/src/index.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,11 @@ const debuggableFunctionConfig = {
3131
maxParams: 2,
3232
},
3333
createVar: {
34-
maxParams: 1,
34+
maxParams: 2,
35+
hasDebugId: ({ arguments: args }) => {
36+
const previousArg = args[args.length - 1];
37+
return t.isStringLiteral(previousArg) || t.isTemplateLiteral(previousArg);
38+
},
3539
},
3640
recipe: {
3741
maxParams: 2,

packages/css/src/transformCss.test.ts

Lines changed: 106 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,14 @@ import { style } from './style';
66
setFileScope('test');
77

88
const testVar = createVar();
9+
const testPropertyVar = createVar(
10+
{
11+
syntax: '<angle>',
12+
inherits: false,
13+
initialValue: '0deg',
14+
},
15+
'test-property',
16+
);
917
const style1 = style({});
1018
const style2 = style({});
1119

@@ -1845,6 +1853,46 @@ describe('transformCss', () => {
18451853
`);
18461854
});
18471855

1856+
it('should handle animations with vars', () => {
1857+
expect(
1858+
transformCss({
1859+
composedClassLists: [],
1860+
localClassNames: ['testClass'],
1861+
cssObjs: [
1862+
{
1863+
type: 'keyframes',
1864+
name: 'myAnimation',
1865+
rule: {
1866+
from: {
1867+
vars: {
1868+
'--my-var': 'red',
1869+
[testVar]: 'green',
1870+
},
1871+
},
1872+
to: {
1873+
vars: {
1874+
'--my-var': 'orange',
1875+
[testVar]: 'blue',
1876+
},
1877+
},
1878+
},
1879+
},
1880+
],
1881+
}).join('\n'),
1882+
).toMatchInlineSnapshot(`
1883+
@keyframes myAnimation {
1884+
from {
1885+
--my-var: red;
1886+
--skkcyc0: green;
1887+
}
1888+
to {
1889+
--my-var: orange;
1890+
--skkcyc0: blue;
1891+
}
1892+
}
1893+
`);
1894+
});
1895+
18481896
it('should handle font face', () => {
18491897
expect(
18501898
transformCss({
@@ -2022,7 +2070,7 @@ describe('transformCss', () => {
20222070
`);
20232071
});
20242072

2025-
it('should handle css vars', () => {
2073+
it('should handle simple css properties', () => {
20262074
expect(
20272075
transformCss({
20282076
composedClassLists: [],
@@ -2076,6 +2124,60 @@ describe('transformCss', () => {
20762124
`);
20772125
});
20782126

2127+
it('should handle complicated css properties', () => {
2128+
expect(
2129+
transformCss({
2130+
composedClassLists: [],
2131+
localClassNames: ['testClass'],
2132+
cssObjs: [
2133+
{
2134+
type: 'local',
2135+
selector: 'testClass',
2136+
rule: {
2137+
display: 'block',
2138+
vars: {
2139+
'--my-var': 'red',
2140+
[testPropertyVar]: '10deg',
2141+
},
2142+
selectors: {
2143+
'&:nth-child(3)': {
2144+
vars: {
2145+
'--my-var': 'orange',
2146+
[testPropertyVar]: '20deg',
2147+
},
2148+
},
2149+
},
2150+
'@media': {
2151+
'screen and (min-width: 700px)': {
2152+
vars: {
2153+
'--my-var': 'yellow',
2154+
[testPropertyVar]: '50deg',
2155+
},
2156+
},
2157+
},
2158+
},
2159+
},
2160+
],
2161+
}).join('\n'),
2162+
).toMatchInlineSnapshot(`
2163+
.testClass {
2164+
--my-var: red;
2165+
--test-property__skkcyc1: 10deg;
2166+
display: block;
2167+
}
2168+
.testClass:nth-child(3) {
2169+
--my-var: orange;
2170+
--test-property__skkcyc1: 20deg;
2171+
}
2172+
@media screen and (min-width: 700px) {
2173+
.testClass {
2174+
--my-var: yellow;
2175+
--test-property__skkcyc1: 50deg;
2176+
}
2177+
}
2178+
`);
2179+
});
2180+
20792181
it('should cast property values to pixels when relevant', () => {
20802182
expect(
20812183
transformCss({
@@ -2288,13 +2390,13 @@ describe('transformCss', () => {
22882390
],
22892391
}).join('\n'),
22902392
).toMatchInlineSnapshot(`
2291-
.skkcyc2 .skkcyc1:before, .skkcyc2 .skkcyc1:after {
2393+
.skkcyc3 .skkcyc2:before, .skkcyc3 .skkcyc2:after {
22922394
background: black;
22932395
}
2294-
._1g1ptzo1._1g1ptzo10 .skkcyc1 {
2396+
._1g1ptzo1._1g1ptzo10 .skkcyc2 {
22952397
background: blue;
22962398
}
2297-
._1g1ptzo10._1g1ptzo1 .skkcyc1 {
2399+
._1g1ptzo10._1g1ptzo1 .skkcyc2 {
22982400
background: blue;
22992401
}
23002402
`);

packages/css/src/transformCss.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import type {
1313
CSSSelectorBlock,
1414
Composition,
1515
WithQueries,
16+
CSSPropertyBlock,
1617
} from './types';
1718
import { markCompositionUsed } from './adapter';
1819
import { forEach, omit, mapKeys } from './utils';
@@ -119,6 +120,7 @@ class Stylesheet {
119120
localClassNamesSearch: AhoCorasick;
120121
composedClassLists: Array<{ identifier: string; regex: RegExp }>;
121122
layers: Map<string, Array<string>>;
123+
propertyRules: Array<CSSPropertyBlock>;
122124

123125
constructor(
124126
localClassNames: Array<string>,
@@ -128,6 +130,7 @@ class Stylesheet {
128130
this.conditionalRulesets = [new ConditionalRuleset()];
129131
this.fontFaceRules = [];
130132
this.keyframesRules = [];
133+
this.propertyRules = [];
131134
this.localClassNamesMap = new Map(
132135
localClassNames.map((localClassName) => [localClassName, localClassName]),
133136
);
@@ -150,10 +153,17 @@ class Stylesheet {
150153

151154
return;
152155
}
156+
157+
if (root.type === 'property') {
158+
this.propertyRules.push(root);
159+
160+
return;
161+
}
162+
153163
if (root.type === 'keyframes') {
154164
root.rule = Object.fromEntries(
155165
Object.entries(root.rule).map(([keyframe, rule]) => {
156-
return [keyframe, this.transformProperties(rule)];
166+
return [keyframe, this.transformVars(this.transformProperties(rule))];
157167
}),
158168
);
159169
this.keyframesRules.push(root);
@@ -582,6 +592,11 @@ class Stylesheet {
582592
css.push(renderCss({ '@font-face': fontFaceRule }));
583593
}
584594

595+
// Render property rules
596+
for (const property of this.propertyRules) {
597+
css.push(renderCss({ [`@property ${property.name}`]: property.rule }));
598+
}
599+
585600
// Render keyframes
586601
for (const keyframe of this.keyframesRules) {
587602
css.push(renderCss({ [`@keyframes ${keyframe.name}`]: keyframe.rule }));

0 commit comments

Comments
 (0)