@mui/material
Version:
Material UI is an open-source React component library that implements Google's Material Design. It's comprehensive and can be used in production out of the box.
259 lines (231 loc) • 7.55 kB
Markdown
# Container queries
Material UI provides a utility function for creating CSS container queries based on theme breakpoints.
## Usage
To create [CSS container queries](https://developer.mozilla.org/en-US/docs/Web/CSS/Guides/Containment/Container_queries), use `theme.containerQueries` with any method available in the [`theme.breakpoints`](/material-ui/customization/breakpoints/#api).
The value can be unitless (in which case it'll be rendered in pixels), a string, or a breakpoint key. For example:
```js
theme.containerQueries.up('sm'); // => '@container (min-width: 600px)'
```
```tsx
import { styled } from '@mui/material/styles';
import Box from '@mui/material/Box';
import Card from '@mui/material/Card';
import CardContent from '@mui/material/CardContent';
import Chip from '@mui/material/Chip';
import Typography from '@mui/material/Typography';
import ResizableDemo from './ResizableDemo';
const DynamicCard = styled(Card)(({ theme }) => ({
display: 'flex',
flexDirection: 'column',
[theme.containerQueries.up(350)]: {
flexDirection: 'row',
},
}));
const Image = styled('img')(({ theme }) => ({
alignSelf: 'stretch',
aspectRatio: '16 / 9',
objectFit: 'cover',
width: '100%',
maxHeight: 160,
transition: '0.4s',
[theme.containerQueries.up(350)]: {
maxWidth: '36%',
maxHeight: 'initial',
},
[theme.containerQueries.up(500)]: {
maxWidth: 240,
},
}));
const Content = styled(CardContent)(({ theme }) => ({
display: 'flex',
flexDirection: 'column',
gap: theme.spacing(1),
padding: theme.spacing(2),
flex: 'auto',
transition: 'padding 0.4s',
[theme.containerQueries.up(500)]: {
padding: theme.spacing(3),
},
}));
export default function BasicContainerQueries() {
return (
<ResizableDemo>
<Box
sx={{
overflow: 'auto',
resize: 'horizontal',
width: 400,
maxWidth: 'min(80vw, 600px)',
containerType: 'inline-size', // required for container queries
}}
>
<DynamicCard variant="outlined">
<Image
alt="The house from the offer."
src="https://images.unsplash.com/photo-1512917774080-9991f1c4c750?auto=format&w=350&dpr=2"
/>
<Content>
<div>
<Typography
component="div"
sx={{ color: 'text.secondary', fontSize: '0.875rem' }}
>
123 Main St, Phoenix AZ
</Typography>
<Typography
component="div"
sx={{
color: 'primary.main',
fontSize: '1.125rem',
fontWeight: 'bold',
}}
>
$280,000 — $310,000
</Typography>
</div>
<Chip
size="small"
label="Confidence score: 85%"
sx={{ p: 0, width: 'fit-content' }}
/>
</Content>
</DynamicCard>
</Box>
</ResizableDemo>
);
}
```
:::info
One of the ancestors must have the CSS container type specified.
:::
### Named containment contexts
To refer to a [containment context](https://developer.mozilla.org/en-US/docs/Web/CSS/Guides/Containment/Container_queries#naming_containment_contexts), call the `containerQueries` method with the name of the container for access to all breakpoint methods:
```js
theme.containerQueries('sidebar').up('500px'); // => '@container sidebar (min-width: 500px)'
```
## Shorthand syntax
When adding styles using the `sx` prop, use the `@<size>` or `@<size>/<name>` notation to apply container queries without referring to the theme.
- `<size>`: a width or a breakpoint key.
- `<name>` (optional): a named containment context.
```tsx
import Box from '@mui/material/Box';
import Card from '@mui/material/Card';
import CardContent from '@mui/material/CardContent';
import Chip from '@mui/material/Chip';
import Typography from '@mui/material/Typography';
import ResizableDemo from './ResizableDemo';
export default function SxPropContainerQueries() {
return (
<ResizableDemo>
<Box
sx={{
overflow: 'auto',
resize: 'horizontal',
width: 400,
maxWidth: 'min(80vw, 600px)',
containerType: 'inline-size', // required for container queries
}}
>
<Card
variant="outlined"
sx={{
display: 'flex',
flexDirection: {
'@': 'column',
'@350': 'row',
},
}}
>
<Box
component="img"
alt="The house from the offer."
src="https://images.unsplash.com/photo-1512917774080-9991f1c4c750?auto=format&w=350&dpr=2"
sx={{
alignSelf: 'stretch',
aspectRatio: '16 / 9',
objectFit: 'cover',
width: '100%',
maxHeight: {
'@': 160,
'@350': 'initial',
},
maxWidth: {
'@350': '36%',
'@500': 240,
},
transition: '0.4s',
}}
/>
<CardContent
sx={{
display: 'flex',
flexDirection: 'column',
gap: 1,
padding: {
'@': 2,
'@500': 3,
},
flex: 'auto',
transition: 'padding 0.4s',
}}
>
<div>
<Typography
component="div"
sx={{ color: 'text.secondary', fontSize: '0.875rem' }}
>
123 Main St, Phoenix AZ
</Typography>
<Typography
component="div"
sx={{
color: 'primary.main',
fontSize: '1.125rem',
fontWeight: 'bold',
}}
>
$280,000 — $310,000
</Typography>
</div>
<Chip
size="small"
label="Confidence score: 85%"
sx={{ p: 0, width: 'fit-content' }}
/>
</CardContent>
</Card>
</Box>
</ResizableDemo>
);
}
```
### Caveats
- The `@` prefix with a unitless value renders as `px`, so `@500` is equivalent to `500px`—but `@500px` is incorrect syntax and won't render correctly.
- `@` with no number renders as `0px`.
- Container queries must share the same units (the sizes can be defined in any order), as shown below:
```js
// ✅ These container queries will be sorted correctly.
padding: {
'@40em': 4,
'@20em': 2,
'@': 0,
}
// ❌ These container queries won't be sorted correctly
// because 40em is typically greater than 50px
// and the units don't match.
padding: {
'@40em': 4,
'@50': 2,
'@': 0,
}
```
## API
CSS container queries support all the methods available in [the breakpoints API](/material-ui/customization/breakpoints/#api).
```js
// For default breakpoints
theme.containerQueries.up('sm'); // => '@container (min-width: 600px)'
theme.containerQueries.down('md'); // => '@container (max-width: 900px)'
theme.containerQueries.only('md'); // => '@container (min-width: 600px) and (max-width: 900px)'
theme.containerQueries.between('sm', 'lg'); // => '@container (min-width: 600px) and (max-width: 1200px)'
theme.containerQueries.not('sm'); // => '@container (max-width: 600px)'
```