Skip to content

Commit

Permalink
add support for css @property (#1092)
Browse files Browse the repository at this point in the history
Co-authored-by: markdalgleish <[email protected]>
Co-authored-by: Adam Skoufis <[email protected]>
  • Loading branch information
3 people authored Dec 20, 2024
1 parent 4abfc0b commit fd673f6
Show file tree
Hide file tree
Showing 25 changed files with 543 additions and 35 deletions.
5 changes: 5 additions & 0 deletions .changeset/angry-rules-tease.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@vanilla-extract/babel-plugin-debug-ids": minor
---

Add support for `createVar` calls that declare `@property` rules
30 changes: 30 additions & 0 deletions .changeset/modern-cobras-sort.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
---
"@vanilla-extract/css": minor
---

`keyframes`: Add support for a `vars` property to steps within `keyframes` declarations

Example usage:

```ts
import { createVar, keyframes } from '@vanilla-extract/css';

const angle = createVar({
syntax: '<angle>',
inherits: false,
initialValue: '0deg'
});

export const angleKeyframes = keyframes({
'0%': {
vars: {
[angle]: '0deg'
}
},
'100%': {
vars: {
[angle]: '360deg'
}
}
});
```
29 changes: 29 additions & 0 deletions .changeset/poor-mirrors-care.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
---
"@vanilla-extract/css": minor
---

`createVar`: Add support for defining [`@property`] rules

Example usage:

```ts
import { createVar } from '@vanilla-extract/css';

export const myVar = createVar({
syntax: '<number>',
inherits: false,
initialValue: '0.5',
});
```

This will generate the following CSS:

```css
@property --myVar__jteyb14 {
syntax: "<number>";
inherits: false;
initial-value: 0.5;
}
```

[`@property`]: https://developer.mozilla.org/en-US/docs/Web/CSS/@property
27 changes: 25 additions & 2 deletions examples/next/components/HelloWorld.css.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,34 @@
import { style } from '@vanilla-extract/css';
import { style, createVar, keyframes, fallbackVar } from '@vanilla-extract/css';

const color = createVar();
const angle = createVar({
syntax: '<angle>',
inherits: false,
initialValue: '0deg',
});

const angleKeyframes = keyframes({
'100%': {
vars: {
[angle]: '360deg',
},
},
});

export const root = style({
background: 'pink',
color: 'blue',
padding: '16px',
transition: 'opacity .1s ease', // Testing autoprefixer
transition: `opacity .1s ease, color .1s ease`, // Testing autoprefixer
backgroundImage: `linear-gradient(${angle}, rgba(153, 70, 198, 0.35) 0%, rgba(28, 56, 240, 0.46) 100%)`,
animation: `${angleKeyframes} 7s infinite ease-in-out both`,
':hover': {
opacity: 0.8,
color: color,
},

vars: {
[color]: '#fef',
[angle]: fallbackVar(angle, '138deg'),
},
});
8 changes: 6 additions & 2 deletions fixtures/themed/src/styles.css.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,12 +64,16 @@ globalStyle(`body ${iDunno}:after`, {
content: "'I am content'",
});

const blankVar1 = createVar();
const blankVar1 = createVar({
syntax: '<number>',
inherits: false,
initialValue: '0.5',
});
const blankVar2 = createVar();

export const opacity = styleVariants(
{
'1/2': fallbackVar(blankVar1, '0.5'),
'1/2': blankVar1,
'1/4': fallbackVar(blankVar1, blankVar2, '0.25'),
},
(value) => ({
Expand Down
16 changes: 16 additions & 0 deletions packages/babel-plugin-debug-ids/src/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,22 @@ describe('babel plugin', () => {
`);
});

it('should handle typed createVar assigned to const', () => {
const source = `
import { createVar } from '@vanilla-extract/css';
const myVar = createVar({ syntax: '*', inherits: true });
`;

expect(transform(source)).toMatchInlineSnapshot(`
import { createVar } from '@vanilla-extract/css';
const myVar = createVar({
syntax: '*',
inherits: true
}, "myVar");
`);
});

it('should handle createContainer assigned to const', () => {
const source = `
import { createContainer } from '@vanilla-extract/css';
Expand Down
6 changes: 5 additions & 1 deletion packages/babel-plugin-debug-ids/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,11 @@ const debuggableFunctionConfig = {
maxParams: 2,
},
createVar: {
maxParams: 1,
maxParams: 2,
hasDebugId: ({ arguments: args }) => {
const previousArg = args[args.length - 1];
return t.isStringLiteral(previousArg) || t.isTemplateLiteral(previousArg);
},
},
recipe: {
maxParams: 2,
Expand Down
110 changes: 106 additions & 4 deletions packages/css/src/transformCss.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,14 @@ import { style } from './style';
setFileScope('test');

const testVar = createVar();
const testPropertyVar = createVar(
{
syntax: '<angle>',
inherits: false,
initialValue: '0deg',
},
'test-property',
);
const style1 = style({});
const style2 = style({});

Expand Down Expand Up @@ -1845,6 +1853,46 @@ describe('transformCss', () => {
`);
});

it('should handle animations with vars', () => {
expect(
transformCss({
composedClassLists: [],
localClassNames: ['testClass'],
cssObjs: [
{
type: 'keyframes',
name: 'myAnimation',
rule: {
from: {
vars: {
'--my-var': 'red',
[testVar]: 'green',
},
},
to: {
vars: {
'--my-var': 'orange',
[testVar]: 'blue',
},
},
},
},
],
}).join('\n'),
).toMatchInlineSnapshot(`
@keyframes myAnimation {
from {
--my-var: red;
--skkcyc0: green;
}
to {
--my-var: orange;
--skkcyc0: blue;
}
}
`);
});

it('should handle font face', () => {
expect(
transformCss({
Expand Down Expand Up @@ -2022,7 +2070,7 @@ describe('transformCss', () => {
`);
});

it('should handle css vars', () => {
it('should handle simple css properties', () => {
expect(
transformCss({
composedClassLists: [],
Expand Down Expand Up @@ -2076,6 +2124,60 @@ describe('transformCss', () => {
`);
});

it('should handle complicated css properties', () => {
expect(
transformCss({
composedClassLists: [],
localClassNames: ['testClass'],
cssObjs: [
{
type: 'local',
selector: 'testClass',
rule: {
display: 'block',
vars: {
'--my-var': 'red',
[testPropertyVar]: '10deg',
},
selectors: {
'&:nth-child(3)': {
vars: {
'--my-var': 'orange',
[testPropertyVar]: '20deg',
},
},
},
'@media': {
'screen and (min-width: 700px)': {
vars: {
'--my-var': 'yellow',
[testPropertyVar]: '50deg',
},
},
},
},
},
],
}).join('\n'),
).toMatchInlineSnapshot(`
.testClass {
--my-var: red;
--test-property__skkcyc1: 10deg;
display: block;
}
.testClass:nth-child(3) {
--my-var: orange;
--test-property__skkcyc1: 20deg;
}
@media screen and (min-width: 700px) {
.testClass {
--my-var: yellow;
--test-property__skkcyc1: 50deg;
}
}
`);
});

it('should cast property values to pixels when relevant', () => {
expect(
transformCss({
Expand Down Expand Up @@ -2288,13 +2390,13 @@ describe('transformCss', () => {
],
}).join('\n'),
).toMatchInlineSnapshot(`
.skkcyc2 .skkcyc1:before, .skkcyc2 .skkcyc1:after {
.skkcyc3 .skkcyc2:before, .skkcyc3 .skkcyc2:after {
background: black;
}
._1g1ptzo1._1g1ptzo10 .skkcyc1 {
._1g1ptzo1._1g1ptzo10 .skkcyc2 {
background: blue;
}
._1g1ptzo10._1g1ptzo1 .skkcyc1 {
._1g1ptzo10._1g1ptzo1 .skkcyc2 {
background: blue;
}
`);
Expand Down
17 changes: 16 additions & 1 deletion packages/css/src/transformCss.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import type {
CSSSelectorBlock,
Composition,
WithQueries,
CSSPropertyBlock,
} from './types';
import { markCompositionUsed } from './adapter';
import { forEach, omit, mapKeys } from './utils';
Expand Down Expand Up @@ -119,6 +120,7 @@ class Stylesheet {
localClassNamesSearch: AhoCorasick;
composedClassLists: Array<{ identifier: string; regex: RegExp }>;
layers: Map<string, Array<string>>;
propertyRules: Array<CSSPropertyBlock>;

constructor(
localClassNames: Array<string>,
Expand All @@ -128,6 +130,7 @@ class Stylesheet {
this.conditionalRulesets = [new ConditionalRuleset()];
this.fontFaceRules = [];
this.keyframesRules = [];
this.propertyRules = [];
this.localClassNamesMap = new Map(
localClassNames.map((localClassName) => [localClassName, localClassName]),
);
Expand All @@ -150,10 +153,17 @@ class Stylesheet {

return;
}

if (root.type === 'property') {
this.propertyRules.push(root);

return;
}

if (root.type === 'keyframes') {
root.rule = Object.fromEntries(
Object.entries(root.rule).map(([keyframe, rule]) => {
return [keyframe, this.transformProperties(rule)];
return [keyframe, this.transformVars(this.transformProperties(rule))];
}),
);
this.keyframesRules.push(root);
Expand Down Expand Up @@ -582,6 +592,11 @@ class Stylesheet {
css.push(renderCss({ '@font-face': fontFaceRule }));
}

// Render property rules
for (const property of this.propertyRules) {
css.push(renderCss({ [`@property ${property.name}`]: property.rule }));
}

// Render keyframes
for (const keyframe of this.keyframesRules) {
css.push(renderCss({ [`@keyframes ${keyframe.name}`]: keyframe.rule }));
Expand Down
Loading

0 comments on commit fd673f6

Please sign in to comment.