Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[system] Add container queries utility #41674

Merged
merged 17 commits into from
Apr 17, 2024
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ Read more on the [Typography page](/system/typography/).

## Responsive values

All properties associated with the `sx` prop also support responsive values for specific breakpoints.
All properties associated with the `sx` prop also support responsive values for specific breakpoints and container queries.

Read more on the [Usage page—Responsive values](/system/getting-started/usage/#responsive-values).

Expand Down
88 changes: 88 additions & 0 deletions docs/data/system/getting-started/usage/ContainerQueries.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import * as React from 'react';
import Box from '@mui/material/Box';

export default function ContainerQueries() {
return (
<Box
sx={{
overflow: 'auto',
resize: 'horizontal',
width: 400,
maxWidth: '80%',
containerType: 'inline-size',
}}
>
<Box
sx={{
display: 'flex',
flexDirection: { xs: 'column', '@350': 'row' },
bgcolor: 'background.default',
border: '1px solid',
borderColor: 'divider',
borderRadius: 2,
overflow: 'clip',
}}
>
<Box
component="img"
sx={{
alignSelf: 'stretch',
aspectRatio: '16 / 9',
objectFit: 'cover',
width: '100%',
maxWidth: { '@350': '36%', '@500': 240 },
}}
alt="The house from the offer."
src="https://images.unsplash.com/photo-1512917774080-9991f1c4c750?auto=format&w=350&dpr=2"
/>
<Box
sx={{
p: { xs: 2, '@500': 3 },
display: 'flex',
flexDirection: 'column',
gap: 1,
}}
>
<div>
<Box
component="span"
sx={{ fontSize: '0.875rem', color: 'text.secondary' }}
>
123 Main St, Phoenix AZ
</Box>
<Box
sx={{
color: 'primary.main',
fontSize: '1.125rem',
fontWeight: 'bold',
}}
>
$280,000 — $310,000
</Box>
</div>
<Box
sx={{
width: 'fit-content',
py: 0.5,
px: 1,
backgroundColor: 'rgba(46, 125, 50, 0.1)',
borderRadius: 10,
display: 'flex',
alignItems: 'center',
gap: 0.5,
border: '1px solid',
borderColor: 'rgba(46, 125, 50, 0.1)',
fontSize: '0.7rem',
fontWeight: 'bold',
letterSpacing: '.05rem',
textTransform: 'uppercase',
color: 'success.main',
}}
>
Confidence score: 85%
</Box>
</Box>
</Box>
</Box>
);
}
88 changes: 88 additions & 0 deletions docs/data/system/getting-started/usage/ContainerQueries.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import * as React from 'react';
import Box from '@mui/material/Box';

export default function ContainerQueries() {
return (
<Box
sx={{
overflow: 'auto',
resize: 'horizontal',
width: 400,
maxWidth: '80%',
containerType: 'inline-size',
}}
>
<Box
sx={{
display: 'flex',
flexDirection: { xs: 'column', '@350': 'row' },
bgcolor: 'background.default',
border: '1px solid',
borderColor: 'divider',
borderRadius: 2,
overflow: 'clip',
}}
>
<Box
component="img"
sx={{
alignSelf: 'stretch',
aspectRatio: '16 / 9',
objectFit: 'cover',
width: '100%',
maxWidth: { '@350': '36%', '@500': 240 },
}}
alt="The house from the offer."
src="https://images.unsplash.com/photo-1512917774080-9991f1c4c750?auto=format&w=350&dpr=2"
/>
<Box
sx={{
p: { xs: 2, '@500': 3 },
display: 'flex',
flexDirection: 'column',
gap: 1,
}}
>
<div>
<Box
component="span"
sx={{ fontSize: '0.875rem', color: 'text.secondary' }}
>
123 Main St, Phoenix AZ
</Box>
<Box
sx={{
color: 'primary.main',
fontSize: '1.125rem',
fontWeight: 'bold',
}}
>
$280,000 — $310,000
</Box>
</div>
<Box
sx={{
width: 'fit-content',
py: 0.5,
px: 1,
backgroundColor: 'rgba(46, 125, 50, 0.1)',
borderRadius: 10,
display: 'flex',
alignItems: 'center',
gap: 0.5,
border: '1px solid',
borderColor: 'rgba(46, 125, 50, 0.1)',
fontSize: '0.7rem',
fontWeight: 'bold',
letterSpacing: '.05rem',
textTransform: 'uppercase',
color: 'success.main',
}}
>
Confidence score: 85%
</Box>
</Box>
</Box>
</Box>
);
}
13 changes: 13 additions & 0 deletions docs/data/system/getting-started/usage/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,19 @@ The following demo shows how to define a set of breakpoints using the object syn

{{"demo": "BreakpointsAsObject.js"}}

:::info
📣 Starting from v6, the object structure supports [container queries](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_containment/Container_queries) shorthand with `@`.

We recommend you to check the [browser support](https://caniuse.com/?search=container%20que) before using CSS container queries.
:::

The shorthand syntax is `@{breakpoint}/{container}`:

- **breakpoint**: a number for `px` unit or a breakpoint key (e.g. `sm`, `md`, `lg`, `xl` for default breakpoints) or a valid CSS value (e.g. `40em`).
- **container** (optional): the name of the [containment context](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_containment/Container_queries#naming_containment_contexts).

{{"demo": "ContainerQueries.js"}}

#### Breakpoints as an array

The second option is to define your breakpoints as an array, from smallest to largest.
Expand Down
3 changes: 2 additions & 1 deletion docs/public/static/error-codes.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,6 @@
"17": "MUI: Expected valid input target. Did you use a custom `slots.input` and forget to forward refs? See https://mui.com/r/input-component-ref-interface for more info.",
"18": "MUI: `vars` is a private field used for CSS variables support.\nPlease use another name.",
"19": "MUI: `useColorScheme` must be called under <CssVarsProvider />",
"20": "MUI: The `experimental_sx` has been moved to `theme.unstable_sx`.For more details, see https://github.com/mui/material-ui/pull/35150."
"20": "MUI: The `experimental_sx` has been moved to `theme.unstable_sx`.For more details, see https://github.com/mui/material-ui/pull/35150.",
"21": "MUI: The provided shorthand %s is invalid. The format should be `@<breakpoint | number>` or `@<breakpoint | number>/<container>`.\nFor example, `@sm` or `@600` or `@40rem/sidebar`."
}
2 changes: 1 addition & 1 deletion packages/mui-babel-macros/MuiError.macro.d.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
export default class MuiError {
constructor(message: string);
constructor(message: string, ...args: string[]);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Come across this since my test is the first .ts that uses MuiError with multiple parameters.

}
6 changes: 6 additions & 0 deletions packages/mui-joy/src/styles/defaultTheme.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ describe('defaultTheme', () => {
'colorSchemeSelector',
'defaultColorScheme',
'breakpoints',
'cq',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we (or should we) use containerQueries/containerQuery instead of cq?

Copy link
Member Author

@siriwatknp siriwatknp Apr 11, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, it can be explicit on this.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let me know if you decide to implement this change or not 😊

Copy link
Member Author

@siriwatknp siriwatknp Apr 12, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done! changed to containerQueries (with ies)

'components',
'colorSchemes',
'focus',
Expand Down Expand Up @@ -46,4 +47,9 @@ describe('defaultTheme', () => {
expect(defaultTheme.palette.mode).to.equal('light');
expect(defaultTheme.palette.colorScheme).to.equal('light');
});

it('has `cq` in the theme', () => {
expect(defaultTheme.cq('sidebar').up('sm')).to.equal('@container sidebar (min-width:600px)');
expect(defaultTheme.cq.up(300)).to.equal('@container (min-width:300px)');
});
});
1 change: 1 addition & 0 deletions packages/mui-joy/src/styles/extendTheme.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ describe('extendTheme', () => {
expect([
'attribute',
'breakpoints',
'cq',
'colorSchemeSelector',
'components',
'colorSchemes',
Expand Down
4 changes: 3 additions & 1 deletion packages/mui-joy/src/styles/extendTheme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
unstable_styleFunctionSx as styleFunctionSx,
SxConfig,
} from '@mui/system';
import cssContainerQueries from '@mui/system/cssContainerQueries';
import { unstable_applyStyles as applyStyles } from '@mui/system/createTheme';
import { createUnarySpacing } from '@mui/system/spacing';
import defaultSxConfig from './sxConfig';
Expand Down Expand Up @@ -559,7 +560,7 @@ export default function extendTheme(themeOptions?: CssVarsThemeOptions): Theme {
? deepmerge(defaultScales, scalesInput)
: defaultScales;

const theme = {
let theme = {
colorSchemes,
defaultColorScheme: 'light',
...mergedScales,
Expand Down Expand Up @@ -605,6 +606,7 @@ export default function extendTheme(themeOptions?: CssVarsThemeOptions): Theme {
getCssVar,
spacing: getSpacingVal(spacing),
} as unknown as Theme & { attribute: string; colorSchemeSelector: string }; // Need type casting due to module augmentation inside the repo
theme = cssContainerQueries(theme);

/**
Color channels generation
Expand Down
3 changes: 2 additions & 1 deletion packages/mui-joy/src/styles/types/theme.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { OverridableStringUnion } from '@mui/types';
import {
Breakpoints,
CssContainerQueries,
Spacing,
SxProps as SystemSxProps,
SystemProps as SystemSystemProps,
Expand Down Expand Up @@ -95,7 +96,7 @@ export type TextColor =

export type ThemeCssVar = OverridableStringUnion<NormalizeVars<ThemeVars>, ThemeCssVarOverrides>;

export interface Theme extends ThemeScales, RuntimeColorSystem {
export interface Theme extends ThemeScales, RuntimeColorSystem, CssContainerQueries {
colorSchemes: Record<DefaultColorScheme | ExtendedColorScheme, ColorSystem>;
defaultColorScheme: DefaultColorScheme | ExtendedColorScheme;
focus: Focus;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -408,6 +408,14 @@ describe('experimental_extendTheme', () => {
});
});

describe('container queries', () => {
it('should generate container queries', () => {
const theme = extendTheme();
expect(theme.cq('sidebar').up('sm')).to.equal('@container sidebar (min-width:600px)');
expect(theme.cq.up(300)).to.equal('@container (min-width:300px)');
});
});

it('shallow merges multiple arguments', () => {
const theme = extendTheme({ foo: 'I am foo' }, { bar: 'I am bar' });
expect(theme.foo).to.equal('I am foo');
Expand Down
26 changes: 25 additions & 1 deletion packages/mui-system/src/breakpoints/breakpoints.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import PropTypes from 'prop-types';
import deepmerge from '@mui/utils/deepmerge';
import merge from '../merge';
import { isCqShorthand, getContainerQuery } from '../cssContainerQueries';

// The breakpoint **start** at this value.
// For instance with the first breakpoint xs: [xs, sm[.
Expand All @@ -19,6 +20,20 @@ const defaultBreakpoints = {
up: (key) => `@media (min-width:${values[key]}px)`,
};

const defaultContainerQueries = {
cq: (containerName) => ({
up: (key) => {
let result = typeof key === 'number' ? key : values[key] || key;
if (typeof result === 'number') {
result = `${result}px`;
}
return containerName
? `@container ${containerName} (min-width:${result})`
: `@container (min-width:${result})`;
},
}),
};

export function handleBreakpoints(props, propValue, styleFromPropValue) {
const theme = props.theme || {};

Expand All @@ -33,8 +48,17 @@ export function handleBreakpoints(props, propValue, styleFromPropValue) {
if (typeof propValue === 'object') {
const themeBreakpoints = theme.breakpoints || defaultBreakpoints;
return Object.keys(propValue).reduce((acc, breakpoint) => {
if (isCqShorthand(themeBreakpoints.keys, breakpoint)) {
const containerKey = getContainerQuery(
theme.cq ? theme : defaultContainerQueries,
breakpoint,
);
if (containerKey) {
acc[containerKey] = styleFromPropValue(propValue[breakpoint], breakpoint);
}
}
// key is breakpoint
if (Object.keys(themeBreakpoints.values || values).indexOf(breakpoint) !== -1) {
else if (Object.keys(themeBreakpoints.values || values).indexOf(breakpoint) !== -1) {
const mediaKey = themeBreakpoints.up(breakpoint);
acc[mediaKey] = styleFromPropValue(propValue[breakpoint], breakpoint);
} else {
Expand Down
3 changes: 2 additions & 1 deletion packages/mui-system/src/createTheme/createTheme.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { Shape, ShapeOptions } from './shape';
import { Spacing, SpacingOptions } from './createSpacing';
import { SxConfig, SxProps } from '../styleFunctionSx';
import { ApplyStyles } from './applyStyles';
import { CssContainerQueries } from '../cssContainerQueries';

export { Breakpoint, BreakpointOverrides } from './createBreakpoints';

Expand All @@ -24,7 +25,7 @@ export interface ThemeOptions {
unstable_sxConfig?: SxConfig;
}

export interface Theme {
export interface Theme extends CssContainerQueries {
shape: Shape;
breakpoints: Breakpoints;
direction: Direction;
Expand Down
2 changes: 2 additions & 0 deletions packages/mui-system/src/createTheme/createTheme.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import deepmerge from '@mui/utils/deepmerge';
import createBreakpoints from './createBreakpoints';
import cssContainerQueries from '../cssContainerQueries';
import shape from './shape';
import createSpacing from './createSpacing';
import styleFunctionSx from '../styleFunctionSx/styleFunctionSx';
Expand Down Expand Up @@ -29,6 +30,7 @@ function createTheme(options = {}, ...args) {
},
other,
);
muiTheme = cssContainerQueries(muiTheme);

muiTheme.applyStyles = applyStyles;

Expand Down
Loading
Loading