diff --git a/.eslintrc.js b/.eslintrc.js
index 7806144472207c..205c9b95d5021e 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -124,6 +124,7 @@ module.exports = {
'material-ui/docgen-ignore-before-comment': 'error',
'material-ui/rules-of-use-theme-variants': 'error',
'material-ui/no-empty-box': 'error',
+ 'material-ui/no-styled-box': 'error',
'material-ui/straight-quotes': 'error',
'react-hooks/exhaustive-deps': ['error', { additionalHooks: 'useEnhancedEffect' }],
diff --git a/docs/data/joy/main-features/color-inversion/ColorInversionAnyParentStyled.js b/docs/data/joy/main-features/color-inversion/ColorInversionAnyParentStyled.js
index ce8f5e342f33e6..77f397766173b6 100644
--- a/docs/data/joy/main-features/color-inversion/ColorInversionAnyParentStyled.js
+++ b/docs/data/joy/main-features/color-inversion/ColorInversionAnyParentStyled.js
@@ -6,7 +6,7 @@ import Box from '@mui/joy/Box';
import Button from '@mui/joy/Button';
import Typography from '@mui/joy/Typography';
-const StyledBox = styled(Box)(
+const StyledBox = styled('div')(
({ theme }) => ({
padding: 32,
display: 'grid',
diff --git a/docs/data/joy/main-features/color-inversion/ColorInversionAnyParentStyled.tsx b/docs/data/joy/main-features/color-inversion/ColorInversionAnyParentStyled.tsx
index 81db36a1540b5f..a010716f89b829 100644
--- a/docs/data/joy/main-features/color-inversion/ColorInversionAnyParentStyled.tsx
+++ b/docs/data/joy/main-features/color-inversion/ColorInversionAnyParentStyled.tsx
@@ -5,7 +5,7 @@ import Box from '@mui/joy/Box';
import Button from '@mui/joy/Button';
import Typography from '@mui/joy/Typography';
-const StyledBox = styled(Box)(
+const StyledBox = styled('div')(
({ theme }) => ({
padding: 32,
display: 'grid',
diff --git a/docs/data/material/components/drawers/SwipeableEdgeDrawer.js b/docs/data/material/components/drawers/SwipeableEdgeDrawer.js
index 6084e75f007ecd..2850ef11b510a0 100644
--- a/docs/data/material/components/drawers/SwipeableEdgeDrawer.js
+++ b/docs/data/material/components/drawers/SwipeableEdgeDrawer.js
@@ -18,11 +18,11 @@ const Root = styled('div')(({ theme }) => ({
theme.palette.mode === 'light' ? grey[100] : theme.palette.background.default,
}));
-const StyledBox = styled(Box)(({ theme }) => ({
+const StyledBox = styled('div')(({ theme }) => ({
backgroundColor: theme.palette.mode === 'light' ? '#fff' : grey[800],
}));
-const Puller = styled(Box)(({ theme }) => ({
+const Puller = styled('div')(({ theme }) => ({
width: 30,
height: 6,
backgroundColor: theme.palette.mode === 'light' ? grey[300] : grey[900],
diff --git a/docs/data/material/components/drawers/SwipeableEdgeDrawer.tsx b/docs/data/material/components/drawers/SwipeableEdgeDrawer.tsx
index 62cbbdbc33a79e..76fa98d56759e7 100644
--- a/docs/data/material/components/drawers/SwipeableEdgeDrawer.tsx
+++ b/docs/data/material/components/drawers/SwipeableEdgeDrawer.tsx
@@ -25,11 +25,11 @@ const Root = styled('div')(({ theme }) => ({
theme.palette.mode === 'light' ? grey[100] : theme.palette.background.default,
}));
-const StyledBox = styled(Box)(({ theme }) => ({
+const StyledBox = styled('div')(({ theme }) => ({
backgroundColor: theme.palette.mode === 'light' ? '#fff' : grey[800],
}));
-const Puller = styled(Box)(({ theme }) => ({
+const Puller = styled('div')(({ theme }) => ({
width: 30,
height: 6,
backgroundColor: theme.palette.mode === 'light' ? grey[300] : grey[900],
diff --git a/docs/data/material/components/material-icons/SearchIcons.js b/docs/data/material/components/material-icons/SearchIcons.js
index 294873ed691edf..30863dcf8a4d69 100644
--- a/docs/data/material/components/material-icons/SearchIcons.js
+++ b/docs/data/material/components/material-icons/SearchIcons.js
@@ -1,7 +1,6 @@
import * as React from 'react';
import { styled } from '@mui/material/styles';
import MuiPaper from '@mui/material/Paper';
-import Box from '@mui/material/Box';
import copy from 'clipboard-copy';
import InputBase from '@mui/material/InputBase';
import Typography from '@mui/material/Typography';
@@ -209,7 +208,7 @@ const Title = styled(Typography)(({ theme }) => ({
},
}));
-const CanvasComponent = styled(Box)(({ theme }) => ({
+const CanvasComponent = styled('div')(({ theme }) => ({
fontSize: 210,
marginTop: theme.spacing(2),
color: theme.palette.text.primary,
@@ -226,7 +225,7 @@ const FontSizeComponent = styled('span')(({ theme }) => ({
margin: theme.spacing(2),
}));
-const ContextComponent = styled(Box, {
+const ContextComponent = styled('div', {
shouldForwardProp: (prop) => prop !== 'contextColor',
})(({ theme, contextColor }) => ({
margin: theme.spacing(0.5),
diff --git a/docs/src/pages/premium-themes/onepirate/modules/form/FormFeedback.js b/docs/src/pages/premium-themes/onepirate/modules/form/FormFeedback.js
index b32b2b7113414b..88059b2389868f 100644
--- a/docs/src/pages/premium-themes/onepirate/modules/form/FormFeedback.js
+++ b/docs/src/pages/premium-themes/onepirate/modules/form/FormFeedback.js
@@ -1,10 +1,10 @@
import * as React from 'react';
import PropTypes from 'prop-types';
import { experimentalStyled as styled } from '@mui/material/styles';
-import Box from '@mui/material/Box';
+
import Typography from '../components/Typography';
-const BoxStyled = styled(Box, {
+const Root = styled('div', {
shouldForwardProp: (prop) => prop !== 'error' && prop !== 'success',
})(({ theme, error, success }) => ({
padding: theme.spacing(2),
@@ -22,9 +22,9 @@ function FormFeedback(props) {
const { className, children, error, success, ...others } = props;
return (
-
+
{children}
-
+
);
}
diff --git a/docs/src/pages/premium-themes/onepirate/modules/form/FormFeedback.tsx b/docs/src/pages/premium-themes/onepirate/modules/form/FormFeedback.tsx
index f876145ed91cc9..98946408e05ba7 100644
--- a/docs/src/pages/premium-themes/onepirate/modules/form/FormFeedback.tsx
+++ b/docs/src/pages/premium-themes/onepirate/modules/form/FormFeedback.tsx
@@ -1,14 +1,15 @@
import * as React from 'react';
-import { experimentalStyled as styled } from '@mui/material/styles';
-import Box, { BoxProps as MuiBoxProps } from '@mui/material/Box';
+import { experimentalStyled as styled, Theme } from '@mui/material/styles';
+import { SxProps } from '@mui/system';
import Typography from '../components/Typography';
-interface FormFeedbackProps extends MuiBoxProps {
+interface FormFeedbackProps extends React.HTMLAttributes {
error?: boolean;
success?: boolean;
+ sx?: SxProps;
}
-const BoxStyled = styled(Box, {
+const Root = styled('div', {
shouldForwardProp: (prop) => prop !== 'error' && prop !== 'success',
})(({ theme, error, success }) => ({
padding: theme.spacing(2),
@@ -22,16 +23,12 @@ const BoxStyled = styled(Box, {
}),
}));
-function FormFeedback(
- props: React.HTMLAttributes & FormFeedbackProps,
-) {
+export default function FormFeedback(props: FormFeedbackProps) {
const { className, children, error, success, ...others } = props;
return (
-
+
{children}
-
+
);
}
-
-export default FormFeedback;
diff --git a/docs/src/pages/premium-themes/onepirate/modules/views/ProductHeroLayout.js b/docs/src/pages/premium-themes/onepirate/modules/views/ProductHeroLayout.js
index ef69d6f33fe602..52972ba02c2fc7 100644
--- a/docs/src/pages/premium-themes/onepirate/modules/views/ProductHeroLayout.js
+++ b/docs/src/pages/premium-themes/onepirate/modules/views/ProductHeroLayout.js
@@ -17,7 +17,7 @@ const ProductHeroLayoutRoot = styled('section')(({ theme }) => ({
},
}));
-const Background = styled(Box)({
+const Background = styled('div')({
position: 'absolute',
left: 0,
right: 0,
diff --git a/docs/src/pages/premium-themes/onepirate/modules/views/ProductHeroLayout.tsx b/docs/src/pages/premium-themes/onepirate/modules/views/ProductHeroLayout.tsx
index 4cc511f8e5c6e7..ec6b4eae21618c 100644
--- a/docs/src/pages/premium-themes/onepirate/modules/views/ProductHeroLayout.tsx
+++ b/docs/src/pages/premium-themes/onepirate/modules/views/ProductHeroLayout.tsx
@@ -16,7 +16,7 @@ const ProductHeroLayoutRoot = styled('section')(({ theme }) => ({
},
}));
-const Background = styled(Box)({
+const Background = styled('div')({
position: 'absolute',
left: 0,
right: 0,
diff --git a/packages/eslint-plugin-material-ui/src/index.js b/packages/eslint-plugin-material-ui/src/index.js
index ac8619f6224f57..ae6e389b24cc00 100644
--- a/packages/eslint-plugin-material-ui/src/index.js
+++ b/packages/eslint-plugin-material-ui/src/index.js
@@ -6,5 +6,6 @@ module.exports.rules = {
'no-hardcoded-labels': require('./rules/no-hardcoded-labels'),
'rules-of-use-theme-variants': require('./rules/rules-of-use-theme-variants'),
'no-empty-box': require('./rules/no-empty-box'),
+ 'no-styled-box': require('./rules/no-styled-box'),
'straight-quotes': require('./rules/straight-quotes'),
};
diff --git a/packages/eslint-plugin-material-ui/src/rules/no-styled-box.js b/packages/eslint-plugin-material-ui/src/rules/no-styled-box.js
new file mode 100644
index 00000000000000..e17beba4ff4ac8
--- /dev/null
+++ b/packages/eslint-plugin-material-ui/src/rules/no-styled-box.js
@@ -0,0 +1,55 @@
+// Copy from https://github.com/eslint/eslint/blob/95075251fb3ce35aaf7eadbd1d0a737106c13ec6/lib/rules/utils/ast-utils.js#L299
+// Why is this not exported by ESLint?
+/**
+ * Retrieve `ChainExpression#expression` value if the given node a `ChainExpression` node. Otherwise, pass through it.
+ * @param {ASTNode} node The node to address.
+ * @returns {ASTNode} The `ChainExpression#expression` value if the node is a `ChainExpression` node. Otherwise, the node.
+ */
+function skipChainExpression(node) {
+ return node && node.type === 'ChainExpression' ? node.expression : node;
+}
+
+/**
+ * @type {import('eslint').Rule.RuleModule}
+ */
+const rule = {
+ meta: {
+ docs: {
+ description: 'Disallow use of styled(Box), we prefer the sx prop over system props.',
+ },
+ messages: {
+ noBox: "The use of styled(Box) is not allowed, use styled('div') instead.",
+ },
+ type: 'suggestion',
+ fixable: 'code',
+ schema: [],
+ },
+ create(context) {
+ return {
+ CallExpression(node) {
+ const callee = skipChainExpression(node.callee);
+ if (callee.type !== 'Identifier') {
+ return;
+ }
+ if (callee.name !== 'styled') {
+ return;
+ }
+ if (!node.arguments[0]) {
+ return;
+ }
+
+ if (node.arguments[0].type === 'Identifier' && node.arguments[0].name === 'Box') {
+ context.report({
+ node,
+ messageId: 'noBox',
+ fix: (fixer) => {
+ return fixer.replaceText(node.arguments[0], "'div'");
+ },
+ });
+ }
+ },
+ };
+ },
+};
+
+module.exports = rule;
diff --git a/packages/eslint-plugin-material-ui/src/rules/no-styled-box.test.js b/packages/eslint-plugin-material-ui/src/rules/no-styled-box.test.js
new file mode 100644
index 00000000000000..35cfe116efa930
--- /dev/null
+++ b/packages/eslint-plugin-material-ui/src/rules/no-styled-box.test.js
@@ -0,0 +1,72 @@
+const eslint = require('eslint');
+const rule = require('./no-styled-box');
+
+const ruleTester = new eslint.RuleTester({
+ parser: require.resolve('@typescript-eslint/parser'),
+ parserOptions: {
+ ecmaFeatures: { jsx: true },
+ },
+});
+
+ruleTester.run('no-styled-box', rule, {
+ valid: [
+ `
+import { styled } from '@mui/system';
+styled('div');
+`,
+ `
+import { styled } from '@mui/system';
+styled('div', {});
+`,
+ ],
+ invalid: [
+ {
+ code: `
+import { styled } from '@mui/system';
+import Box from '@mui/material/Box';
+
+const foo = styled(Box)({
+ color: 'red',
+});
+`,
+ errors: [
+ {
+ messageId: 'noBox',
+ type: 'CallExpression',
+ },
+ ],
+ output: `
+import { styled } from '@mui/system';
+import Box from '@mui/material/Box';
+
+const foo = styled('div')({
+ color: 'red',
+});
+`,
+ },
+ {
+ code: `
+import { styled } from '@mui/system';
+import Box from '@mui/material/Box';
+
+const foo = styled(Box, {})({
+ color: 'red',
+});
+`,
+ errors: [
+ {
+ messageId: 'noBox',
+ type: 'CallExpression',
+ },
+ ],
+ output: `
+import { styled } from '@mui/system';
+import Box from '@mui/material/Box';
+
+const foo = styled('div', {})({
+ color: 'red',
+});
+`,
+ },
+ ],
+});
diff --git a/packages/mui-joy/src/colorInversion/colorInversionUtils.spec.tsx b/packages/mui-joy/src/colorInversion/colorInversionUtils.spec.tsx
index cad5e83fc348c8..b4172f201e3f20 100644
--- a/packages/mui-joy/src/colorInversion/colorInversionUtils.spec.tsx
+++ b/packages/mui-joy/src/colorInversion/colorInversionUtils.spec.tsx
@@ -21,7 +21,7 @@ import Box from '@mui/joy/Box';
/**
* styled API type check
*/
-const StyledBox = styled(Box)(
+const StyledBox = styled('div')(
({ theme }) => ({
padding: 32,
display: 'grid',
diff --git a/packages/mui-joy/src/colorInversion/colorInversionUtils.test.tsx b/packages/mui-joy/src/colorInversion/colorInversionUtils.test.tsx
index 6bb5637509a8f7..b96b8d12b858a1 100644
--- a/packages/mui-joy/src/colorInversion/colorInversionUtils.test.tsx
+++ b/packages/mui-joy/src/colorInversion/colorInversionUtils.test.tsx
@@ -15,7 +15,7 @@ describe('colorInversionUtil', () => {
it('should not throw error using styled API', () => {
expect(() => {
- styled(Box)(applySoftInversion('primary'), applySolidInversion('primary'));
+ styled('div')(applySoftInversion('primary'), applySolidInversion('primary'));
}).not.to.throw();
});
});
diff --git a/packages/mui-system/src/Box/Box.spec.tsx b/packages/mui-system/src/Box/Box.spec.tsx
index 022b6e89df9b7f..2e2dc63d4debbe 100644
--- a/packages/mui-system/src/Box/Box.spec.tsx
+++ b/packages/mui-system/src/Box/Box.spec.tsx
@@ -1,5 +1,5 @@
import * as React from 'react';
-import { Box, Theme, styled } from '@mui/system';
+import { Box, styled } from '@mui/system';
interface TestProps {
test?: string;
@@ -105,6 +105,7 @@ function TestFillPropCallback() {
/>;
}
+// eslint-disable-next-line material-ui/no-styled-box
const StyledBox = styled(Box)`
color: white;
`;