Search code examples
cssreactjscss-gridchakra-ui

Grid Row/Column settings


I'm trying to create a Grid with React and Chakra-UI that behaves as follows: Grid behaviour

Basically, depending on the amount of colors it is given (with a maximum of 6) it will automatically display them as shown.

The code so far is the following:

<Grid
  width={'20vw'}
  minWidth={'200px'}
  height={'20vh'}
  minHeight={'100px'}
  boxShadow={`${shadowColor} 0px 0px 2px`}
  // Grid settings here
>
  {simulationPalette.colors.map((color) => (
      <Box backgroundColor={color} />
  ))}
</Grid>

I tried some settings like

gridTemplateColumns={`repeat(auto-fit, minmax(50%, 1fr))`}

which would create empty space in case of an odd number of colors

or simply

gridTemplateColumns={auto-fill}

which would just use a new columns for ever color


Solution

  • You need flexbox:

    #container {
      display: flex;
      flex-wrap: wrap;
    }
    
    #container div {
      /* Make them as wide as possible from a basis of 0 and shrink as needed */
      /* (see https://stackoverflow.com/q/37386244 for more information) */
      flex: 1;
      /* ...but not narrower than 1 / 3 of the container. */
      box-sizing: border-box;
      min-width: calc(100% / 3);
    }
    

    You also need to add a class or similar to let CSS know if #container has more than 4 children:

    #container.more-than-4-colors div {
      min-width: calc(100% / 3);
    }
    
    const method = container.children.length > 4 ? 'add' : 'remove';
    container.classList[method]('more-than-4-colors');
    

    When the :has() pseudo-class gets widely supported, you can use this:

    /* Not yet widely supported */
    #container:has(> :nth-child(5)) div {
      min-width: calc(100% / 3);
    }
    

    Try it:

    const container = document.querySelector('#container');
    const button = document.querySelector('button');
    
    button.addEventListener('click', () => {
      const element = document.createElement('div');
    
      if (container.children.length === 6) {
        container.innerHTML = '';
      }
      
      container.appendChild(element);
      
      const method = container.children.length > 4 ? 'add' : 'remove';
      container.classList[method]('more-than-4-colors');
    });
    #container {
      display: flex;
      flex-wrap: wrap;
    }
    
    #container div {
      flex: 1;
      box-sizing: border-box;
      min-width: calc(100% / 2);
    }
    
    #container.more-than-4-colors div {
      min-width: calc(100% / 3);
    }
    
    /* Not yet widely supported */
    #container:has(> :nth-child(5)) {
      min-width: calc(100% / 3);
    }
    
    
    /* Demo only */
    
    #container {
      margin: 1em 0;
    }
    
    #container div {
      height: 100px;
      outline: 1px solid var(--border);
      background: var(--background);
    }
    
    #container div:nth-child(1) {
      --border: #6c8ebf;
      --background: #dae8fc;
    }
    
    #container div:nth-child(2) {
      --border: #82b366;
      --background: #d5e8d4;
    }
    
    #container div:nth-child(3) {
      --border: #9673a6;
      --background: #e1d5e7;
    }
    
    #container div:nth-child(4) {
      --border: #d79b00;
      --background: #ffe6cc;
    }
    
    #container div:nth-child(5) {
      --border: #b85450;
      --background: #f8cecc;
    }
    
    #container div:nth-child(6) {
      --border: #d6b656;
      --background: #fff2cc;
    }
    <button>Add</button>
    
    <div id="container">
      <div></div>
    </div>