@stylix/core 6.2.1 → 6.3.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/dist/index.js CHANGED
@@ -1,5 +1,61 @@
1
1
  import { jsx } from 'react/jsx-runtime';
2
- import React, { createContext, useLayoutEffect, useContext, useRef, useEffect } from 'react';
2
+ import React, { createContext, useRef, useInsertionEffect, useContext, useEffect } from 'react';
3
+
4
+ function flattenRules(ctx) {
5
+ return Object.values(ctx.rules)
6
+ .flatMap((val) => (val && val.refs > 0 ? val.rules : []))
7
+ .filter(Boolean);
8
+ }
9
+ /**
10
+ * Applies rules from given StylixContext to the <style> element.
11
+ */
12
+ function applyRules(ctx) {
13
+ if (ctx.styleCollector) {
14
+ const flattenedRules = flattenRules(ctx);
15
+ ctx.styleCollector.length = 0;
16
+ ctx.styleCollector.push(...flattenedRules);
17
+ return;
18
+ }
19
+ if (ctx.ssr)
20
+ return;
21
+ const supportsAdoptedStylesheets = 'adoptedStyleSheets' in document;
22
+ // If there's no style element, and we're in dev mode, create one
23
+ if (!ctx.styleElement && (ctx.devMode || !supportsAdoptedStylesheets)) {
24
+ ctx.styleElement = document.createElement('style');
25
+ ctx.styleElement.className = 'stylix';
26
+ if (ctx.id)
27
+ ctx.styleElement.id = `stylix-${ctx.id}`;
28
+ document.head.appendChild(ctx.styleElement);
29
+ }
30
+ if (ctx.styleElement) {
31
+ // If there's a style element, use it
32
+ const flattenedRules = flattenRules(ctx);
33
+ ctx.styleElement.innerHTML = flattenedRules.join('\n');
34
+ }
35
+ else {
36
+ // Still no stylesheet yet, create one
37
+ if (!ctx.stylesheet) {
38
+ ctx.stylesheet = new CSSStyleSheet();
39
+ if (supportsAdoptedStylesheets) {
40
+ document.adoptedStyleSheets.push(ctx.stylesheet);
41
+ }
42
+ else if (ctx.stylesheet.ownerNode) {
43
+ document.head.appendChild(ctx.stylesheet.ownerNode);
44
+ }
45
+ }
46
+ const stylesheet = ctx.stylesheet;
47
+ const flattenedRules = flattenRules(ctx);
48
+ if (stylesheet.replaceSync) {
49
+ try {
50
+ stylesheet.replaceSync(flattenedRules.join('\n'));
51
+ }
52
+ catch (e) {
53
+ // Errors are ignored, this just means that a browser doesn't support a certain CSS feature.
54
+ console.warn(e);
55
+ }
56
+ }
57
+ }
58
+ }
3
59
 
4
60
  function classifyProps(props, knownStyleProps) {
5
61
  const styles = {};
@@ -587,8 +643,8 @@ function mapObject(source, mapFn, context) {
587
643
 
588
644
  const defaultIgnoreUnits = [
589
645
  'aspect-ratio',
590
- 'columns',
591
646
  'column-count',
647
+ 'columns',
592
648
  'fill-opacity',
593
649
  'flex',
594
650
  'flex-grow',
@@ -596,15 +652,15 @@ const defaultIgnoreUnits = [
596
652
  'font-weight',
597
653
  'line-height',
598
654
  'opacity',
655
+ 'order',
599
656
  'orphans',
600
657
  'stroke-opacity',
601
658
  'widows',
602
659
  'z-index',
603
660
  'zoom',
604
- 'order',
605
661
  ];
606
662
  /**
607
- * Adds unit (px, em, etc) to numeric values for any style properties not included in `ignoreProps`..
663
+ * Adds unit (px, em, etc) to numeric values for any style properties not included in `ignoreProps`.
608
664
  */
609
665
  const defaultUnits = (unit = 'px', ignoreProps = defaultIgnoreUnits) => {
610
666
  return {
@@ -651,6 +707,36 @@ const hoistKeyframes = {
651
707
  },
652
708
  };
653
709
 
710
+ function _hoistLayers(styles, root) {
711
+ for (const key in styles) {
712
+ const value = styles[key];
713
+ if (typeof value === 'string' && key.startsWith('@layer')) {
714
+ // Add layer rules as-is directly to root object
715
+ root['@layer'] ||= [];
716
+ root['@layer'].push(value.replace('@layer', '').trim());
717
+ if (styles !== root)
718
+ delete styles[key];
719
+ }
720
+ else if (isPlainObject(value)) {
721
+ // Recursively flatten nested styles
722
+ _hoistLayers(value, root);
723
+ }
724
+ }
725
+ return styles;
726
+ }
727
+ /**
728
+ * Hoists @layer declarations to root of styles object.
729
+ */
730
+ const hoistLayers = {
731
+ name: 'hoistLayers',
732
+ type: 'processStyles',
733
+ plugin(_ctx, styles) {
734
+ if (styles && typeof styles === 'object' && !Array.isArray(styles))
735
+ styles['@layer'] = [];
736
+ return _hoistLayers(styles, styles);
737
+ },
738
+ };
739
+
654
740
  /**
655
741
  * Expands media objects using the media definitions from the Stylix context.
656
742
  */
@@ -684,10 +770,21 @@ function processMediaStyles(mediaDef, styleProps, styles) {
684
770
  // An object for a style prop is definitely a media object
685
771
  for (const mediaKey in styleValue) {
686
772
  result[mediaKey] ||= [];
687
- result[mediaKey].push(mediaDef[mediaKey]({
688
- // process recursively
689
- [styleKey]: processMediaStyles(mediaDef, styleProps, styleValue[mediaKey]),
690
- }));
773
+ // mediaKey corresponds to a media definition
774
+ if (mediaKey in mediaDef) {
775
+ result[mediaKey].push(mediaDef[mediaKey]({
776
+ // process recursively
777
+ [styleKey]: processMediaStyles(mediaDef, styleProps, styleValue[mediaKey]),
778
+ }));
779
+ }
780
+ // mediaKey does not correspond to a media definition, it must be a @media or @container rule
781
+ else {
782
+ result[mediaKey].push({
783
+ [mediaKey]: {
784
+ [styleKey]: processMediaStyles(mediaDef, styleProps, styleValue[mediaKey]),
785
+ },
786
+ });
787
+ }
691
788
  }
692
789
  continue;
693
790
  }
@@ -889,6 +986,7 @@ const defaultPlugins = [
889
986
  mergeArrays,
890
987
  propCasing,
891
988
  hoistKeyframes,
989
+ hoistLayers,
892
990
  replace$$class,
893
991
  defaultPixelUnits,
894
992
  cleanStyles,
@@ -911,152 +1009,6 @@ function createStyleCollector() {
911
1009
  return collector;
912
1010
  }
913
1011
 
914
- const detectSSR = () => !(typeof window !== 'undefined' && window.document?.head?.appendChild);
915
- function useIsoLayoutEffect(fn, deps, runOnSsr, isSsr = detectSSR()) {
916
- if (isSsr) {
917
- if (runOnSsr)
918
- return fn();
919
- }
920
- else {
921
- // biome-ignore lint/correctness/useHookAtTopLevel: isSsr should never change
922
- // biome-ignore lint/correctness/useExhaustiveDependencies: dependencies are passed as-is
923
- useLayoutEffect(fn, deps);
924
- }
925
- }
926
-
927
- let defaultStyleProps;
928
- function createStylixContext(userValues = {}) {
929
- if (!defaultStyleProps) {
930
- defaultStyleProps = {};
931
- for (const value of cssProps) {
932
- defaultStyleProps[simplifyStylePropName(value)] = value;
933
- }
934
- }
935
- const ctx = {
936
- id: userValues.id || '$default',
937
- devMode: !!userValues.devMode,
938
- styleProps: defaultStyleProps,
939
- media: userValues.media,
940
- styleElement: userValues.styleElement,
941
- plugins: defaultPlugins.flat(),
942
- styleCounter: 0,
943
- rules: {},
944
- ssr: userValues.ssr ?? detectSSR(),
945
- cleanupRequest: undefined,
946
- requestApply: false,
947
- classifyProps(props) {
948
- const [styles, other] = classifyProps(props, this.styleProps);
949
- return [styles, other];
950
- },
951
- };
952
- if (userValues.plugins?.length) {
953
- const flatPlugins = userValues.plugins.flat();
954
- for (const i in flatPlugins) {
955
- const plugin = flatPlugins[i];
956
- let pluginIndex = -1;
957
- if (plugin.before)
958
- pluginIndex = ctx.plugins.findIndex((v) => v.name === plugin.before);
959
- else if (plugin.after) {
960
- pluginIndex = ctx.plugins.findIndex((v) => v.name === plugin.before);
961
- if (pluginIndex !== -1)
962
- pluginIndex += 1;
963
- }
964
- else if (plugin.atIndex !== undefined)
965
- pluginIndex = plugin.atIndex;
966
- if (pluginIndex === -1)
967
- ctx.plugins.push(plugin);
968
- else
969
- ctx.plugins.splice(pluginIndex, 0, plugin);
970
- }
971
- }
972
- applyPlugins('initialize', null, null, ctx);
973
- return ctx;
974
- }
975
- // The React context object
976
- const stylixContext = createContext(undefined);
977
- let defaultStylixContext;
978
- /**
979
- * Gets the current Stylix context.
980
- */
981
- function useStylixContext() {
982
- const ctx = useContext(stylixContext);
983
- if (!ctx) {
984
- if (!defaultStylixContext)
985
- defaultStylixContext = createStylixContext();
986
- return defaultStylixContext;
987
- }
988
- return ctx;
989
- }
990
- function StylixProvider({ id, devMode, plugins, media, styleElement, children, ssr, }) {
991
- const ctx = useRef(null);
992
- if (!ctx.current)
993
- ctx.current = createStylixContext({ id, devMode, plugins, media, styleElement, ssr });
994
- ctx.current.styleCollector = useContext(styleCollectorContext);
995
- // When the component is unmounted, remove the style element, if any
996
- useEffect(() => {
997
- return () => {
998
- ctx.current?.styleElement?.remove();
999
- };
1000
- }, []);
1001
- return jsx(stylixContext.Provider, { value: ctx.current, children: children });
1002
- }
1003
-
1004
- function flattenRules(ctx) {
1005
- return Object.values(ctx.rules)
1006
- .flatMap((val) => (val && val.refs > 0 ? val.rules : []))
1007
- .filter(Boolean);
1008
- }
1009
- /**
1010
- * Applies rules from given StylixContext to the <style> element.
1011
- */
1012
- function applyRules(ctx) {
1013
- if (ctx.styleCollector) {
1014
- const flattenedRules = flattenRules(ctx);
1015
- ctx.styleCollector.length = 0;
1016
- ctx.styleCollector.push(...flattenedRules);
1017
- return;
1018
- }
1019
- if (ctx.ssr)
1020
- return;
1021
- const supportsAdoptedStylesheets = 'adoptedStyleSheets' in document;
1022
- // If there's no style element, and we're in dev mode, create one
1023
- if (!ctx.styleElement && (ctx.devMode || !supportsAdoptedStylesheets)) {
1024
- ctx.styleElement = document.createElement('style');
1025
- ctx.styleElement.className = 'stylix';
1026
- if (ctx.id)
1027
- ctx.styleElement.id = `stylix-${ctx.id}`;
1028
- document.head.appendChild(ctx.styleElement);
1029
- }
1030
- if (ctx.styleElement) {
1031
- // If there's a style element, use it
1032
- const flattenedRules = flattenRules(ctx);
1033
- ctx.styleElement.innerHTML = flattenedRules.join('\n');
1034
- }
1035
- else {
1036
- // Still no stylesheet yet, create one
1037
- if (!ctx.stylesheet) {
1038
- ctx.stylesheet = new CSSStyleSheet();
1039
- if (supportsAdoptedStylesheets) {
1040
- document.adoptedStyleSheets.push(ctx.stylesheet);
1041
- }
1042
- else if (ctx.stylesheet.ownerNode) {
1043
- document.head.appendChild(ctx.stylesheet.ownerNode);
1044
- }
1045
- }
1046
- const stylesheet = ctx.stylesheet;
1047
- const flattenedRules = flattenRules(ctx);
1048
- if (stylesheet.replaceSync) {
1049
- try {
1050
- stylesheet.replaceSync(flattenedRules.join('\n'));
1051
- }
1052
- catch (e) {
1053
- // Errors are ignored, this just means that a browser doesn't support a certain CSS feature.
1054
- console.warn(e);
1055
- }
1056
- }
1057
- }
1058
- }
1059
-
1060
1012
  function getParentComponentName() {
1061
1013
  const internals = React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;
1062
1014
  const stack = internals?.ReactDebugCurrentFrame?.getStackAddendum?.()?.split('\n') || [];
@@ -1073,6 +1025,9 @@ function getParentComponentName() {
1073
1025
  * Serialize selector and styles to css rule string
1074
1026
  */
1075
1027
  function serialize(selector, styles) {
1028
+ if (selector.startsWith('@') && Array.isArray(styles)) {
1029
+ return `${selector} ${styles.join(', ')};`;
1030
+ }
1076
1031
  const lines = [];
1077
1032
  for (const key in styles) {
1078
1033
  const value = styles[key];
@@ -1092,6 +1047,11 @@ function stylesToRuleArray(styles, className, context) {
1092
1047
  try {
1093
1048
  const processedStyles = applyPlugins('processStyles', styles, className, context);
1094
1049
  const result = [];
1050
+ // Handle @layer rules first
1051
+ if (processedStyles['@layer']) {
1052
+ result.push(serialize('@layer', processedStyles['@layer']));
1053
+ delete processedStyles['@layer'];
1054
+ }
1095
1055
  for (const key in processedStyles) {
1096
1056
  const value = processedStyles[key];
1097
1057
  result.push(serialize(key, value));
@@ -1128,68 +1088,80 @@ function cleanup(ctx) {
1128
1088
  ctx.cleanupRequest = setTimeout(doCleanup, 100);
1129
1089
  }
1130
1090
  }
1131
- /**
1132
- * Accepts a Stylix CSS object and returns a unique className.
1133
- * The styles are registered with the Stylix context and will be applied to the document.
1134
- * If `global` is false, provided styles will be nested within the generated classname.
1135
- * Returns the className if enabled, or an empty string.
1136
- */
1137
- function useStyles(styles, options = { global: false }) {
1138
- const stylixCtx = useStylixContext();
1139
- const prevStylesJson = useRef('');
1140
- // Preprocess styles with plugins
1141
- if (styles && !isEmpty(styles))
1091
+ function createStyles(config) {
1092
+ const { stylixCtx, global } = config;
1093
+ let styles = config.styles;
1094
+ const priorKey = config.key || '';
1095
+ let stylesKey = '';
1096
+ if (styles && !isEmpty(styles)) {
1097
+ // Preprocess styles with plugins
1142
1098
  styles = applyPlugins('preprocessStyles', styles, null, stylixCtx);
1143
- let stylesJson = styles && !isEmpty(styles) ? JSON.stringify(styles) : '';
1144
- if (stylesJson && options.global)
1145
- stylesJson = `global:${stylesJson}`;
1146
- const changed = stylesJson !== prevStylesJson.current;
1147
- prevStylesJson.current = stylesJson;
1148
- options.debugLabel ||= stylixCtx.devMode ? getParentComponentName() : '';
1149
- if (stylesJson && !stylixCtx.rules[stylesJson]) {
1099
+ // Generate styles key
1100
+ stylesKey = styles ? (global ? 'global:' : '') + JSON.stringify(styles) : '';
1101
+ }
1102
+ if (stylesKey && !stylixCtx.rules[stylesKey]) {
1150
1103
  stylixCtx.styleCounter++;
1151
- const className = `stylix-${(stylixCtx.styleCounter).toString(36)}${options.debugLabel ? `-${options.debugLabel}` : ''}`;
1104
+ const debugLabel = config.debugLabel || (stylixCtx.devMode && getParentComponentName()) || '';
1105
+ const className = `stylix-${(stylixCtx.styleCounter).toString(36)}${debugLabel ? `-${debugLabel}` : ''}`;
1152
1106
  // If not global styles, wrap original styles with classname
1153
- if (!options.global)
1107
+ if (!global)
1154
1108
  styles = { [`.${className}`]: styles };
1155
- stylixCtx.rules[stylesJson] = {
1109
+ stylixCtx.rules[stylesKey] = {
1156
1110
  className,
1157
1111
  rules: stylesToRuleArray(styles, className, stylixCtx),
1158
1112
  refs: 0,
1159
1113
  };
1160
1114
  }
1161
- if (changed)
1115
+ const isChanged = stylesKey !== priorKey;
1116
+ const ruleSet = stylesKey ? stylixCtx.rules[stylesKey] : null;
1117
+ if (isChanged) {
1118
+ // Mark styles to be applied
1162
1119
  stylixCtx.requestApply = true;
1163
- // When json changes, add/remove ref count
1164
- const ruleSet = stylixCtx.rules[stylesJson];
1165
- if (stylesJson && changed && ruleSet) {
1166
- ruleSet.refs++;
1120
+ // When json changes, add/remove ref count
1121
+ const priorRuleSet = priorKey ? stylixCtx.rules[priorKey] : null;
1122
+ if (priorRuleSet)
1123
+ priorRuleSet.refs--;
1124
+ if (ruleSet)
1125
+ ruleSet.refs++;
1167
1126
  }
1127
+ return {
1128
+ className: ruleSet?.className || '',
1129
+ key: stylesKey,
1130
+ };
1131
+ }
1132
+ /**
1133
+ * Accepts a Stylix CSS object and returns a unique className.
1134
+ * The styles are registered with the Stylix context and will be applied to the document.
1135
+ * If `global` is false, provided styles will be nested within the generated classname.
1136
+ * Returns the className if enabled, or an empty string.
1137
+ */
1138
+ function useStyles(styles, options = { global: false }) {
1139
+ const stylixCtx = useStylixContext();
1140
+ const prevStylesKey = useRef('');
1141
+ const s = createStyles({
1142
+ stylixCtx,
1143
+ styles,
1144
+ global: options.global,
1145
+ debugLabel: options.debugLabel,
1146
+ key: prevStylesKey.current,
1147
+ });
1148
+ prevStylesKey.current = s.key;
1168
1149
  // Apply styles if requested.
1169
- // This runs on every render. We utilize useLayoutEffect so that it runs *after* all the other
1150
+ // This runs on every render. We utilize useInsertionEffect so that it runs *after* all the other
1170
1151
  // renders have completed. stylixCtx.requestApply guards against multiple runs. This reduces the number of calls
1171
1152
  // to applyRules(), but prevents styles potentially being added to the DOM after other components force the
1172
1153
  // browser to compute styles.
1173
- useIsoLayoutEffect(() => {
1154
+ // biome-ignore lint/correctness/useExhaustiveDependencies: stylixCtx is stable
1155
+ useInsertionEffect(() => {
1174
1156
  if (!stylixCtx.requestApply)
1175
1157
  return;
1176
1158
  stylixCtx.requestApply = false;
1177
1159
  applyRules(stylixCtx);
1178
- }, undefined, true, stylixCtx.ssr);
1179
- useIsoLayoutEffect(() => {
1180
- if (!stylesJson || !changed)
1181
- return;
1182
1160
  return () => {
1183
- const ruleSet = stylixCtx.rules[stylesJson];
1184
- if (!ruleSet)
1185
- return;
1186
- ruleSet.refs--;
1187
- if (ruleSet.refs <= 0)
1188
- stylixCtx.rules[stylesJson] = undefined;
1189
1161
  cleanup(stylixCtx);
1190
1162
  };
1191
- }, [stylesJson], false, stylixCtx.ssr);
1192
- return stylixCtx.rules[stylesJson]?.className || '';
1163
+ }, [s.key]);
1164
+ return s.className;
1193
1165
  }
1194
1166
  function useKeyframes(keyframes) {
1195
1167
  return useStyles({ '@keyframes $$class': keyframes }, { global: true });
@@ -1198,6 +1170,115 @@ function useGlobalStyles(styles, options = { disabled: false }) {
1198
1170
  return useStyles(styles, { ...options, global: true });
1199
1171
  }
1200
1172
 
1173
+ const detectSSR = () => !(typeof window !== 'undefined' && window.document?.head?.appendChild);
1174
+
1175
+ /**
1176
+ * Default style props mapping. This will be populated on the first call to createStylixContext.
1177
+ */
1178
+ let defaultStyleProps;
1179
+ function createStylixContext(userValues = {}) {
1180
+ if (!defaultStyleProps) {
1181
+ defaultStyleProps = {};
1182
+ for (const value of cssProps) {
1183
+ defaultStyleProps[simplifyStylePropName(value)] = value;
1184
+ }
1185
+ }
1186
+ const ctx = {
1187
+ id: userValues.id || '$default',
1188
+ devMode: !!userValues.devMode,
1189
+ styleProps: defaultStyleProps,
1190
+ media: userValues.media,
1191
+ styleElement: userValues.styleElement,
1192
+ plugins: defaultPlugins.flat(),
1193
+ styleCounter: 0,
1194
+ rules: {},
1195
+ ssr: userValues.ssr ?? detectSSR(),
1196
+ cleanupRequest: undefined,
1197
+ requestApply: false,
1198
+ classifyProps(props) {
1199
+ const [styles, other] = classifyProps(props, this.styleProps);
1200
+ return [styles, other];
1201
+ },
1202
+ styles(styles, config) {
1203
+ const s = createStyles({
1204
+ stylixCtx: ctx,
1205
+ styles,
1206
+ global: config?.global || false,
1207
+ });
1208
+ applyRules(ctx);
1209
+ return s.className;
1210
+ },
1211
+ };
1212
+ if (userValues.plugins?.length) {
1213
+ const flatPlugins = userValues.plugins.flat();
1214
+ for (const i in flatPlugins) {
1215
+ const plugin = flatPlugins[i];
1216
+ let pluginIndex = -1;
1217
+ if (plugin.before)
1218
+ pluginIndex = ctx.plugins.findIndex((v) => v.name === plugin.before);
1219
+ else if (plugin.after) {
1220
+ pluginIndex = ctx.plugins.findIndex((v) => v.name === plugin.before);
1221
+ if (pluginIndex !== -1)
1222
+ pluginIndex += 1;
1223
+ }
1224
+ else if (plugin.atIndex !== undefined)
1225
+ pluginIndex = plugin.atIndex;
1226
+ if (pluginIndex === -1)
1227
+ ctx.plugins.push(plugin);
1228
+ else
1229
+ ctx.plugins.splice(pluginIndex, 0, plugin);
1230
+ }
1231
+ }
1232
+ applyPlugins('initialize', null, null, ctx);
1233
+ return ctx;
1234
+ }
1235
+ /**
1236
+ * The React context object for Stylix.
1237
+ */
1238
+ const stylixContext = createContext(undefined);
1239
+ /**
1240
+ * Default Stylix context, used when no provider is present.
1241
+ */
1242
+ let defaultStylixContext;
1243
+ /**
1244
+ * React hook that gets the current Stylix context.
1245
+ */
1246
+ function useStylixContext() {
1247
+ const ctx = useContext(stylixContext);
1248
+ if (!ctx) {
1249
+ if (!defaultStylixContext)
1250
+ defaultStylixContext = createStylixContext();
1251
+ return defaultStylixContext;
1252
+ }
1253
+ return ctx;
1254
+ }
1255
+ /**
1256
+ * StylixProvider component. Provides a Stylix context to its descendent elements.
1257
+ * Can either accept an existing context via the `context` prop, or create a new context
1258
+ * using the other configuration props.
1259
+ */
1260
+ function StylixProvider(props) {
1261
+ const { context, id, devMode, plugins, media, styleElement, children, ssr } = props;
1262
+ const ctx = useRef(context);
1263
+ if (!ctx.current)
1264
+ ctx.current = createStylixContext({
1265
+ id,
1266
+ devMode,
1267
+ plugins,
1268
+ media,
1269
+ styleElement,
1270
+ ssr,
1271
+ });
1272
+ ctx.current.styleCollector = useContext(styleCollectorContext);
1273
+ // When the component is unmounted, remove the style element, if any
1274
+ useEffect(() => {
1275
+ return () => {
1276
+ ctx.current?.styleElement?.remove();
1277
+ };
1278
+ }, []);
1279
+ return jsx(stylixContext.Provider, { value: ctx.current, children: children });
1280
+ }
1281
+
1201
1282
  function _Stylix(props, ref) {
1202
1283
  const { $el, $render, $css, className: outerClassName, children, ...rest } = props;
1203
1284
  const ctx = useStylixContext();
@@ -1214,7 +1295,7 @@ function _Stylix(props, ref) {
1214
1295
  if (React.isValidElement($el)) {
1215
1296
  const $elProps = {
1216
1297
  ...$el.props,
1217
- ref,
1298
+ ref: ('ref' in $el && $el.ref) || ref,
1218
1299
  /**
1219
1300
  * `allProps` must override `$el.props` because the latter may contain default prop values provided by defaultProps.
1220
1301
  * The expectation is that for <$ $el={<SomeComponent />} someComponentProp="my value" />,