Skip to content

Commit

Permalink
Add @layer support (#147)
Browse files Browse the repository at this point in the history
Co-authored-by: Rogin Farrer <[email protected]>
  • Loading branch information
aspirisen and roginfarrer authored Jul 10, 2023
1 parent e1ed0f1 commit 3731e6a
Show file tree
Hide file tree
Showing 12 changed files with 255 additions and 20 deletions.
19 changes: 19 additions & 0 deletions .changeset/selfish-yaks-destroy.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
---
'rainbow-sprinkles': minor
---

Support assigning properties to layers via `@layer` option on `defineProperties`

**Example usage:**

```ts
// sprinkles.css.ts
import { defineProperties } from 'rainbow-sprinkles';
import { layer } from '@vanilla-extract/css';

export const sprinklesLayer = layer();

const properties = defineProperties({
'@layer': sprinklesLayer,
// etc.
});
3 changes: 3 additions & 0 deletions examples/react/components/rainbow-sprinkles.css.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { createRainbowSprinkles, defineProperties } from 'rainbow-sprinkles';
import { vars } from '../vars.css';
import { interactiveLayer, responsiveLayer } from '../layers.css';

const positiveSpace = {
'250': vars.space['250'],
Expand All @@ -16,6 +17,7 @@ const positiveSpace = {
} as const;

const responsiveProperties = defineProperties({
'@layer': responsiveLayer,
conditions: {
mobile: {},
tablet: { '@media': 'screen and (min-width: 768px)' },
Expand Down Expand Up @@ -87,6 +89,7 @@ const responsiveProperties = defineProperties({
});

const interactiveProperties = defineProperties({
'@layer': interactiveLayer,
conditions: {
base: {},
hover: { selector: '&:hover' },
Expand Down
5 changes: 5 additions & 0 deletions examples/react/layers.css.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { layer } from '@vanilla-extract/css';

export const resetLayer = layer('reset');
export const responsiveLayer = layer('responsive');
export const interactiveLayer = layer('interactive');
11 changes: 8 additions & 3 deletions examples/react/styles/index.css.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
import { globalStyle, globalKeyframes } from '@vanilla-extract/css';
import { globalStyle, globalKeyframes, layer } from '@vanilla-extract/css';
import { resetLayer } from '../layers.css';

globalStyle('body, h1, h2, h3, h4, h5, h6, p, div', {
all: 'unset',
boxSizing: 'border-box',
'@layer': {
[resetLayer]: {
all: 'unset',
boxSizing: 'border-box',
},
},
});

globalKeyframes('pinwheelSpin', {
Expand Down
16 changes: 16 additions & 0 deletions packages/rainbow-sprinkles/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,22 @@ function App() {
}
```

## CSS Layers

You can define a [css layer](https://developer.mozilla.org/en-US/docs/Web/CSS/@layer) for a given set of properties.

```typescript
// rainbow-sprinkles.css.ts
import { layer } from '@vanilla-extract/css';
import { defineProperties } from 'rainbow-sprinkles';

export const sprinklesLayer = layer();

const properties = defineProperties({
'@layer': sprinklesLayer
// etc.
});

### `dynamicProperties` vs `staticProperties`

One trade off that's made for supporting dynamic values is that we have to increase the size of the document. Instead of just appending a single class to an element to add a style, both a utility class and an inline style assignment is added to an element. While this setup will still produce an overall smaller bundle in many cases, some large applications may observe frequent recurrence of specific combinations of CSS properties and values. In these cases, those combinations can be set-up in `staticProperties` in the initial configuration. `staticProperties` will produce typical CSS utility classes. The runtime portion of Rainbow Sprinkles will defer to the CSS classes created by `staticProperties` and not apply any inline style assignments.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,3 +116,86 @@ it('returns expected configuration given array scale', () => {

style.mockClear();
});

it('returns expected configuration for css layers', () => {
const style = jest.spyOn(VE, 'style');
const result = createStaticStyles(
'display',
['block', 'inline-block'],
conditions,
'mobile',
{ '@layer': 'primary' },
);

const calledArgs = style.mock.calls;

expect(calledArgs.length).toBe(6);

expect(calledArgs[0][0]).toMatchObject({
'@layer': { primary: { display: 'block' } },
});
expect(calledArgs[1][0]).toMatchObject({
'@layer': {
primary: {
'@media': { 'screen and (min-width: 768px)': { display: 'block' } },
},
},
});
expect(calledArgs[2][0]).toMatchObject({
'@layer': {
primary: {
'@media': { 'screen and (min-width: 1024px)': { display: 'block' } },
},
},
});
expect(calledArgs[3][0]).toMatchObject({
'@layer': { primary: { display: 'inline-block' } },
});
expect(calledArgs[4][0]).toMatchObject({
'@layer': {
primary: {
'@media': {
'screen and (min-width: 768px)': {
display: 'inline-block',
},
},
},
},
});
expect(calledArgs[5][0]).toMatchObject({
'@layer': {
primary: {
'@media': {
'screen and (min-width: 1024px)': {
display: 'inline-block',
},
},
},
},
});

expect(result).toMatchObject({
values: {
block: {
conditions: {
mobile: 'display-block-mobile',
tablet: 'display-block-tablet',
desktop: 'display-block-desktop',
},
default: 'display-block-mobile',
},
'inline-block': {
conditions: {
mobile: 'display-inline-block-mobile',
tablet: 'display-inline-block-tablet',
desktop: 'display-inline-block-desktop',
},
default: 'display-inline-block-mobile',
},
},
name: 'display',
staticScale: ['block', 'inline-block'],
});

style.mockClear();
});
66 changes: 66 additions & 0 deletions packages/rainbow-sprinkles/src/__tests__/createStyles.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,3 +94,69 @@ it('returns expected configuration with scale: true', () => {

style.mockClear();
});

it('returns expected configuration for css layers', () => {
const style = jest.spyOn(VE, 'style');
const result = createStyles('backgroundColor', scale, conditions, 'mobile', {
'@layer': 'primary',
});

const calledArgs = style.mock.calls;
expect(calledArgs.length).toBe(3);

expect(calledArgs[0][0]).toMatchObject({
'@layer': {
primary: {
backgroundColor: '--backgroundColor-mobile',
},
},
});
expect(calledArgs[1][0]).toMatchObject({
'@layer': {
primary: {
'@media': {
'screen and (min-width: 768px)': {
backgroundColor: '--backgroundColor-tablet',
},
},
},
},
});
expect(calledArgs[2][0]).toMatchObject({
'@layer': {
primary: {
'@media': {
'screen and (min-width: 1024px)': {
backgroundColor: '--backgroundColor-desktop',
},
},
},
},
});

expect(result).toMatchObject({
dynamic: {
default: 'backgroundColor-mobile',
conditions: {
mobile: 'backgroundColor-mobile',
tablet: 'backgroundColor-tablet',
desktop: 'backgroundColor-desktop',
},
},
name: 'backgroundColor',
vars: {
conditions: {
mobile: '--backgroundColor-mobile',
tablet: '--backgroundColor-tablet',
desktop: '--backgroundColor-desktop',
},
default: '--backgroundColor-mobile',
},
dynamicScale: {
primary: 'primary-color',
secondary: 'secondary-color',
},
});

style.mockClear();
});
19 changes: 16 additions & 3 deletions packages/rainbow-sprinkles/src/createStaticStyles.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { style } from '@vanilla-extract/css';
import type { CreateStylesOutput } from './types';
import type { CommonOptions, CreateStylesOutput } from './types';
import { mapValues } from './utils';

export function createStaticStyles(
property: string,
scale: ReadonlyArray<string> | Record<string, string>,
conditions: Record<string, Record<string, string>>,
defaultCondition: string,
options: CommonOptions = {},
): CreateStylesOutput {
const scaleObj = Array.isArray(scale)
? Object.assign(
Expand All @@ -18,9 +19,16 @@ export function createStaticStyles(
: scale;

const values = mapValues(scaleObj, (scaleValue, scaleKey) => {
const styleValue = { [property]: scaleValue };

if (!conditions) {
return {
default: style({ [property]: scaleValue }, `${property}-${scaleKey}`),
default: style(
options['@layer']
? { ['@layer']: { [options['@layer']]: styleValue } }
: styleValue,
`${property}-${scaleKey}`,
),
};
}

Expand Down Expand Up @@ -54,7 +62,12 @@ export function createStaticStyles(
},
};
}
return style(styleValue, `${property}-${scaleKey}-${conditionName}`);
return style(
options['@layer']
? { ['@layer']: { [options['@layer']]: styleValue } }
: styleValue,
`${property}-${scaleKey}-${conditionName}`,
);
});
return {
conditions: classes,
Expand Down
19 changes: 16 additions & 3 deletions packages/rainbow-sprinkles/src/createStyles.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,24 @@
import { createVar, style } from '@vanilla-extract/css';
import type { CreateStylesOutput } from './types';
import type { CommonOptions, CreateStylesOutput } from './types';
import { mapValues } from './utils';

export function createStyles(
property: string,
scale: true | Record<string, string>,
conditions: Record<string, Record<string, string>>,
defaultCondition: string,
options: CommonOptions = {},
): CreateStylesOutput {
if (!conditions) {
const cssVar = createVar(property);
const className = style({ [property]: cssVar }, property);
const styleValue = { [property]: cssVar };

const className = style(
options['@layer']
? { ['@layer']: { [options['@layer']]: styleValue } }
: styleValue,
property,
);

return {
vars: { default: cssVar },
Expand Down Expand Up @@ -56,7 +64,12 @@ export function createStyles(
};
}

return style(styleValue, `${property}-${conditionName}`);
return style(
options['@layer']
? { ['@layer']: { [options['@layer']]: styleValue } }
: styleValue,
`${property}-${conditionName}`,
);
});

return {
Expand Down
1 change: 1 addition & 0 deletions packages/rainbow-sprinkles/src/css.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7161,6 +7161,7 @@ export type AtRules =
| '@import'
| '@keyframes'
| '@media'
| '@layer'
| '@namespace'
| '@page'
| '@property'
Expand Down
Loading

0 comments on commit 3731e6a

Please sign in to comment.