@wordpress/global-styles-engine 1.12.0 → 1.12.1-next.v.202605131032.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/core/render.cjs +260 -222
- package/build/core/render.cjs.map +2 -2
- package/build-module/core/render.mjs +260 -222
- package/build-module/core/render.mjs.map +2 -2
- package/build-types/core/render.d.ts +1 -1
- package/build-types/core/render.d.ts.map +1 -1
- package/build-types/utils/background.d.ts +3 -3
- package/build-types/utils/background.d.ts.map +1 -1
- package/build-types/utils/fluid.d.ts +1 -1
- package/build-types/utils/fluid.d.ts.map +1 -1
- package/build-types/utils/layout.d.ts +13 -13
- package/build-types/utils/layout.d.ts.map +1 -1
- package/build-types/utils/object.d.ts.map +1 -1
- package/package.json +6 -6
- package/src/core/render.tsx +415 -301
- package/src/test/render.test.ts +437 -0
package/src/core/render.tsx
CHANGED
|
@@ -138,6 +138,44 @@ export type BlockSelectors = Record<
|
|
|
138
138
|
}
|
|
139
139
|
>;
|
|
140
140
|
|
|
141
|
+
/**
|
|
142
|
+
* Style node metadata used to render one selector's style rules.
|
|
143
|
+
*
|
|
144
|
+
* - `styles`: theme.json style object for this node.
|
|
145
|
+
* - `selector`: CSS selector used for the node's base declarations.
|
|
146
|
+
* - `selectorSuffix`: optional suffix used to append additional selectors,
|
|
147
|
+
* such as pseudo selectors, to base and feature selectors.
|
|
148
|
+
* - `mediaQuery`: optional media query wrapping this node's rules.
|
|
149
|
+
* - `skipSelectorWrapper`: omits the `:root :where()` specificity wrapper.
|
|
150
|
+
* - `duotoneSelector`: alternate selector for duotone filter declarations.
|
|
151
|
+
* - `featureSelectors`: feature-level selectors for block supports.
|
|
152
|
+
* - `fallbackGapValue`: fallback block gap value used by layout rules.
|
|
153
|
+
* - `hasLayoutSupport`: whether layout styles can be generated for the node.
|
|
154
|
+
* - `isStyleVariation`: whether this node is a block style variation.
|
|
155
|
+
* - `layoutSelector`: optional selector override for layout styles.
|
|
156
|
+
* - `layoutHasBlockGapSupport`: optional block gap support override for layout styles.
|
|
157
|
+
* - `name`: block name used by block-specific declaration adjustments.
|
|
158
|
+
* - `elementName`: element name used to resolve valid pseudo selectors.
|
|
159
|
+
*/
|
|
160
|
+
interface StylesNode {
|
|
161
|
+
styles: any;
|
|
162
|
+
selector: string;
|
|
163
|
+
selectorSuffix?: string;
|
|
164
|
+
mediaQuery?: string;
|
|
165
|
+
skipSelectorWrapper?: boolean;
|
|
166
|
+
duotoneSelector?: string;
|
|
167
|
+
featureSelectors?:
|
|
168
|
+
| string
|
|
169
|
+
| Record< string, string | Record< string, string > >;
|
|
170
|
+
fallbackGapValue?: string;
|
|
171
|
+
hasLayoutSupport?: boolean;
|
|
172
|
+
isStyleVariation?: boolean;
|
|
173
|
+
layoutSelector?: string;
|
|
174
|
+
layoutHasBlockGapSupport?: boolean;
|
|
175
|
+
name?: string;
|
|
176
|
+
elementName?: string;
|
|
177
|
+
}
|
|
178
|
+
|
|
141
179
|
type ElementName = keyof typeof ELEMENTS;
|
|
142
180
|
|
|
143
181
|
// Elements that rely on class names in their selectors.
|
|
@@ -163,6 +201,38 @@ const VALID_BLOCK_PSEUDO_SELECTORS: Record< string, string[] > = {
|
|
|
163
201
|
'core/navigation-link': [ ':hover', ':focus', ':focus-visible', ':active' ],
|
|
164
202
|
};
|
|
165
203
|
|
|
204
|
+
// The valid pseudo-selectors that can be used for elements.
|
|
205
|
+
// Keep in sync with WP_Theme_JSON_Gutenberg::VALID_ELEMENT_PSEUDO_SELECTORS.
|
|
206
|
+
const VALID_ELEMENT_PSEUDO_SELECTORS: Record< string, string[] > = {
|
|
207
|
+
link: [
|
|
208
|
+
':link',
|
|
209
|
+
':any-link',
|
|
210
|
+
':visited',
|
|
211
|
+
':hover',
|
|
212
|
+
':focus',
|
|
213
|
+
':focus-visible',
|
|
214
|
+
':active',
|
|
215
|
+
],
|
|
216
|
+
button: [
|
|
217
|
+
':link',
|
|
218
|
+
':any-link',
|
|
219
|
+
':visited',
|
|
220
|
+
':hover',
|
|
221
|
+
':focus',
|
|
222
|
+
':focus-visible',
|
|
223
|
+
':active',
|
|
224
|
+
],
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Responsive breakpoint state keys and their corresponding CSS media queries.
|
|
229
|
+
* Keep in sync with WP_Theme_JSON_Gutenberg::RESPONSIVE_BREAKPOINTS.
|
|
230
|
+
*/
|
|
231
|
+
const RESPONSIVE_BREAKPOINTS: Record< string, string > = {
|
|
232
|
+
mobile: '@media (width <= 480px)',
|
|
233
|
+
tablet: '@media (480px < width <= 782px)',
|
|
234
|
+
};
|
|
235
|
+
|
|
166
236
|
/**
|
|
167
237
|
* Transform given preset tree into a set of preset class declarations.
|
|
168
238
|
*
|
|
@@ -875,9 +945,12 @@ function pickStyleAndPseudoKeys(
|
|
|
875
945
|
const allowedPseudoSelectors = blockName
|
|
876
946
|
? VALID_BLOCK_PSEUDO_SELECTORS[ blockName ] ?? []
|
|
877
947
|
: [];
|
|
948
|
+
|
|
878
949
|
const pickedEntries = entries.filter(
|
|
879
950
|
( [ key ] ) =>
|
|
880
|
-
STYLE_KEYS.includes( key ) ||
|
|
951
|
+
STYLE_KEYS.includes( key ) ||
|
|
952
|
+
allowedPseudoSelectors.includes( key ) ||
|
|
953
|
+
RESPONSIVE_BREAKPOINTS[ key ]
|
|
881
954
|
);
|
|
882
955
|
// clone the style objects so that `getFeatureDeclarations` can remove consumed keys from it
|
|
883
956
|
const clonedEntries = pickedEntries.map( ( [ key, style ] ) => [
|
|
@@ -887,109 +960,157 @@ function pickStyleAndPseudoKeys(
|
|
|
887
960
|
return Object.fromEntries( clonedEntries );
|
|
888
961
|
}
|
|
889
962
|
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
963
|
+
/**
|
|
964
|
+
* Creates style nodes for configured block and element pseudo selectors.
|
|
965
|
+
*
|
|
966
|
+
* Only pseudo selectors listed in the matching block or element allow-list are
|
|
967
|
+
* considered. This mirrors the PHP renderer and avoids treating arbitrary
|
|
968
|
+
* colon-prefixed keys as pseudo selectors.
|
|
969
|
+
*
|
|
970
|
+
* @param node Style node that may contain configured pseudo styles.
|
|
971
|
+
* @return Style nodes for the configured pseudo states.
|
|
972
|
+
*/
|
|
973
|
+
function getPseudoStyleNodes( node: StylesNode ): StylesNode[] {
|
|
974
|
+
const {
|
|
975
|
+
styles,
|
|
976
|
+
selector,
|
|
977
|
+
featureSelectors,
|
|
978
|
+
name,
|
|
979
|
+
elementName,
|
|
980
|
+
mediaQuery,
|
|
981
|
+
} = node;
|
|
982
|
+
const pseudoSelectors = name
|
|
983
|
+
? VALID_BLOCK_PSEUDO_SELECTORS[ name ] ?? []
|
|
984
|
+
: VALID_ELEMENT_PSEUDO_SELECTORS[ elementName ?? '' ] ?? [];
|
|
985
|
+
|
|
986
|
+
if ( ! pseudoSelectors.length ) {
|
|
987
|
+
return [];
|
|
908
988
|
}
|
|
909
989
|
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
990
|
+
return pseudoSelectors.flatMap( ( pseudoSelector ) => {
|
|
991
|
+
const pseudoStyles = styles?.[ pseudoSelector ];
|
|
992
|
+
if ( ! pseudoStyles || typeof pseudoStyles !== 'object' ) {
|
|
993
|
+
return [];
|
|
913
994
|
}
|
|
914
995
|
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
featureSelectors
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
pseudoFeatureDeclarations = updateButtonWidthDeclarations(
|
|
932
|
-
pseudoFeatureDeclarations,
|
|
933
|
-
treeSettings
|
|
934
|
-
);
|
|
996
|
+
return [
|
|
997
|
+
{
|
|
998
|
+
styles: JSON.parse( JSON.stringify( pseudoStyles ) ),
|
|
999
|
+
selector,
|
|
1000
|
+
selectorSuffix: pseudoSelector,
|
|
1001
|
+
mediaQuery,
|
|
1002
|
+
featureSelectors:
|
|
1003
|
+
featureSelectors && typeof featureSelectors !== 'string'
|
|
1004
|
+
? featureSelectors
|
|
1005
|
+
: undefined,
|
|
1006
|
+
name,
|
|
1007
|
+
elementName,
|
|
1008
|
+
},
|
|
1009
|
+
];
|
|
1010
|
+
} );
|
|
1011
|
+
}
|
|
935
1012
|
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
1013
|
+
/**
|
|
1014
|
+
* Creates style nodes for configured responsive breakpoint states.
|
|
1015
|
+
*
|
|
1016
|
+
* Breakpoint nodes render feature-level, base, and pseudo declarations through
|
|
1017
|
+
* the normal node renderer.
|
|
1018
|
+
*
|
|
1019
|
+
* @param node Style node that may contain configured responsive state styles.
|
|
1020
|
+
* @return Responsive style nodes in configured breakpoint order.
|
|
1021
|
+
*/
|
|
1022
|
+
function getResponsiveStyleNodes( node: StylesNode ): StylesNode[] {
|
|
1023
|
+
const {
|
|
1024
|
+
styles,
|
|
1025
|
+
selector,
|
|
1026
|
+
featureSelectors,
|
|
1027
|
+
name,
|
|
1028
|
+
elementName,
|
|
1029
|
+
isStyleVariation,
|
|
1030
|
+
} = node;
|
|
1031
|
+
|
|
1032
|
+
if ( ! name && ! elementName ) {
|
|
1033
|
+
return [];
|
|
1034
|
+
}
|
|
956
1035
|
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
1036
|
+
return Object.entries( RESPONSIVE_BREAKPOINTS ).flatMap(
|
|
1037
|
+
( [ breakpointKey, mediaQuery ] ) => {
|
|
1038
|
+
const breakpointStyles = styles?.[ breakpointKey ];
|
|
1039
|
+
if ( ! breakpointStyles || typeof breakpointStyles !== 'object' ) {
|
|
1040
|
+
return [];
|
|
1041
|
+
}
|
|
960
1042
|
|
|
961
|
-
|
|
962
|
-
|
|
1043
|
+
return [
|
|
1044
|
+
{
|
|
1045
|
+
styles: JSON.parse( JSON.stringify( breakpointStyles ) ),
|
|
1046
|
+
selector,
|
|
1047
|
+
mediaQuery,
|
|
1048
|
+
featureSelectors:
|
|
1049
|
+
featureSelectors && typeof featureSelectors !== 'string'
|
|
1050
|
+
? featureSelectors
|
|
1051
|
+
: undefined,
|
|
1052
|
+
name,
|
|
1053
|
+
elementName,
|
|
1054
|
+
isStyleVariation,
|
|
1055
|
+
},
|
|
1056
|
+
];
|
|
963
1057
|
}
|
|
1058
|
+
);
|
|
1059
|
+
}
|
|
964
1060
|
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
1061
|
+
/**
|
|
1062
|
+
* Scopes feature selectors to a style variation selector.
|
|
1063
|
+
*
|
|
1064
|
+
* Variation feature selectors are compound selectors rather than suffixes. For
|
|
1065
|
+
* example, `.wp-image-spacing` becomes `.is-style-foo.wp-image.wp-image-spacing`.
|
|
1066
|
+
*
|
|
1067
|
+
* @param featureSelectors Feature-level selectors from a style node.
|
|
1068
|
+
* @param styleVariationSelector Selector for the style variation.
|
|
1069
|
+
* @return Feature-level selectors scoped to the style variation.
|
|
1070
|
+
*/
|
|
1071
|
+
function getVariationFeatureSelectors(
|
|
1072
|
+
featureSelectors: StylesNode[ 'featureSelectors' ],
|
|
1073
|
+
styleVariationSelector: string
|
|
1074
|
+
): StylesNode[ 'featureSelectors' ] {
|
|
1075
|
+
if ( ! featureSelectors || typeof featureSelectors === 'string' ) {
|
|
1076
|
+
return undefined;
|
|
1077
|
+
}
|
|
969
1078
|
|
|
970
|
-
|
|
971
|
-
|
|
1079
|
+
return Object.fromEntries(
|
|
1080
|
+
Object.entries( featureSelectors ).map( ( [ feature, selector ] ) => {
|
|
1081
|
+
if ( typeof selector === 'string' ) {
|
|
1082
|
+
return [
|
|
1083
|
+
feature,
|
|
1084
|
+
concatFeatureVariationSelectorString(
|
|
1085
|
+
selector,
|
|
1086
|
+
styleVariationSelector
|
|
1087
|
+
),
|
|
1088
|
+
];
|
|
1089
|
+
}
|
|
972
1090
|
|
|
973
|
-
|
|
1091
|
+
return [
|
|
1092
|
+
feature,
|
|
1093
|
+
Object.fromEntries(
|
|
1094
|
+
Object.entries( selector ).map(
|
|
1095
|
+
( [ subfeature, subfeatureSelector ] ) => [
|
|
1096
|
+
subfeature,
|
|
1097
|
+
concatFeatureVariationSelectorString(
|
|
1098
|
+
subfeatureSelector,
|
|
1099
|
+
styleVariationSelector
|
|
1100
|
+
),
|
|
1101
|
+
]
|
|
1102
|
+
)
|
|
1103
|
+
),
|
|
1104
|
+
];
|
|
1105
|
+
} )
|
|
1106
|
+
);
|
|
974
1107
|
}
|
|
975
1108
|
|
|
976
1109
|
export const getNodesWithStyles = (
|
|
977
1110
|
tree: GlobalStylesConfig,
|
|
978
1111
|
blockSelectors: string | BlockSelectors
|
|
979
1112
|
): any[] => {
|
|
980
|
-
const nodes:
|
|
981
|
-
styles: Partial< Omit< GlobalStylesStyles, 'elements' | 'blocks' > >;
|
|
982
|
-
selector: string;
|
|
983
|
-
skipSelectorWrapper?: boolean;
|
|
984
|
-
duotoneSelector?: string;
|
|
985
|
-
featureSelectors?:
|
|
986
|
-
| string
|
|
987
|
-
| Record< string, string | Record< string, string > >;
|
|
988
|
-
fallbackGapValue?: string;
|
|
989
|
-
hasLayoutSupport?: boolean;
|
|
990
|
-
styleVariationSelectors?: Record< string, string >;
|
|
991
|
-
name?: string;
|
|
992
|
-
}[] = [];
|
|
1113
|
+
const nodes: StylesNode[] = [];
|
|
993
1114
|
|
|
994
1115
|
if ( ! tree?.styles ) {
|
|
995
1116
|
return nodes;
|
|
@@ -1012,6 +1133,7 @@ export const getNodesWithStyles = (
|
|
|
1012
1133
|
nodes.push( {
|
|
1013
1134
|
styles: tree.styles?.elements?.[ name ] ?? {},
|
|
1014
1135
|
selector: selector as string,
|
|
1136
|
+
elementName: name,
|
|
1015
1137
|
// Top level elements that don't use a class name should not receive the
|
|
1016
1138
|
// `:root :where()` wrapper to maintain backwards compatibility.
|
|
1017
1139
|
skipSelectorWrapper: ! (
|
|
@@ -1027,22 +1149,20 @@ export const getNodesWithStyles = (
|
|
|
1027
1149
|
const blockStyles = pickStyleAndPseudoKeys( node, blockName );
|
|
1028
1150
|
const typedNode = node as BlockNode;
|
|
1029
1151
|
|
|
1030
|
-
// Store variation
|
|
1031
|
-
// Variations should be processed AFTER the main block styles to match PHP order.
|
|
1152
|
+
// Store variation child nodes so they can be inserted after the block's own elements.
|
|
1032
1153
|
const variationNodesToAdd: typeof nodes = [];
|
|
1154
|
+
const variationStyleNodesToAdd: typeof nodes = [];
|
|
1033
1155
|
|
|
1034
1156
|
if ( typedNode?.variations ) {
|
|
1035
|
-
const variations: Record< string, any > = {};
|
|
1036
1157
|
Object.entries( typedNode.variations ).forEach(
|
|
1037
1158
|
( [ variationName, variation ] ) => {
|
|
1038
1159
|
const typedVariation = variation as BlockVariation;
|
|
1039
|
-
|
|
1160
|
+
const variationStyles = pickStyleAndPseudoKeys(
|
|
1040
1161
|
typedVariation,
|
|
1041
1162
|
blockName
|
|
1042
1163
|
);
|
|
1043
1164
|
if ( typedVariation?.css ) {
|
|
1044
|
-
|
|
1045
|
-
typedVariation.css;
|
|
1165
|
+
variationStyles.css = typedVariation.css;
|
|
1046
1166
|
}
|
|
1047
1167
|
const variationSelector =
|
|
1048
1168
|
typeof blockSelectors !== 'string'
|
|
@@ -1051,6 +1171,29 @@ export const getNodesWithStyles = (
|
|
|
1051
1171
|
variationName
|
|
1052
1172
|
]
|
|
1053
1173
|
: undefined;
|
|
1174
|
+
if (
|
|
1175
|
+
variationSelector &&
|
|
1176
|
+
typeof blockSelectors !== 'string'
|
|
1177
|
+
) {
|
|
1178
|
+
const blockSelector = blockSelectors[ blockName ];
|
|
1179
|
+
variationStyleNodesToAdd.push( {
|
|
1180
|
+
styles: variationStyles,
|
|
1181
|
+
selector: variationSelector,
|
|
1182
|
+
featureSelectors: getVariationFeatureSelectors(
|
|
1183
|
+
blockSelector?.featureSelectors,
|
|
1184
|
+
variationSelector
|
|
1185
|
+
),
|
|
1186
|
+
fallbackGapValue:
|
|
1187
|
+
blockSelector?.fallbackGapValue,
|
|
1188
|
+
hasLayoutSupport:
|
|
1189
|
+
blockSelector?.hasLayoutSupport,
|
|
1190
|
+
isStyleVariation: true,
|
|
1191
|
+
layoutSelector:
|
|
1192
|
+
variationSelector + blockSelector.selector,
|
|
1193
|
+
layoutHasBlockGapSupport: true,
|
|
1194
|
+
name: blockName,
|
|
1195
|
+
} );
|
|
1196
|
+
}
|
|
1054
1197
|
|
|
1055
1198
|
// Process the variation's inner element styles.
|
|
1056
1199
|
// This comes before the inner block styles so the
|
|
@@ -1069,6 +1212,8 @@ export const getNodesWithStyles = (
|
|
|
1069
1212
|
variationSelector,
|
|
1070
1213
|
ELEMENTS[ element as ElementName ]
|
|
1071
1214
|
),
|
|
1215
|
+
elementName: element,
|
|
1216
|
+
isStyleVariation: true,
|
|
1072
1217
|
} );
|
|
1073
1218
|
}
|
|
1074
1219
|
} );
|
|
@@ -1127,6 +1272,8 @@ export const getNodesWithStyles = (
|
|
|
1127
1272
|
|
|
1128
1273
|
variationNodesToAdd.push( {
|
|
1129
1274
|
selector: variationBlockSelector,
|
|
1275
|
+
name: variationBlockName,
|
|
1276
|
+
isStyleVariation: true,
|
|
1130
1277
|
duotoneSelector: variationDuotoneSelector,
|
|
1131
1278
|
featureSelectors: variationFeatureSelectors,
|
|
1132
1279
|
fallbackGapValue:
|
|
@@ -1161,6 +1308,9 @@ export const getNodesWithStyles = (
|
|
|
1161
1308
|
variationBlockElement as ElementName
|
|
1162
1309
|
]
|
|
1163
1310
|
),
|
|
1311
|
+
elementName:
|
|
1312
|
+
variationBlockElement,
|
|
1313
|
+
isStyleVariation: true,
|
|
1164
1314
|
} );
|
|
1165
1315
|
}
|
|
1166
1316
|
}
|
|
@@ -1169,7 +1319,6 @@ export const getNodesWithStyles = (
|
|
|
1169
1319
|
);
|
|
1170
1320
|
}
|
|
1171
1321
|
);
|
|
1172
|
-
blockStyles.variations = variations;
|
|
1173
1322
|
}
|
|
1174
1323
|
|
|
1175
1324
|
if (
|
|
@@ -1187,12 +1336,12 @@ export const getNodesWithStyles = (
|
|
|
1187
1336
|
styles: blockStyles,
|
|
1188
1337
|
featureSelectors:
|
|
1189
1338
|
blockSelectors[ blockName ].featureSelectors,
|
|
1190
|
-
styleVariationSelectors:
|
|
1191
|
-
blockSelectors[ blockName ].styleVariationSelectors,
|
|
1192
1339
|
name: blockName,
|
|
1193
1340
|
} );
|
|
1194
1341
|
}
|
|
1195
1342
|
|
|
1343
|
+
nodes.push( ...variationStyleNodesToAdd );
|
|
1344
|
+
|
|
1196
1345
|
Object.entries( typedNode?.elements ?? {} ).forEach(
|
|
1197
1346
|
( [ elementName, value ] ) => {
|
|
1198
1347
|
if (
|
|
@@ -1216,6 +1365,7 @@ export const getNodesWithStyles = (
|
|
|
1216
1365
|
);
|
|
1217
1366
|
} )
|
|
1218
1367
|
.join( ',' ),
|
|
1368
|
+
elementName,
|
|
1219
1369
|
} );
|
|
1220
1370
|
}
|
|
1221
1371
|
}
|
|
@@ -1437,6 +1587,154 @@ export const generateCustomProperties = (
|
|
|
1437
1587
|
return ruleset;
|
|
1438
1588
|
};
|
|
1439
1589
|
|
|
1590
|
+
/**
|
|
1591
|
+
* Renders CSS rules for a single style node.
|
|
1592
|
+
*
|
|
1593
|
+
* The node renderer handles feature-level selectors, duotone declarations,
|
|
1594
|
+
* layout styles, base declarations, and custom CSS. State nodes are expanded
|
|
1595
|
+
* before rendering so ordering matches the PHP renderer.
|
|
1596
|
+
*
|
|
1597
|
+
* @param node Style node metadata and styles.
|
|
1598
|
+
* @param context Render context and feature flags.
|
|
1599
|
+
* @param context.tree Global styles tree.
|
|
1600
|
+
* @param context.useRootPaddingAlign Whether root padding alignment is enabled.
|
|
1601
|
+
* @param context.disableLayoutStyles Whether layout styles are disabled.
|
|
1602
|
+
* @param context.hasBlockGapSupport Whether block gap support is enabled.
|
|
1603
|
+
* @param context.hasFallbackGapSupport Whether fallback gap support is enabled.
|
|
1604
|
+
* @param context.disableRootPadding Whether root padding declarations are disabled.
|
|
1605
|
+
* @return Rendered CSS rules for the node.
|
|
1606
|
+
*/
|
|
1607
|
+
function renderStylesNode(
|
|
1608
|
+
node: StylesNode,
|
|
1609
|
+
{
|
|
1610
|
+
tree,
|
|
1611
|
+
useRootPaddingAlign,
|
|
1612
|
+
disableLayoutStyles,
|
|
1613
|
+
hasBlockGapSupport,
|
|
1614
|
+
hasFallbackGapSupport,
|
|
1615
|
+
disableRootPadding,
|
|
1616
|
+
}: {
|
|
1617
|
+
tree: GlobalStylesConfig;
|
|
1618
|
+
useRootPaddingAlign?: boolean;
|
|
1619
|
+
disableLayoutStyles: boolean;
|
|
1620
|
+
hasBlockGapSupport?: boolean;
|
|
1621
|
+
hasFallbackGapSupport?: boolean;
|
|
1622
|
+
disableRootPadding: boolean;
|
|
1623
|
+
}
|
|
1624
|
+
): string {
|
|
1625
|
+
const {
|
|
1626
|
+
selector,
|
|
1627
|
+
selectorSuffix,
|
|
1628
|
+
mediaQuery,
|
|
1629
|
+
duotoneSelector,
|
|
1630
|
+
styles,
|
|
1631
|
+
fallbackGapValue,
|
|
1632
|
+
hasLayoutSupport,
|
|
1633
|
+
featureSelectors,
|
|
1634
|
+
layoutSelector,
|
|
1635
|
+
layoutHasBlockGapSupport,
|
|
1636
|
+
skipSelectorWrapper,
|
|
1637
|
+
name,
|
|
1638
|
+
} = node;
|
|
1639
|
+
let ruleset = '';
|
|
1640
|
+
const effectiveSelector = selectorSuffix
|
|
1641
|
+
? appendToSelector( selector, selectorSuffix )
|
|
1642
|
+
: selector;
|
|
1643
|
+
|
|
1644
|
+
// Process styles for block support features with custom feature level
|
|
1645
|
+
// CSS selectors set.
|
|
1646
|
+
if ( featureSelectors && typeof featureSelectors !== 'string' ) {
|
|
1647
|
+
let featureDeclarations = getFeatureDeclarations(
|
|
1648
|
+
featureSelectors,
|
|
1649
|
+
styles
|
|
1650
|
+
);
|
|
1651
|
+
|
|
1652
|
+
// Update text indent selector for paragraph blocks based on the textIndent setting.
|
|
1653
|
+
featureDeclarations = updateParagraphTextIndentSelector(
|
|
1654
|
+
featureDeclarations,
|
|
1655
|
+
tree.settings,
|
|
1656
|
+
name
|
|
1657
|
+
);
|
|
1658
|
+
|
|
1659
|
+
// Update button width declarations for percentage values to use calc() with block gap.
|
|
1660
|
+
featureDeclarations = updateButtonWidthDeclarations(
|
|
1661
|
+
featureDeclarations,
|
|
1662
|
+
tree.settings
|
|
1663
|
+
);
|
|
1664
|
+
|
|
1665
|
+
Object.entries( featureDeclarations ).forEach(
|
|
1666
|
+
( [ featureSelector, declarations ] ) => {
|
|
1667
|
+
if ( declarations.length ) {
|
|
1668
|
+
const selectorForRule = selectorSuffix
|
|
1669
|
+
? appendToSelector( featureSelector, selectorSuffix )
|
|
1670
|
+
: featureSelector;
|
|
1671
|
+
const rules = declarations.join( ';' );
|
|
1672
|
+
ruleset += `:root :where(${ selectorForRule }){${ rules };}`;
|
|
1673
|
+
}
|
|
1674
|
+
}
|
|
1675
|
+
);
|
|
1676
|
+
}
|
|
1677
|
+
|
|
1678
|
+
// Process duotone styles.
|
|
1679
|
+
if ( duotoneSelector ) {
|
|
1680
|
+
const duotoneStyles: any = {};
|
|
1681
|
+
if ( styles?.filter ) {
|
|
1682
|
+
duotoneStyles.filter = styles.filter;
|
|
1683
|
+
delete styles.filter;
|
|
1684
|
+
}
|
|
1685
|
+
const duotoneDeclarations = getStylesDeclarations( duotoneStyles );
|
|
1686
|
+
if ( duotoneDeclarations.length ) {
|
|
1687
|
+
ruleset += `${ duotoneSelector }{${ duotoneDeclarations.join(
|
|
1688
|
+
';'
|
|
1689
|
+
) };}`;
|
|
1690
|
+
}
|
|
1691
|
+
}
|
|
1692
|
+
|
|
1693
|
+
// Process blockGap and layout styles.
|
|
1694
|
+
const selectorForLayout = layoutSelector ?? effectiveSelector;
|
|
1695
|
+
const hasBlockGapSupportForLayout =
|
|
1696
|
+
layoutHasBlockGapSupport ?? hasBlockGapSupport;
|
|
1697
|
+
if (
|
|
1698
|
+
! disableLayoutStyles &&
|
|
1699
|
+
( ROOT_BLOCK_SELECTOR === selectorForLayout || hasLayoutSupport )
|
|
1700
|
+
) {
|
|
1701
|
+
ruleset += getLayoutStyles( {
|
|
1702
|
+
style: styles,
|
|
1703
|
+
selector: selectorForLayout,
|
|
1704
|
+
hasBlockGapSupport: hasBlockGapSupportForLayout,
|
|
1705
|
+
hasFallbackGapSupport,
|
|
1706
|
+
fallbackGapValue,
|
|
1707
|
+
} );
|
|
1708
|
+
}
|
|
1709
|
+
|
|
1710
|
+
// Process the remaining block styles (they use either normal block class or __experimentalSelector).
|
|
1711
|
+
const styleDeclarations = getStylesDeclarations(
|
|
1712
|
+
styles,
|
|
1713
|
+
effectiveSelector,
|
|
1714
|
+
useRootPaddingAlign,
|
|
1715
|
+
tree,
|
|
1716
|
+
disableRootPadding
|
|
1717
|
+
);
|
|
1718
|
+
if ( styleDeclarations?.length ) {
|
|
1719
|
+
const generalSelector = skipSelectorWrapper
|
|
1720
|
+
? effectiveSelector
|
|
1721
|
+
: `:root :where(${ effectiveSelector })`;
|
|
1722
|
+
ruleset += `${ generalSelector }{${ styleDeclarations.join( ';' ) };}`;
|
|
1723
|
+
}
|
|
1724
|
+
if ( styles?.css ) {
|
|
1725
|
+
ruleset += processCSSNesting(
|
|
1726
|
+
styles.css,
|
|
1727
|
+
`:root :where(${ effectiveSelector })`
|
|
1728
|
+
);
|
|
1729
|
+
}
|
|
1730
|
+
|
|
1731
|
+
if ( mediaQuery && ruleset ) {
|
|
1732
|
+
return `${ mediaQuery }{${ ruleset }}`;
|
|
1733
|
+
}
|
|
1734
|
+
|
|
1735
|
+
return ruleset;
|
|
1736
|
+
}
|
|
1737
|
+
|
|
1440
1738
|
export const transformToStyles = (
|
|
1441
1739
|
tree: GlobalStylesConfig,
|
|
1442
1740
|
blockSelectors: string | BlockSelectors,
|
|
@@ -1506,213 +1804,29 @@ export const transformToStyles = (
|
|
|
1506
1804
|
}
|
|
1507
1805
|
|
|
1508
1806
|
if ( options.blockStyles ) {
|
|
1509
|
-
nodesWithStyles.forEach(
|
|
1510
|
-
( {
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
styles,
|
|
1514
|
-
fallbackGapValue,
|
|
1515
|
-
hasLayoutSupport,
|
|
1516
|
-
featureSelectors,
|
|
1517
|
-
styleVariationSelectors,
|
|
1518
|
-
skipSelectorWrapper,
|
|
1519
|
-
name,
|
|
1520
|
-
} ) => {
|
|
1521
|
-
// Process styles for block support features with custom feature level
|
|
1522
|
-
// CSS selectors set.
|
|
1523
|
-
if ( featureSelectors ) {
|
|
1524
|
-
let featureDeclarations = getFeatureDeclarations(
|
|
1525
|
-
featureSelectors,
|
|
1526
|
-
styles
|
|
1527
|
-
);
|
|
1528
|
-
|
|
1529
|
-
// Update text indent selector for paragraph blocks based on the textIndent setting.
|
|
1530
|
-
featureDeclarations = updateParagraphTextIndentSelector(
|
|
1531
|
-
featureDeclarations,
|
|
1532
|
-
tree.settings,
|
|
1533
|
-
name
|
|
1534
|
-
);
|
|
1535
|
-
|
|
1536
|
-
// Update button width declarations for percentage values to use calc() with block gap.
|
|
1537
|
-
featureDeclarations = updateButtonWidthDeclarations(
|
|
1538
|
-
featureDeclarations,
|
|
1539
|
-
tree.settings
|
|
1540
|
-
);
|
|
1541
|
-
|
|
1542
|
-
Object.entries( featureDeclarations ).forEach(
|
|
1543
|
-
( [ cssSelector, declarations ] ) => {
|
|
1544
|
-
if ( declarations.length ) {
|
|
1545
|
-
const rules = declarations.join( ';' );
|
|
1546
|
-
ruleset += `:root :where(${ cssSelector }){${ rules };}`;
|
|
1547
|
-
}
|
|
1548
|
-
}
|
|
1549
|
-
);
|
|
1550
|
-
}
|
|
1551
|
-
|
|
1552
|
-
// Process duotone styles.
|
|
1553
|
-
if ( duotoneSelector ) {
|
|
1554
|
-
const duotoneStyles: any = {};
|
|
1555
|
-
if ( styles?.filter ) {
|
|
1556
|
-
duotoneStyles.filter = styles.filter;
|
|
1557
|
-
delete styles.filter;
|
|
1558
|
-
}
|
|
1559
|
-
const duotoneDeclarations =
|
|
1560
|
-
getStylesDeclarations( duotoneStyles );
|
|
1561
|
-
if ( duotoneDeclarations.length ) {
|
|
1562
|
-
ruleset += `${ duotoneSelector }{${ duotoneDeclarations.join(
|
|
1563
|
-
';'
|
|
1564
|
-
) };}`;
|
|
1565
|
-
}
|
|
1566
|
-
}
|
|
1567
|
-
|
|
1568
|
-
// Process blockGap and layout styles.
|
|
1569
|
-
if (
|
|
1570
|
-
! disableLayoutStyles &&
|
|
1571
|
-
( ROOT_BLOCK_SELECTOR === selector || hasLayoutSupport )
|
|
1572
|
-
) {
|
|
1573
|
-
ruleset += getLayoutStyles( {
|
|
1574
|
-
style: styles,
|
|
1575
|
-
selector,
|
|
1576
|
-
hasBlockGapSupport,
|
|
1577
|
-
hasFallbackGapSupport,
|
|
1578
|
-
fallbackGapValue,
|
|
1579
|
-
} );
|
|
1580
|
-
}
|
|
1807
|
+
nodesWithStyles.forEach( ( node ) => {
|
|
1808
|
+
if ( node.isStyleVariation && ! options.variationStyles ) {
|
|
1809
|
+
return;
|
|
1810
|
+
}
|
|
1581
1811
|
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1812
|
+
const responsiveNodes = getResponsiveStyleNodes( node );
|
|
1813
|
+
// Match PHP node order: base, responsive base, pseudo, responsive pseudo.
|
|
1814
|
+
[
|
|
1815
|
+
node,
|
|
1816
|
+
...responsiveNodes,
|
|
1817
|
+
...getPseudoStyleNodes( node ),
|
|
1818
|
+
...responsiveNodes.flatMap( getPseudoStyleNodes ),
|
|
1819
|
+
].forEach( ( expandedNode ) => {
|
|
1820
|
+
ruleset += renderStylesNode( expandedNode, {
|
|
1587
1821
|
tree,
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
) };}`;
|
|
1597
|
-
}
|
|
1598
|
-
if ( styles?.css ) {
|
|
1599
|
-
ruleset += processCSSNesting(
|
|
1600
|
-
styles.css,
|
|
1601
|
-
`:root :where(${ selector })`
|
|
1602
|
-
);
|
|
1603
|
-
}
|
|
1604
|
-
|
|
1605
|
-
if ( options.variationStyles && styleVariationSelectors ) {
|
|
1606
|
-
Object.entries( styleVariationSelectors ).forEach(
|
|
1607
|
-
( [ styleVariationName, styleVariationSelector ] ) => {
|
|
1608
|
-
const styleVariations =
|
|
1609
|
-
styles?.variations?.[ styleVariationName ];
|
|
1610
|
-
if ( styleVariations ) {
|
|
1611
|
-
// If the block uses any custom selectors for block support, add those first.
|
|
1612
|
-
if ( featureSelectors ) {
|
|
1613
|
-
let featureDeclarations =
|
|
1614
|
-
getFeatureDeclarations(
|
|
1615
|
-
featureSelectors,
|
|
1616
|
-
styleVariations
|
|
1617
|
-
);
|
|
1618
|
-
|
|
1619
|
-
// Update text indent selector for paragraph blocks based on the textIndent setting.
|
|
1620
|
-
featureDeclarations =
|
|
1621
|
-
updateParagraphTextIndentSelector(
|
|
1622
|
-
featureDeclarations,
|
|
1623
|
-
tree.settings,
|
|
1624
|
-
name
|
|
1625
|
-
);
|
|
1626
|
-
|
|
1627
|
-
// Update button width declarations for percentage values to use calc() with block gap.
|
|
1628
|
-
featureDeclarations =
|
|
1629
|
-
updateButtonWidthDeclarations(
|
|
1630
|
-
featureDeclarations,
|
|
1631
|
-
tree.settings
|
|
1632
|
-
);
|
|
1633
|
-
|
|
1634
|
-
Object.entries(
|
|
1635
|
-
featureDeclarations
|
|
1636
|
-
).forEach(
|
|
1637
|
-
( [ baseSelector, declarations ]: [
|
|
1638
|
-
string,
|
|
1639
|
-
string[],
|
|
1640
|
-
] ) => {
|
|
1641
|
-
if ( declarations.length ) {
|
|
1642
|
-
const cssSelector =
|
|
1643
|
-
concatFeatureVariationSelectorString(
|
|
1644
|
-
baseSelector,
|
|
1645
|
-
styleVariationSelector as string
|
|
1646
|
-
);
|
|
1647
|
-
const rules =
|
|
1648
|
-
declarations.join( ';' );
|
|
1649
|
-
ruleset += `:root :where(${ cssSelector }){${ rules };}`;
|
|
1650
|
-
}
|
|
1651
|
-
}
|
|
1652
|
-
);
|
|
1653
|
-
}
|
|
1654
|
-
|
|
1655
|
-
// Otherwise add regular selectors.
|
|
1656
|
-
const styleVariationDeclarations =
|
|
1657
|
-
getStylesDeclarations(
|
|
1658
|
-
styleVariations,
|
|
1659
|
-
styleVariationSelector as string,
|
|
1660
|
-
useRootPaddingAlign,
|
|
1661
|
-
tree
|
|
1662
|
-
);
|
|
1663
|
-
if ( styleVariationDeclarations.length ) {
|
|
1664
|
-
ruleset += `:root :where(${ styleVariationSelector }){${ styleVariationDeclarations.join(
|
|
1665
|
-
';'
|
|
1666
|
-
) };}`;
|
|
1667
|
-
}
|
|
1668
|
-
if ( styleVariations?.css ) {
|
|
1669
|
-
ruleset += processCSSNesting(
|
|
1670
|
-
styleVariations.css,
|
|
1671
|
-
`:root :where(${ styleVariationSelector })`
|
|
1672
|
-
);
|
|
1673
|
-
}
|
|
1674
|
-
|
|
1675
|
-
ruleset = appendPseudoSelectorStyles(
|
|
1676
|
-
styleVariations,
|
|
1677
|
-
styleVariationSelector as string,
|
|
1678
|
-
ruleset,
|
|
1679
|
-
featureSelectors,
|
|
1680
|
-
tree.settings,
|
|
1681
|
-
name,
|
|
1682
|
-
styleVariationSelector as string
|
|
1683
|
-
);
|
|
1684
|
-
|
|
1685
|
-
// Generate layout styles for the variation if it supports layout and has blockGap defined.
|
|
1686
|
-
if (
|
|
1687
|
-
hasLayoutSupport &&
|
|
1688
|
-
styleVariations?.spacing?.blockGap
|
|
1689
|
-
) {
|
|
1690
|
-
// Append block selector to variation selector so layout classes are properly constructed.
|
|
1691
|
-
const variationSelectorWithBlock =
|
|
1692
|
-
styleVariationSelector + selector;
|
|
1693
|
-
ruleset += getLayoutStyles( {
|
|
1694
|
-
style: styleVariations,
|
|
1695
|
-
selector: variationSelectorWithBlock,
|
|
1696
|
-
hasBlockGapSupport: true,
|
|
1697
|
-
hasFallbackGapSupport,
|
|
1698
|
-
fallbackGapValue,
|
|
1699
|
-
} );
|
|
1700
|
-
}
|
|
1701
|
-
}
|
|
1702
|
-
}
|
|
1703
|
-
);
|
|
1704
|
-
}
|
|
1705
|
-
|
|
1706
|
-
ruleset = appendPseudoSelectorStyles(
|
|
1707
|
-
styles,
|
|
1708
|
-
selector,
|
|
1709
|
-
ruleset,
|
|
1710
|
-
featureSelectors,
|
|
1711
|
-
tree.settings,
|
|
1712
|
-
name
|
|
1713
|
-
);
|
|
1714
|
-
}
|
|
1715
|
-
);
|
|
1822
|
+
useRootPaddingAlign,
|
|
1823
|
+
disableLayoutStyles,
|
|
1824
|
+
hasBlockGapSupport,
|
|
1825
|
+
hasFallbackGapSupport,
|
|
1826
|
+
disableRootPadding,
|
|
1827
|
+
} );
|
|
1828
|
+
} );
|
|
1829
|
+
} );
|
|
1716
1830
|
}
|
|
1717
1831
|
|
|
1718
1832
|
if ( options.layoutStyles ) {
|