diff --git a/docs/pages/api-docs/grid.json b/docs/pages/api-docs/grid.json
index 5277e065e7fb56..862d2a25e81dcb 100644
--- a/docs/pages/api-docs/grid.json
+++ b/docs/pages/api-docs/grid.json
@@ -61,6 +61,7 @@
},
"default": "0"
},
+ "sx": { "type": { "name": "object" } },
"wrap": {
"type": {
"name": "enum",
@@ -143,5 +144,5 @@
"filename": "/packages/material-ui/src/Grid/Grid.js",
"inheritance": null,
"demos": "
",
- "styledComponent": false
+ "styledComponent": true
}
diff --git a/docs/scripts/buildApi.ts b/docs/scripts/buildApi.ts
index f7fddf81289470..1bc2d8cb1abd13 100644
--- a/docs/scripts/buildApi.ts
+++ b/docs/scripts/buildApi.ts
@@ -709,8 +709,15 @@ async function updateStylesDefinition(context: {
if (members) {
styles.descriptions = styles.descriptions || {};
members.forEach((member) => {
- const className = ((member as babel.types.TSPropertySignature)
+ let className = ((member as babel.types.TSPropertySignature)
.key as babel.types.Identifier).name;
+
+ if (!className) {
+ // Necessary for classes defined as kebab case
+ className = ((member as babel.types.TSPropertySignature)
+ .key as babel.types.StringLiteral).value;
+ }
+
styles.classes.push(className);
if (member.leadingComments) {
styles.descriptions[className] = trimComment(member.leadingComments[0].value);
diff --git a/docs/translations/api-docs/grid/grid.json b/docs/translations/api-docs/grid/grid.json
index 0f11afda580efc..a4b938bb09d92b 100644
--- a/docs/translations/api-docs/grid/grid.json
+++ b/docs/translations/api-docs/grid/grid.json
@@ -14,6 +14,7 @@
"md": "Defines the number of grids the component is going to use. It's applied for the md
breakpoint and wider screens if not overridden.",
"sm": "Defines the number of grids the component is going to use. It's applied for the sm
breakpoint and wider screens if not overridden.",
"spacing": "Defines the space between the type item
component. It can only be used on a type container
component.",
+ "sx": "The system prop that allows defining system overrides as well as additional CSS styles. See the `sx` page for more details.",
"wrap": "Defines the flex-wrap
style property. It's applied for all screen sizes.",
"xl": "Defines the number of grids the component is going to use. It's applied for the xl
breakpoint and wider screens.",
"xs": "Defines the number of grids the component is going to use. It's applied for all the screen sizes with the lowest priority.",
diff --git a/packages/material-ui/src/Button/Button.js b/packages/material-ui/src/Button/Button.js
index d19e174bac541e..80a67fd2932857 100644
--- a/packages/material-ui/src/Button/Button.js
+++ b/packages/material-ui/src/Button/Button.js
@@ -34,7 +34,7 @@ const overridesResolver = (props, styles) => {
};
const useUtilityClasses = (styleProps) => {
- const { color, disableElevation, fullWidth, size, variant, classes = {} } = styleProps;
+ const { color, disableElevation, fullWidth, size, variant, classes } = styleProps;
const slots = {
root: [
diff --git a/packages/material-ui/src/ButtonBase/ButtonBase.js b/packages/material-ui/src/ButtonBase/ButtonBase.js
index d8836e07728288..f3a9de6c7cd670 100644
--- a/packages/material-ui/src/ButtonBase/ButtonBase.js
+++ b/packages/material-ui/src/ButtonBase/ButtonBase.js
@@ -21,7 +21,7 @@ const overridesResolver = (props, styles) => {
};
const useUtilityClasses = (styleProps) => {
- const { disabled, focusVisible, focusVisibleClassName, classes = {} } = styleProps;
+ const { disabled, focusVisible, focusVisibleClassName, classes } = styleProps;
const slots = {
root: ['root', disabled && 'disabled', focusVisible && 'focusVisible'],
diff --git a/packages/material-ui/src/Grid/Grid.d.ts b/packages/material-ui/src/Grid/Grid.d.ts
index e15b97a5eedf47..50f1dc597071aa 100644
--- a/packages/material-ui/src/Grid/Grid.d.ts
+++ b/packages/material-ui/src/Grid/Grid.d.ts
@@ -1,4 +1,6 @@
import * as React from 'react';
+import { SxProps } from '@material-ui/system';
+import { Theme } from '../styles';
import { OverridableComponent, OverrideProps } from '../OverridableComponent';
export type GridItemsAlignment = 'flex-start' | 'center' | 'flex-end' | 'stretch' | 'baseline';
@@ -170,6 +172,10 @@ export interface GridTypeMap {
* @default 0
*/
spacing?: GridSpacing;
+ /**
+ * The system prop that allows defining system overrides as well as additional CSS styles.
+ */
+ sx?: SxProps;
/**
* Defines the `flex-wrap` style property.
* It's applied for all screen sizes.
diff --git a/packages/material-ui/src/Grid/Grid.js b/packages/material-ui/src/Grid/Grid.js
index fa64775cfcb0da..ce5c22aa23a4b5 100644
--- a/packages/material-ui/src/Grid/Grid.js
+++ b/packages/material-ui/src/Grid/Grid.js
@@ -12,50 +12,45 @@
import * as React from 'react';
import PropTypes from 'prop-types';
import clsx from 'clsx';
-import withStyles from '../styles/withStyles';
+import { deepmerge } from '@material-ui/utils';
+import { unstable_composeClasses as composeClasses } from '@material-ui/unstyled';
import requirePropFactory from '../utils/requirePropFactory';
+import experimentalStyled from '../styles/experimentalStyled';
+import useThemeProps from '../styles/useThemeProps';
+import gridClasses, { getGridUtilityClass } from './gridClasses';
-const SPACINGS = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
-const GRID_SIZES = ['auto', true, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
+function generateGrid(globalStyles, theme, breakpoint, styleProps) {
+ const size = styleProps[breakpoint];
-function generateGrid(globalStyles, theme, breakpoint) {
- const styles = {};
+ if (!size) return;
- GRID_SIZES.forEach((size) => {
- const key = `grid-${breakpoint}-${size}`;
-
- if (size === true) {
- // For the auto layouting
- styles[key] = {
- flexBasis: 0,
- flexGrow: 1,
- maxWidth: '100%',
- };
-
- return;
- }
-
- if (size === 'auto') {
- styles[key] = {
- flexBasis: 'auto',
- flexGrow: 0,
- maxWidth: 'none',
- };
-
- return;
- }
+ let styles = {};
+ if (size === true) {
+ // For the auto layouting
+ styles = {
+ flexBasis: 0,
+ flexGrow: 1,
+ maxWidth: '100%',
+ };
+ } else if (size === 'auto') {
+ styles = {
+ flexBasis: 'auto',
+ flexGrow: 0,
+ maxWidth: 'none',
+ };
+ } else {
// Keep 7 significant numbers.
const width = `${Math.round((size / 12) * 10e7) / 10e5}%`;
// Close to the bootstrap implementation:
// https://github.com/twbs/bootstrap/blob/8fccaa2439e97ec72a4b7dc42ccc1f649790adb0/scss/mixins/_grid.scss#L41
- styles[key] = {
+ styles = {
flexBasis: width,
flexGrow: 0,
maxWidth: width,
};
- });
+ }
// No need for a media query for the first size.
if (breakpoint === 'xs') {
@@ -70,150 +65,177 @@ function getOffset(val, div = 1) {
return `${parse / div}${String(val).replace(String(parse), '') || 'px'}`;
}
-function generateGutter(theme, breakpoint) {
- const styles = {};
+function generateGutter({ theme, styleProps }) {
+ const { container, spacing } = styleProps;
+ let styles = {};
- SPACINGS.forEach((spacing) => {
+ if (container && spacing !== 0) {
const themeSpacing = theme.spacing(spacing);
- if (themeSpacing === '0px') {
- return;
+ if (themeSpacing !== '0px') {
+ styles = {
+ margin: `-${getOffset(themeSpacing, 2)}`,
+ width: `calc(100% + ${getOffset(themeSpacing)})`,
+ [`& > .${gridClasses.item}`]: {
+ padding: getOffset(themeSpacing, 2),
+ },
+ };
}
-
- styles[`spacing-${breakpoint}-${spacing}`] = {
- margin: `-${getOffset(themeSpacing, 2)}`,
- width: `calc(100% + ${getOffset(themeSpacing)})`,
- '& > $item': {
- padding: getOffset(themeSpacing, 2),
- },
- };
- });
+ }
return styles;
}
+const overridesResolver = (props, styles) => {
+ const {
+ alignContent,
+ alignItems,
+ container,
+ direction,
+ item,
+ justifyContent,
+ lg,
+ md,
+ sm,
+ spacing,
+ wrap,
+ xl,
+ xs,
+ zeroMinWidth,
+ } = props.styleProps;
+
+ return deepmerge(styles.root || {}, {
+ ...(container && styles.container),
+ ...(item && styles.item),
+ ...(zeroMinWidth && styles.zeroMinWidth),
+ ...(container && spacing !== 0 && styles[`spacing-xs-${String(spacing)}`]),
+ ...(direction !== 'row' && styles[`direction-xs-${String(direction)}`]),
+ ...(wrap !== 'wrap' && styles[`wrap-xs-${String(wrap)}`]),
+ ...(alignItems !== 'stretch' && styles[`align-items-xs-${String(alignItems)}`]),
+ ...(alignContent !== 'stretch' && styles[`align-content-xs-${String(alignContent)}`]),
+ ...(justifyContent !== 'flex-start' && styles[`justify-content-xs-${String(justifyContent)}`]),
+ ...(xs !== false && styles[`grid-xs-${String(xs)}`]),
+ ...(sm !== false && styles[`grid-sm-${String(sm)}`]),
+ ...(md !== false && styles[`grid-md-${String(md)}`]),
+ ...(lg !== false && styles[`grid-lg-${String(lg)}`]),
+ ...(xl !== false && styles[`grid-xl-${String(xl)}`]),
+ });
+};
+
// Default CSS values
// flex: '0 1 auto',
// flexDirection: 'row',
// alignItems: 'flex-start',
// flexWrap: 'nowrap',
// justifyContent: 'flex-start',
-export const styles = (theme) => ({
- /* Styles applied to the root element. */
- root: {},
- /* Styles applied to the root element if `container={true}`. */
- container: {
- boxSizing: 'border-box',
- display: 'flex',
- flexWrap: 'wrap',
- width: '100%',
- },
- /* Styles applied to the root element if `item={true}`. */
- item: {
+const GridRoot = experimentalStyled(
+ 'div',
+ {},
+ { name: 'MuiGrid', slot: 'Root', overridesResolver },
+)(
+ ({ styleProps }) => ({
boxSizing: 'border-box',
- margin: 0, // For instance, it's useful when used with a `figure` element.
- },
- /* Styles applied to the root element if `zeroMinWidth={true}`. */
- zeroMinWidth: {
- minWidth: 0,
- },
- /* Styles applied to the root element if `direction="column"`. */
- 'direction-xs-column': {
- flexDirection: 'column',
- '& > $item': {
- maxWidth: 'none',
- },
- },
- /* Styles applied to the root element if `direction="column-reverse"`. */
- 'direction-xs-column-reverse': {
- flexDirection: 'column-reverse',
- '& > $item': {
- maxWidth: 'none',
- },
- },
- /* Styles applied to the root element if `direction="row-reverse"`. */
- 'direction-xs-row-reverse': {
- flexDirection: 'row-reverse',
- },
- /* Styles applied to the root element if `wrap="nowrap"`. */
- 'wrap-xs-nowrap': {
- flexWrap: 'nowrap',
- },
- /* Styles applied to the root element if `wrap="reverse"`. */
- 'wrap-xs-wrap-reverse': {
- flexWrap: 'wrap-reverse',
- },
- /* Styles applied to the root element if `alignItems="center"`. */
- 'align-items-xs-center': {
- alignItems: 'center',
- },
- /* Styles applied to the root element if `alignItems="flex-start"`. */
- 'align-items-xs-flex-start': {
- alignItems: 'flex-start',
- },
- /* Styles applied to the root element if `alignItems="flex-end"`. */
- 'align-items-xs-flex-end': {
- alignItems: 'flex-end',
- },
- /* Styles applied to the root element if `alignItems="baseline"`. */
- 'align-items-xs-baseline': {
- alignItems: 'baseline',
- },
- /* Styles applied to the root element if `alignContent="center"`. */
- 'align-content-xs-center': {
- alignContent: 'center',
- },
- /* Styles applied to the root element if `alignContent="flex-start"`. */
- 'align-content-xs-flex-start': {
- alignContent: 'flex-start',
- },
- /* Styles applied to the root element if `alignContent="flex-end"`. */
- 'align-content-xs-flex-end': {
- alignContent: 'flex-end',
- },
- /* Styles applied to the root element if `alignContent="space-between"`. */
- 'align-content-xs-space-between': {
- alignContent: 'space-between',
- },
- /* Styles applied to the root element if `alignContent="space-around"`. */
- 'align-content-xs-space-around': {
- alignContent: 'space-around',
- },
- /* Styles applied to the root element if `justifyContent="center"`. */
- 'justify-content-xs-center': {
- justifyContent: 'center',
- },
- /* Styles applied to the root element if `justifyContent="flex-end"`. */
- 'justify-content-xs-flex-end': {
- justifyContent: 'flex-end',
- },
- /* Styles applied to the root element if `justifyContent="space-between"`. */
- 'justify-content-xs-space-between': {
- justifyContent: 'space-between',
- },
- /* Styles applied to the root element if `justifyContent="space-around"`. */
- 'justify-content-xs-space-around': {
- justifyContent: 'space-around',
- },
- /* Styles applied to the root element if `justifyContent="space-evenly"`. */
- 'justify-content-xs-space-evenly': {
- justifyContent: 'space-evenly',
- },
- ...generateGutter(theme, 'xs'),
- ...theme.breakpoints.keys.reduce((accumulator, key) => {
- // Use side effect over immutability for better performance.
- generateGrid(accumulator, theme, key);
- return accumulator;
- }, {}),
-});
+ ...(styleProps.container && {
+ display: 'flex',
+ flexWrap: 'wrap',
+ width: '100%',
+ }),
+ ...(styleProps.item && {
+ margin: 0, // For instance, it's useful when used with a `figure` element.
+ }),
+ ...(styleProps.zeroMinWidth && {
+ minWidth: 0,
+ }),
+ ...(styleProps.direction === 'column' && {
+ flexDirection: 'column',
+ [`& > .${gridClasses.item}`]: {
+ maxWidth: 'none',
+ },
+ }),
+ ...(styleProps.direction === 'column-reverse' && {
+ flexDirection: 'column-reverse',
+ [`& > .${gridClasses.item}`]: {
+ maxWidth: 'none',
+ },
+ }),
+ ...(styleProps.direction === 'row-reverse' && {
+ flexDirection: 'row-reverse',
+ }),
+ ...(styleProps.wrap === 'nowrap' && {
+ flexWrap: 'nowrap',
+ }),
+ ...(styleProps.wrap === 'reverse' && {
+ flexWrap: 'wrap-reverse',
+ }),
+ ...(styleProps.alignItems && {
+ alignItems: styleProps.alignItems,
+ }),
+ ...(styleProps.alignContent && {
+ alignContent: styleProps.alignContent,
+ }),
+ ...(styleProps.justifyContent && {
+ justifyContent: styleProps.justifyContent,
+ }),
+ }),
+ generateGutter,
+ ({ theme, styleProps }) =>
+ theme.breakpoints.keys.reduce((accumulator, key) => {
+ // Use side effect over immutability for better performance.
+ generateGrid(accumulator, theme, key, styleProps);
+ return accumulator;
+ }, {}),
+);
+
+const useUtilityClasses = (styleProps) => {
+ const {
+ alignContent,
+ alignItems,
+ classes,
+ container,
+ direction,
+ item,
+ justifyContent,
+ lg,
+ md,
+ sm,
+ spacing,
+ wrap,
+ xl,
+ xs,
+ zeroMinWidth,
+ } = styleProps;
+
+ const slots = {
+ root: [
+ 'root',
+ container && 'container',
+ item && 'item',
+ zeroMinWidth && 'zeroMinWidth',
+ container && spacing !== 0 && `spacing-xs-${String(spacing)}`,
+ direction !== 'row' && `direction-xs-${String(direction)}`,
+ wrap !== 'wrap' && `wrap-xs-${String(wrap)}`,
+ alignItems !== 'stretch' && `align-items-xs-${String(alignItems)}`,
+ alignContent !== 'stretch' && `align-content-xs-${String(alignContent)}`,
+ justifyContent !== 'flex-start' && `justify-content-xs-${String(justifyContent)}`,
+ xs !== false && `grid-xs-${String(xs)}`,
+ sm !== false && `grid-sm-${String(sm)}`,
+ md !== false && `grid-md-${String(md)}`,
+ lg !== false && `grid-lg-${String(lg)}`,
+ xl !== false && `grid-xl-${String(xl)}`,
+ ],
+ };
+
+ return composeClasses({ slots, classes, getUtilityClass: getGridUtilityClass });
+};
+
+const Grid = React.forwardRef(function Grid(inProps, ref) {
+ const props = useThemeProps({ props: inProps, name: 'MuiGrid' });
-const Grid = React.forwardRef(function Grid(props, ref) {
const {
alignContent = 'stretch',
alignItems = 'stretch',
- classes,
- className: classNameProp,
- component: Component = 'div',
+ className,
+ component = 'div',
container = false,
direction = 'row',
item = false,
@@ -229,28 +251,35 @@ const Grid = React.forwardRef(function Grid(props, ref) {
...other
} = props;
- const className = clsx(
- classes.root,
- {
- [classes.container]: container,
- [classes.item]: item,
- [classes.zeroMinWidth]: zeroMinWidth,
- [classes[`spacing-xs-${String(spacing)}`]]: container && spacing !== 0,
- [classes[`direction-xs-${String(direction)}`]]: direction !== 'row',
- [classes[`wrap-xs-${String(wrap)}`]]: wrap !== 'wrap',
- [classes[`align-items-xs-${String(alignItems)}`]]: alignItems !== 'stretch',
- [classes[`align-content-xs-${String(alignContent)}`]]: alignContent !== 'stretch',
- [classes[`justify-content-xs-${String(justifyContent)}`]]: justifyContent !== 'flex-start',
- [classes[`grid-xs-${String(xs)}`]]: xs !== false,
- [classes[`grid-sm-${String(sm)}`]]: sm !== false,
- [classes[`grid-md-${String(md)}`]]: md !== false,
- [classes[`grid-lg-${String(lg)}`]]: lg !== false,
- [classes[`grid-xl-${String(xl)}`]]: xl !== false,
- },
- classNameProp,
- );
+ const styleProps = {
+ ...props,
+ alignContent,
+ alignItems,
+ container,
+ direction,
+ item,
+ justifyContent,
+ lg,
+ md,
+ sm,
+ spacing,
+ wrap,
+ xl,
+ xs,
+ zeroMinWidth,
+ };
+
+ const classes = useUtilityClasses(styleProps);
- return ;
+ return (
+
+ );
});
Grid.propTypes = {
@@ -358,6 +387,10 @@ Grid.propTypes = {
* @default 0
*/
spacing: PropTypes.oneOf([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]),
+ /**
+ * The system prop that allows defining system overrides as well as additional CSS styles.
+ */
+ sx: PropTypes.object,
/**
* Defines the `flex-wrap` style property.
* It's applied for all screen sizes.
@@ -390,12 +423,12 @@ Grid.propTypes = {
zeroMinWidth: PropTypes.bool,
};
-const StyledGrid = withStyles(styles, { name: 'MuiGrid' })(Grid);
-
if (process.env.NODE_ENV !== 'production') {
const requireProp = requirePropFactory('Grid');
- StyledGrid.propTypes = {
- ...StyledGrid.propTypes,
+ // eslint-disable-next-line no-useless-concat
+ Grid['propTypes' + ''] = {
+ // eslint-disable-next-line react/forbid-foreign-prop-types
+ ...Grid.propTypes,
alignContent: requireProp('container'),
alignItems: requireProp('container'),
direction: requireProp('container'),
@@ -410,4 +443,4 @@ if (process.env.NODE_ENV !== 'production') {
};
}
-export default StyledGrid;
+export default Grid;
diff --git a/packages/material-ui/src/Grid/Grid.test.js b/packages/material-ui/src/Grid/Grid.test.js
index 4d64ec03a8826a..6592a9518ff9e8 100644
--- a/packages/material-ui/src/Grid/Grid.test.js
+++ b/packages/material-ui/src/Grid/Grid.test.js
@@ -1,24 +1,23 @@
import * as React from 'react';
import { expect } from 'chai';
-import { getClasses, createMount, describeConformance, createClientRender } from 'test/utils';
-import { createMuiTheme } from '@material-ui/core/styles';
-import Grid, { styles } from './Grid';
+import { createMount, describeConformanceV5, createClientRender, screen } from 'test/utils';
+import { createMuiTheme, ThemeProvider } from '@material-ui/core/styles';
+import Grid from './Grid';
+import classes from './gridClasses';
describe('', () => {
const mount = createMount();
const render = createClientRender();
- let classes;
- before(() => {
- classes = getClasses();
- });
-
- describeConformance(, () => ({
+ describeConformanceV5(, () => ({
classes,
inheritComponent: 'div',
mount,
refInstanceof: window.HTMLDivElement,
- testComponentPropWith: 'span',
+ muiName: 'MuiGrid',
+ testVariantProps: { container: true, spacing: 5 },
+ testStateOverrides: { prop: 'container', value: true, styleKey: 'container' },
+ skip: ['componentsProp'],
}));
describe('prop: container', () => {
@@ -90,21 +89,63 @@ describe('', () => {
});
describe('gutter', () => {
- it('should generate the right values', () => {
- const defaultTheme = createMuiTheme();
+ it('should generate the right values', function test() {
+ if (/jsdom/.test(window.navigator.userAgent)) this.skip();
+
+ const parentWidth = 500;
+ const remValue = 16;
const remTheme = createMuiTheme({
spacing: (factor) => `${0.25 * factor}rem`,
});
- expect(styles(remTheme)['spacing-xs-2']).to.deep.equal({
- margin: '-0.25rem',
- width: 'calc(100% + 0.5rem)',
- '& > $item': { padding: '0.25rem' },
+ const { rerender } = render(
+
+
+
+
+
+
+
+
,
+ );
+
+ expect(screen.getByTestId('grid')).toHaveComputedStyle({
+ marginTop: `${-1 * remValue * 0.25}px`, // '-0.25rem'
+ marginBottom: `${-1 * remValue * 0.25}px`, // '-0.25rem'
+ marginLeft: `${-1 * remValue * 0.25}px`, // '-0.25rem'
+ marginRight: `${-1 * remValue * 0.25}px`, // '-0.25rem'
+ width: `${parentWidth + remValue * 0.5}px`, // 'calc(100% + 0.5rem)'
+ });
+
+ expect(screen.getByTestId('first-custom-theme')).toHaveComputedStyle({
+ paddingTop: `${0.25 * remValue}px`, // 0.25rem
+ paddingBottom: `${0.25 * remValue}px`, // 0.25rem
+ paddingLeft: `${0.25 * remValue}px`, // 0.25rem
+ paddingRight: `${0.25 * remValue}px`, // 0.25rem
+ });
+
+ rerender(
+
+
+
+
+
+
,
+ );
+
+ expect(screen.getByTestId('grid')).toHaveComputedStyle({
+ marginTop: '-8px',
+ marginBottom: '-8px',
+ marginLeft: '-8px',
+ marginRight: '-8px',
+ width: `${parentWidth + 16}px`, // 'calc(100% + 16px)'
});
- expect(styles(defaultTheme)['spacing-xs-2']).to.deep.equal({
- margin: '-8px',
- width: 'calc(100% + 16px)',
- '& > $item': { padding: '8px' },
+
+ expect(screen.getByTestId('first-default-theme')).toHaveComputedStyle({
+ paddingTop: '8px',
+ paddingBottom: '8px',
+ paddingLeft: '8px',
+ paddingRight: '8px',
});
});
});
diff --git a/packages/material-ui/src/Grid/gridClasses.d.ts b/packages/material-ui/src/Grid/gridClasses.d.ts
new file mode 100644
index 00000000000000..9492249389c70f
--- /dev/null
+++ b/packages/material-ui/src/Grid/gridClasses.d.ts
@@ -0,0 +1,131 @@
+export interface GridClasses {
+ root: string;
+ container: string;
+ item: string;
+ zeroMinWidth: string;
+
+ 'spacing-xs-auto': string;
+ 'spacing-xs-true': string;
+ 'spacing-xs-1': string;
+ 'spacing-xs-2': string;
+ 'spacing-xs-3': string;
+ 'spacing-xs-4': string;
+ 'spacing-xs-5': string;
+ 'spacing-xs-6': string;
+ 'spacing-xs-7': string;
+ 'spacing-xs-8': string;
+ 'spacing-xs-9': string;
+ 'spacing-xs-10': string;
+ 'spacing-xs-11': string;
+ 'spacing-xs-12': string;
+
+ 'direction-xs-column-reverse': string;
+ 'direction-xs-column': string;
+ 'direction-xs-row-reverse': string;
+ 'direction-xs-row': string;
+
+ 'align-content-xs-center': string;
+ 'align-content-xs-flex-end': string;
+ 'align-content-xs-flex-start': string;
+ 'align-content-xs-space-around': string;
+ 'align-content-xs-space-between': string;
+ 'align-content-xs-stretch': string;
+
+ 'align-items-xs-baseline': string;
+ 'align-items-xs-center': string;
+ 'align-items-xs-flex-end': string;
+ 'align-items-xs-flex-start': string;
+ 'align-items-xs-stretch': string;
+
+ 'wrap-xs-nowrap': string;
+ 'wrap-xs-wrap-reverse': string;
+ 'wrap-xs-wrap': string;
+
+ 'justify-content-xs-center': string;
+ 'justify-content-xs-flex-end': string;
+ 'justify-content-xs-flex-start': string;
+ 'justify-content-xs-space-around': string;
+ 'justify-content-xs-space-between': string;
+ 'justify-content-xs-space-evenly': string;
+
+ 'grid-xs-auto': string;
+ 'grid-xs-true': string;
+ 'grid-xs-1': string;
+ 'grid-xs-2': string;
+ 'grid-xs-3': string;
+ 'grid-xs-4': string;
+ 'grid-xs-5': string;
+ 'grid-xs-6': string;
+ 'grid-xs-7': string;
+ 'grid-xs-8': string;
+ 'grid-xs-9': string;
+ 'grid-xs-10': string;
+ 'grid-xs-11': string;
+ 'grid-xs-12': string;
+
+ 'grid-sm-auto': string;
+ 'grid-sm-true': string;
+ 'grid-sm-1': string;
+ 'grid-sm-2': string;
+ 'grid-sm-3': string;
+ 'grid-sm-4': string;
+ 'grid-sm-5': string;
+ 'grid-sm-6': string;
+ 'grid-sm-7': string;
+ 'grid-sm-8': string;
+ 'grid-sm-9': string;
+ 'grid-sm-10': string;
+ 'grid-sm-11': string;
+ 'grid-sm-12': string;
+
+ 'grid-md-auto': string;
+ 'grid-md-true': string;
+ 'grid-md-1': string;
+ 'grid-md-2': string;
+ 'grid-md-3': string;
+ 'grid-md-4': string;
+ 'grid-md-5': string;
+ 'grid-md-6': string;
+ 'grid-md-7': string;
+ 'grid-md-8': string;
+ 'grid-md-9': string;
+ 'grid-md-10': string;
+ 'grid-md-11': string;
+ 'grid-md-12': string;
+
+ 'grid-lg-auto': string;
+ 'grid-lg-true': string;
+ 'grid-lg-1': string;
+ 'grid-lg-2': string;
+ 'grid-lg-3': string;
+ 'grid-lg-4': string;
+ 'grid-lg-5': string;
+ 'grid-lg-6': string;
+ 'grid-lg-7': string;
+ 'grid-lg-8': string;
+ 'grid-lg-9': string;
+ 'grid-lg-10': string;
+ 'grid-lg-11': string;
+ 'grid-lg-12': string;
+
+ 'grid-xl-auto': string;
+ 'grid-xl-true': string;
+ 'grid-xl-1': string;
+ 'grid-xl-2': string;
+ 'grid-xl-3': string;
+ 'grid-xl-4': string;
+ 'grid-xl-5': string;
+ 'grid-xl-6': string;
+ 'grid-xl-7': string;
+ 'grid-xl-8': string;
+ 'grid-xl-9': string;
+ 'grid-xl-10': string;
+ 'grid-xl-11': string;
+ 'grid-xl-12': string;
+}
+
+declare const gridClasses: GridClasses;
+
+export function getGridUtilityClass(slot: string): string;
+
+export default gridClasses;
diff --git a/packages/material-ui/src/Grid/gridClasses.js b/packages/material-ui/src/Grid/gridClasses.js
new file mode 100644
index 00000000000000..9a3225c9c52fa0
--- /dev/null
+++ b/packages/material-ui/src/Grid/gridClasses.js
@@ -0,0 +1,56 @@
+import { generateUtilityClass, generateUtilityClasses } from '@material-ui/unstyled';
+
+export function getGridUtilityClass(slot) {
+ return generateUtilityClass('MuiGrid', slot);
+}
+
+const SPACINGS = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
+const DIRECTIONS = ['column-reverse', 'column', 'row-reverse', 'row'];
+const ALIGN_CONTENTS = [
+ 'center',
+ 'flex-end',
+ 'flex-start',
+ 'space-around',
+ 'space-between',
+ 'stretch',
+];
+const ALIGN_ITEMS = ['baseline', 'center', 'flex-end', 'flex-start', 'stretch'];
+const JUSTIFY_CONTENTS = [
+ 'center',
+ 'flex-end',
+ 'flex-start',
+ 'space-around',
+ 'space-between',
+ 'space-evenly',
+];
+const WRAPS = ['nowrap', 'wrap-reverse', 'wrap'];
+const GRID_SIZES = ['auto', true, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
+
+const gridClasses = generateUtilityClasses('MuiGrid', [
+ 'root',
+ 'container',
+ 'item',
+ 'zeroMinWidth',
+
+ // spacings
+ ...SPACINGS.map((spacing) => `spacing-xs-${spacing}`),
+ // direction values
+ ...DIRECTIONS.map((direction) => `direction-xs-${direction}`),
+ // align content values
+ ...ALIGN_CONTENTS.map((alignContent) => `align-content-xs-${alignContent}`),
+ // align items values
+ ...ALIGN_ITEMS.map((alignItems) => `align-items-xs-${alignItems}`),
+ // wrap values
+ ...WRAPS.map((wrap) => `wrap-xs-${wrap}`),
+ // justify content values
+ ...JUSTIFY_CONTENTS.map((justifyContent) => `justify-content-xs-${justifyContent}`),
+
+ // grid sizes for all breakpoints
+ ...GRID_SIZES.map((size) => `grid-xs-${String(size)}`),
+ ...GRID_SIZES.map((size) => `grid-sm-${String(size)}`),
+ ...GRID_SIZES.map((size) => `grid-md-${String(size)}`),
+ ...GRID_SIZES.map((size) => `grid-lg-${String(size)}`),
+ ...GRID_SIZES.map((size) => `grid-xl-${String(size)}`),
+]);
+
+export default gridClasses;
diff --git a/packages/material-ui/src/Grid/index.d.ts b/packages/material-ui/src/Grid/index.d.ts
index 243da15e14de82..de7826137c176c 100644
--- a/packages/material-ui/src/Grid/index.d.ts
+++ b/packages/material-ui/src/Grid/index.d.ts
@@ -1,2 +1,5 @@
export { default } from './Grid';
export * from './Grid';
+
+export { default as gridClasses } from './gridClasses';
+export * from './gridClasses';
diff --git a/packages/material-ui/src/Grid/index.js b/packages/material-ui/src/Grid/index.js
index 3d919c9acb4394..c952f0ddd87e8e 100644
--- a/packages/material-ui/src/Grid/index.js
+++ b/packages/material-ui/src/Grid/index.js
@@ -1 +1,3 @@
export { default } from './Grid';
+export { default as gridClasses } from './gridClasses';
+export * from './gridClasses';