Skip to content

Commit ece6a19

Browse files
committed
[core] Enable support for base and variant styles
through css layers. Behind a feature-flag right now but the aim is to make this default before v1 release.
1 parent 698f036 commit ece6a19

File tree

12 files changed

+199
-24
lines changed

12 files changed

+199
-24
lines changed

packages/pigment-css-react/package.json

+2
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@
5050
"cssesc": "^3.0.0",
5151
"csstype": "^3.1.3",
5252
"lodash": "^4.17.21",
53+
"postcss": "^8.4.38",
54+
"postcss-merge-rules": "^7.0.0",
5355
"stylis": "^4.3.1",
5456
"stylis-plugin-rtl": "^2.1.1"
5557
},

packages/pigment-css-react/src/processors/styled.ts

+15-6
Original file line numberDiff line numberDiff line change
@@ -310,9 +310,9 @@ export class StyledProcessor extends BaseProcessor {
310310
* which we can use to generate our styles.
311311
* Order of processing styles -
312312
* 1. CSS directly declared in styled call
313-
* 3. Variants declared in styled call
314-
* 2. CSS declared in theme object's styledOverrides
315-
* 3. Variants declared in theme object
313+
* 2. Variants declared in styled call
314+
* 3. CSS declared in theme object's styledOverrides
315+
* 4. Variants declared in theme object
316316
*/
317317
build(values: ValueCache): void {
318318
if (this.isTemplateTag) {
@@ -449,6 +449,7 @@ export class StyledProcessor extends BaseProcessor {
449449
variantsAccumulator,
450450
themeImportIdentifier,
451451
);
452+
452453
const className = this.getClassName();
453454
this.baseClasses.push(className);
454455
this.collectedStyles.push([className, finalStyle, styleArg]);
@@ -523,8 +524,8 @@ export class StyledProcessor extends BaseProcessor {
523524
styleArg: ExpressionValue | null,
524525
variantsAccumulator?: VariantData[],
525526
themeImportIdentifier?: string,
526-
) {
527-
const { themeArgs = {} } = this.options as IOptions;
527+
): string {
528+
const { themeArgs = {}, experiments = {} } = this.options as IOptions;
528529
const styleObj = typeof styleObjOrFn === 'function' ? styleObjOrFn(themeArgs) : styleObjOrFn;
529530
if (!styleObj) {
530531
return '';
@@ -551,7 +552,15 @@ export class StyledProcessor extends BaseProcessor {
551552
if (res.length) {
552553
this.collectedVariables.push(...res);
553554
}
554-
return processCssObject(styleObj, themeArgs);
555+
const cssText = processCssObject(styleObj, themeArgs);
556+
557+
if (experiments.styleLayers) {
558+
if (variantsAccumulator) {
559+
return `@layer pigment-base {${cssText}}`;
560+
}
561+
return `@layer pigment-variant {${cssText}}`;
562+
}
563+
return cssText;
555564
}
556565

557566
public override get asSelector(): string {

packages/pigment-css-react/src/utils/cssFnValueToVariable.ts

+7
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,13 @@ export type PluginCustomOptions = {
3838
*/
3939
getDirSelector?: (dir: 'ltr' | 'rtl') => string;
4040
};
41+
experiments?: {
42+
/**
43+
* Wrap generated CSS in layers so that all the base styles come before all the variant styles.
44+
* Experimental right now, but will be default by v1 release.
45+
*/
46+
styleLayers?: boolean;
47+
};
4148
};
4249

4350
type CssFnValueToVariableParams = {

packages/pigment-css-react/src/utils/generateCss.ts

+6-2
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
11
import { serializeStyles } from '@emotion/serialize';
22
import { Theme } from './extendTheme';
3+
import { PluginCustomOptions } from './cssFnValueToVariable';
34

4-
export function generateTokenCss(theme?: Theme) {
5+
export function generateTokenCss(
6+
theme?: Theme,
7+
experiments: PluginCustomOptions['experiments'] = {},
8+
) {
59
if (!theme) {
610
return '';
711
}
812
// use emotion to serialize the object to css string
913
const { styles } = serializeStyles(theme.generateStyleSheets?.() || []);
10-
return styles;
14+
return experiments.styleLayers ? `@layer pigment-base, pigment-variant;\n${styles}` : styles;
1115
}
1216

1317
export function generateThemeTokens(theme?: Theme) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { styled } from '@pigment-css/react';
2+
3+
const SliderRail = styled('span', {
4+
name: 'MuiSlider',
5+
slot: 'Rail',
6+
})({
7+
color: 'red',
8+
variants: [
9+
{
10+
props: { color: 'primary' },
11+
style: {
12+
color: 'tomato',
13+
},
14+
},
15+
{
16+
props: ({ ownerState }) => ownerState.color === 'secondary',
17+
style: {
18+
color: 'salmon',
19+
},
20+
},
21+
],
22+
});
23+
24+
export const SliderOverride = styled(SliderRail)({
25+
color: 'blue',
26+
variants: [
27+
{
28+
props: { color: 'primary' },
29+
style: {
30+
color: 'indigo',
31+
},
32+
},
33+
],
34+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
@layer pigment-base, pigment-variant;
2+
@layer pigment-base {
3+
.srxh6zy {
4+
color: red;
5+
}
6+
}
7+
@layer pigment-variant {
8+
.srxh6zy-1 {
9+
color: tomato;
10+
}
11+
}
12+
@layer pigment-variant {
13+
.srxh6zy-2 {
14+
color: salmon;
15+
}
16+
}
17+
@layer pigment-base {
18+
.srxh6zy-3 {
19+
font-size: 1.5rem;
20+
}
21+
}
22+
@layer pigment-base {
23+
.s1q936we {
24+
color: blue;
25+
}
26+
}
27+
@layer pigment-variant {
28+
.s1q936we-1 {
29+
color: indigo;
30+
}
31+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { styled as _styled2 } from '@pigment-css/react';
2+
import _theme2 from '@pigment-css/react/theme';
3+
import { styled as _styled } from '@pigment-css/react';
4+
import _theme from '@pigment-css/react/theme';
5+
const SliderRail = /*#__PURE__*/ _styled('span', {
6+
name: 'MuiSlider',
7+
slot: 'Rail',
8+
})({
9+
classes: ['srxh6zy', 'srxh6zy-3'],
10+
variants: [
11+
{
12+
props: {
13+
color: 'primary',
14+
},
15+
className: 'srxh6zy-1',
16+
},
17+
{
18+
props: ({ ownerState }) => ownerState.color === 'secondary',
19+
className: 'srxh6zy-2',
20+
},
21+
],
22+
});
23+
const _exp3 = /*#__PURE__*/ () => SliderRail;
24+
export const SliderOverride = /*#__PURE__*/ _styled2(_exp3())({
25+
classes: ['s1q936we'],
26+
variants: [
27+
{
28+
props: {
29+
color: 'primary',
30+
},
31+
className: 's1q936we-1',
32+
},
33+
],
34+
});

packages/pigment-css-react/tests/styled/styled.test.tsx

+17
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,23 @@ describe('Pigment CSS - styled', () => {
8282
expect(output.css).to.equal(fixture.css);
8383
});
8484

85+
it('should work with variants with layers enabled', async () => {
86+
const { output, fixture } = await runTransformation(
87+
path.join(__dirname, 'fixtures/styled-variants-layer.input.js'),
88+
{
89+
themeArgs: {
90+
theme,
91+
},
92+
experiments: {
93+
styleLayers: true,
94+
},
95+
},
96+
);
97+
98+
expect(output.js).to.equal(fixture.js);
99+
expect(output.css).to.equal(fixture.css);
100+
});
101+
85102
it('should work with theme styleOverrides', async () => {
86103
const { output, fixture } = await runTransformation(
87104
path.join(__dirname, 'fixtures/styled-theme-styleOverrides.input.js'),

packages/pigment-css-react/tests/testUtils.ts

+7-9
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import {
88
transform as wywTransform,
99
createFileReporter,
1010
} from '@wyw-in-js/transform';
11-
import { PluginCustomOptions, preprocessor } from '@pigment-css/react/utils';
11+
import { PluginCustomOptions, preprocessor, generateTokenCss } from '@pigment-css/react/utils';
1212
import * as prettier from 'prettier';
1313

1414
import sxTransformPlugin from '../exports/sx-plugin';
@@ -24,10 +24,7 @@ function runSxTransform(code: string, filename: string) {
2424
});
2525
}
2626

27-
export async function runTransformation(
28-
absolutePath: string,
29-
options?: { themeArgs?: { theme?: any }; css?: PluginCustomOptions['css'] },
30-
) {
27+
export async function runTransformation(absolutePath: string, options?: PluginCustomOptions) {
3128
const cache = new TransformCacheCollection();
3229
const { emitter: eventEmitter } = createFileReporter(false);
3330
const inputFilePath = absolutePath;
@@ -43,9 +40,7 @@ export async function runTransformation(
4340
const babelResult = await runSxTransform(inputContent, inputFilePath);
4441

4542
const pluginOptions = {
46-
themeArgs: {
47-
theme: options?.themeArgs?.theme,
48-
},
43+
...options,
4944
babelOptions: {
5045
configFile: false,
5146
babelrc: false,
@@ -80,7 +75,10 @@ export async function runTransformation(
8075
...prettierConfig,
8176
parser: 'babel',
8277
});
83-
const formattedCss = await prettier.format(result.cssText ?? '', {
78+
const baseCss = generateTokenCss(options?.themeArgs?.theme ?? {}, options?.experiments);
79+
const originalCss = baseCss + result.cssText ?? '';
80+
81+
const formattedCss = await prettier.format(originalCss, {
8482
...prettierConfig,
8583
parser: 'css',
8684
});

packages/pigment-css-unplugin/src/index.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -345,7 +345,7 @@ export const plugin = createUnplugin<PigmentOptions, true>((options) => {
345345
},
346346
transform(_code, id) {
347347
if (id.endsWith('styles.css')) {
348-
return theme ? generateTokenCss(theme) : _code;
348+
return theme ? generateTokenCss(theme, options.experiments) : _code;
349349
}
350350
if (id.includes('pigment-css-react/theme')) {
351351
return `export default ${
@@ -370,7 +370,7 @@ export const plugin = createUnplugin<PigmentOptions, true>((options) => {
370370
},
371371
load(id) {
372372
if (id === VIRTUAL_CSS_FILE && theme) {
373-
return generateTokenCss(theme);
373+
return generateTokenCss(theme, options.experiments);
374374
}
375375
if (id === VIRTUAL_THEME_FILE) {
376376
return `export default ${

packages/pigment-css-vite-plugin/src/index.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ export function pigment(options: PigmentOptions) {
6262
},
6363
load(id) {
6464
if (id === VIRTUAL_CSS_FILE) {
65-
return generateTokenCss(theme);
65+
return generateTokenCss(theme, options.experiments);
6666
}
6767
if (id === VIRTUAL_THEME_FILE) {
6868
return `export default ${JSON.stringify(generateThemeTokens(theme))};`;

0 commit comments

Comments
 (0)