mui / material-ui

Material UI: Comprehensive React component library that implements Google's Material Design. Free forever.
https://mui.com/material-ui/
MIT License
93.9k stars 32.26k forks source link

[Grid] Container responsive queries #25189

Open Mistes974 opened 3 years ago

Mistes974 commented 3 years ago

Summary 💡

Hi, I started a project with react-splitter-layout and material ui library. I would like to find a way to create responsive components, with material ui Grid or Box component

I encounter a problem with responsive, I would like my left panel to be responsive (use of xs / md / lg) with Grid component based on the size of the container (not window size), as you can see in the example below , this is not the case. It's use the viewport size. (I know it's normal because of media queries).

Examples 🌈

Here the code sample : https://codesandbox.io/s/material-demo-i04rr?file=/demo.js (recommended to open the rendering in a new tab to see the problem)

import React from "react";
import { makeStyles } from "@material-ui/core/styles";
import Paper from "@material-ui/core/Paper";
import Grid from "@material-ui/core/Grid";
import SplitterLayout from "react-splitter-layout";

const useStyles = makeStyles(theme => ({
  root: {
    flexGrow: 1
  },
  paper: {
    padding: theme.spacing(2),
    textAlign: "center",
    color: theme.palette.text.secondary
  }
}));

export default function CenteredGrid() {
  const classes = useStyles();

  return (
    <SplitterLayout>
      <div className={classes.root}>
        <Grid container spacing={3}>
          <Grid item xs={4} md={6} lg={8}>
            <Paper className={classes.paper}>xs=3</Paper>
          </Grid>
          <Grid item xs={4} md={4} lg={2}>
            <Paper className={classes.paper}>xs=3</Paper>
          </Grid>
          <Grid item xs={4} md={2} lg={2}>
            <Paper className={classes.paper}>xs=3</Paper>
          </Grid>
        </Grid>
      </div>
      <div>Panel 2</div>
    </SplitterLayout>
  );
}

Motivation 🔦

In my project the user can open/close the right panel like a sidebar/drawer with a button located on the topbar. So, everything on the left should be responsive.(When sidebar is closed the left side == screen size) Just like on the codesandbox UI, they have a code part and a rendered part, the rendering is displayed inside an iframe and when you dragging in the middle the content adapts to different devices. I would like something similar but without using an iframe. Actually, I'm using react-container-query, but it doesn't meet all of my needs.

zehawki commented 3 years ago

Hah, I came here to report this exact issue and see its here already. Its a bit strange that the grid is not responsive to its container, and instead computes based on viewport. This are many cases where one wants to render a component (which is using grid internally) within a smaller container / box etc etc. In such cases, not having the grid resize according to its own container leads to terrible UI.

oliviertassinari commented 3 years ago

Interesting issue. There are, more or less two solutions track.

  1. JavaScript. In this track, It's about: a. Having an API to measure the size of the container: #22519. b. Having an API to turn size into a breakpoint value. c. Having an API to resolve a set of responsive values, e.g, { xs: 1, md: 2 } to the current one based on the breakpoint. It's related to #23885. I think that it's worth pushing further
  2. CSS. a. The css grid minmax approach: https://blog.logrocket.com/flexible-layouts-without-media-queries/. For instance:

    <Box
      display="grid"
      gridTemplateColumns="repeat(auto-fill, minmax(min(10rem, 100%), 1fr))"
      gap={2}
    >
      <Item>item</Item>
      <Item>item</Item>
      <Item>item</Item>
      <Item>item</Item>
    </Box>

    https://codesandbox.io/s/material-demo-forked-c5zpk?file=/demo.js:494-767 b. else?

briant1234 commented 3 years ago

Came here to report the same issue, and I was also interested in a potential solution! I was looking for a way to pass a ref to my desired container component, as a prop or something similar. The javascript solution mentioned also seems to make sense. I am using FlexLayout in my react app, and would like to be able to have the Grid respond to each tab https://github.com/caplin/FlexLayout

membla commented 3 years ago

Given that Container Queries are now in Chrome Canary, and the immense utility and push for this that has been over the years I'd say this possibly (probably) would get other vendors in motion too.

I actually came here looking for a way to decouple the breakpoints from the @media css implementation by allowing function values and thus having the freedom to let the breakpoint mean just about anything (but emulating container queries would clearly be the most common use case). Correct me if I'm wrong but this is not possible today, right? @oliviertassinari

oliviertassinari commented 3 years ago

@membla looking at the syntax of the container query, it seems something that we could easily support. The main challenge is about the browser support. At which point should we consider the feature with enough adoption to expose an API for it?

membla commented 3 years ago

Yes, that's the question. But at least I think the scene is firmly set for it now. So should be something to be taken into consideration in future planning I reckon, I guess it also depends on what kind of integration/interoperability with the current @media query system you'd aim for.

Anyway, my particular use case at the moment for something like this was very niche, I wanted to support @media queries in essentially an entire mui app based on the dimensions of the container it was mounted into. I finally just turned to react-frame-component but that was where the idea of some option of decoupling the responsivity/breakpoints from @media queries came from. Maybe that could be a way to introduce @container queries, but now I'm just speculating and it's late. @oliviertassinari

zehawki commented 3 years ago

Given slow adoption rates, I would imagine it would be a long time before Container Queries can be relied on, or?

@oliviertassinari your demo (https://codesandbox.io/s/material-demo-forked-c5zpk?file=/demo.js:494-767) looks absolutely perfect. Doesnt that already solve the issue? I will try to implement the same and report back.

zehawki commented 3 years ago

Sorry now I'm a bit confused. The Box API (https://material-ui.com/components/box/) does not have the props you mentioned.

oliviertassinari commented 3 years ago

@zehawki The docs of v4 !== the docs of v5.

zehawki commented 2 years ago

Hi @oliviertassinari is there an update to this in v5, now that's it formally out. ie is there a better way to do responsive grid?

Mistes974 commented 2 years ago

for people who want an alternative while waiting react-container-query

Modularity is the heart of component based UI. With most JavaScript modularized, CSS failed to catch up. When developing a responsive web page, we use media queries to toggle styles based on the size of the viewport. This creates problems when creating component level styles. The same component will behave differently when it is placed in different locations on a page. It seriously breaks the modularity of a component. We need components to be responsive and independent of viewport sizes.

nassim-mes commented 2 years ago

Intent to support container queries in chrome https://groups.google.com/a/chromium.org/g/blink-dev/c/gwzxnTJDLJ8

ianschmitz commented 2 years ago

Container queries are available in Chromium as well as Safari. No impl. in Firefox but looks like they're working on it!

https://caniuse.com/css-container-queries https://bugzilla.mozilla.org/show_bug.cgi?id=1744221

Mistes974 commented 1 year ago

Container queries are also available for Tailwind https://tailwindcss.com/blog/tailwindcss-v3-2#container-queries

oliviertassinari commented 1 year ago

@Mistes974 I had a quick play with the API, to see a bit how it feels like right now:

import * as React from "react";
import { styled } from "@mui/system";

const Container = styled("div")({
  display: "flex",
  flexWrap: "wrap",
  gap: 4,
  containerType: "inline-size"
});

const Item = styled("div")(({ theme }) => ({
  background: "#e4bebb",
  width: "100%",
  [theme.breakpoints.up("sm").replace("@media", "@container")]: {
    width: "calc(50% - 4px)"
  },
  [theme.breakpoints.up("md").replace("@media", "@container")]: {
    width: "calc(100% / 4 - 12px)"
  }
}));

export default function GridSimple() {
  return (
    <Container>
      {new Array(10).fill().map(() => (
        <Item>test case</Item>
      ))}
    </Container>
  );
}

https://codesandbox.io/s/pedantic-bardeen-b9ghp5?file=/src/App.js

A few ideas:

  1. We might want to add an equivalent helper to theme.breakpoints.up, e.g. theme.query.container.up("md") or to make it configurable, theme.breakpoints.up("md", { container: true }).
  2. The sx prop could benefit from a breakpoint. I'm not sure about the API. e.g. <Box sx={{ width: { xs: 0, container: true } }} />

But in any case, this is off-topic to this issue. This issue is about the Grid component.