search

Home  >  Q&A  >  body text

As the parent element becomes smaller, reduce the padding size

I have a grid that draws squares in cells. It has the number of rows and columns, then draws the grid cells and checks if there should be a square in each cell (according to the array) and draws a square if needed. The HTML final result looks like this: (assuming I have 1 row and 3 columns, only 2 cells should have squares)

.row {
  display: flex;
  flex-wrap: wrap;
  flex: 10000 1 0%;
}

.column {
  display: flex;
  flex-wrap: wrap;
  max-width: 100px;
  min-width: 10px;
  padding: 4px;
  border: 1px solid grey;
}

.square {
  background-color: red;
  width: 100%;
  aspect-ratio: 1/1;
  border-radius: 5px;
}
<div class="row">
    <div class="column">
        <div class="square"></div>
    </div>
    <div class="column">
        <div class="square"></div>
    </div>
    <div class="column"></div>
</div>

The rows take up the entire width of the screen and the column sizes should be the same between all columns and change based on the number of columns on the screen (for example if I have 5 columns they should all come with a width of 100 pixels but if I have 1000 columns and they should all be 10 pixels wide).

My problem is that the padding and border radius look weird after a certain breakpoint in the column size, and I want to change their values ​​when I hit that breakpoint. I can't use @container queries because it's still not fully supported.

If it helps, I'm using vue 2. But I think a CSS solution would be better in this case.

P粉807397973P粉807397973268 days ago390

reply all(1)I'll reply

  • P粉427877676

    P粉4278776762024-04-04 14:37:48

    Attempt to solve the problem described:

    I made a small demo to help me better explore the conditions for achieving this scenario.

    Get border:collapse equivalent on flexbox item

    The

    .row element is still a Flexbox container, but its Flex item does not have border set, but uses the outline setting for styling.

    The outline takes up no space and will "collapse" when colliding with the outline generated by another element.

    So, to ensure that the layout is not affected by styling weirdness, when trying to display the borders of a Flex item, this demo relies on only 2 key aspects to render those borders:

    • Set the gap between flexible items
    • Set Outline size to cover the gap left between element
    .row {
      gap: var(--col-gap);
    }
    .column {
      outline: var(--col-gap) solid gray;
    }

    Use ::after to add content to an element

    Additionally, the red dot is applied as a ::after pseudo-element with position:absolute, again ensuring that nothing affects the grid layout:

    .column.square::after {
      position: absolute;
      content: '';
      background-color: red;
      
      width: 50%;
      aspect-ratio: 1/1;
      border-radius: 100%;
      
      top: 50%;
      left: 50%;
      transform: translate(-50%, -50%); 
    }

    Dashboard - Explore Options

    From there, I added a "dashboard" with position:fixed which stays at the top of the page and allows you to control:

    • Column Width (px) : You can set the width here, changing the number of columns per row based on the available container space
    • Number of columns per row: Here you can set the number of columns per row, changing its width based on the available container space width
    • Gap between cells (px) : Gap between cells on the grid
    • Toggle red dot visibility: will show/hide the red dot, again display: none; will not change the grid layout it depends entirely on .column element size set by custom variable --col-width
    • Toggle counter visibility: Will show/hide the counter on top of each flex item

    Conclusion so far:

    Although we have worked hard to minimize distractions and taken all the steps needed to correctly set up the grid layout based only on the fixed size of the cells , there are still some issues Rendering issues, sometimes a general mismatch pattern in border sizes for certain lines. I should say that I only have issues with my laptop display, not my desktop monitor, so that's another factor.

    I tried different parameters and crunched the numbers in the demo, taking the gaps into account as well. A good and safe layout can minimize potential problems (for example, it can also increase the border size).

    I couldn't get any further than this using Flex layout.

    const container = document.getElementById('container');
    
    //draws the board
    emptyElementAndFillWithColumns(container, 100);
    //sets some columns randomly as .square
    addRandomSquares(container);
    
    //initializes the dashboard with the value coming from the css custom props
    let columnsGap = parseInt(getCssCustomProp('col-gap'));
    let columnsWidth = parseInt(getCssCustomProp('col-width'));
    document.getElementById('gap').value = columnsGap;
    document.getElementById('width').value = columnsWidth;
    document.getElementById('width').dispatchEvent(new Event('change'));
    document.getElementById('cols').value = Math.trunc(container.offsetWidth / (columnsWidth+columnsGap));
    
    //input#width change event handler
    document.getElementById('width')
      .addEventListener('change', event => {
        const width = parseInt(event.target.value);
        const newCols = Math.trunc(container.offsetWidth / (width+columnsGap));
        setCssCustomProp(container, 'col-width', `${width}px`);
        document.getElementById('cols').value = newCols;
      });
    
    //input#cols change event handler
    document.getElementById('cols')
      .addEventListener('change', event => {
        const cols = parseInt(event.target.value);
        const newWidth = Math.trunc(container.offsetWidth / cols) - columnsGap;
        setCssCustomProp(container, 'col-width', `${newWidth}px`);
        document.getElementById('width').value = newWidth;
      });
      
    //input#gap change event handler
    document.getElementById('gap')
      .addEventListener('change', event => {
        const gap = parseInt(event.target.value);
        setCssCustomProp(container, 'col-gap', `${gap}px`);
        columnsGap = gap;
      });
      
    //input#toggle-dots change event handler
    document.getElementById('toggle-dots')
      .addEventListener('change', event => {
        container.classList.toggle('hide-dots');
      });
      
    //input#toggle-counters change event handler
    document.getElementById('toggle-counters')
      .addEventListener('change', event => {
        container.classList.toggle('hide-counters');
      });
    
    //sets the --propName custom property at the style of target
    function setCssCustomProp(target, propName, value){
      target.style.setProperty(`--${propName}`, `${value}`);
    }
    
    //gets the --propName custom property value from the rule set on :root
    function getCssCustomProp(propName){
      const propValue =
      getComputedStyle(document.documentElement).getPropertyValue(`--${propName}`);
      return propValue;
    }
    
    //resets the container and appends a count number of columns
    function emptyElementAndFillWithColumns(target, count){
      for (i = 0; i <= count; i++) {
        const column = document.createElement('div');
        column.classList.add('column');
        target.append(column);
      }
    }
    
    //adds the square class to random .column elements in target
    function addRandomSquares(target){
      target.querySelectorAll('.column').forEach(column => {
        if (Math.random() >= 0.5)
          column.classList.add('square');
      })
    }
    :root {
      --col-width: 100px;
      --col-gap: 1px;
    }
    
    *,
    *::after,
    *::before {
      box-sizing: border-box;
      margin: 0;
      padding: 0;
    }
    
    body {
      font-family: sans-serif;
    }
    
    .row {
      display: flex;
      flex-wrap: wrap;
      gap: var(--col-gap);
      counter-reset: itemnr;
    }
    
    .column {
      position: relative;
      display: flex;
      flex-wrap: wrap;
      width: var(--col-width);
      height: var(--col-width);
      padding: 4px;
      outline: var(--col-gap) solid gray;
    }
    
    .column.square::after {
      position: absolute;
      content: '';
      background-color: red;
      
      width: 50%;
      aspect-ratio: 1/1;
      border-radius: 100%;
      
      top: 50%;
      left: 50%;
      transform: translate(-50%, -50%); 
    }
    
    .dashboard {
      position: fixed;
      right: 1rem;
      top: 2rem;
      border: solid darkgray;
      padding: 1em;
      z-index: 100;
      background: gray;
      color: white;
      font-weight: 600;
      font-size: 1.2rem;
      opacity: .9;
    }
    
    .dashboard > *{
      display: grid;
      grid-template-columns: 1fr auto;
      width: 100%;
      gap: 1em;
    }
    
    .dashboard label{
    }
    
    .dashboard input[type="number"] {
      width: 5em;
      cursor: pointer;
    }
    
    .dashboard input[type="checkbox"] {
      width: 1rem;
      line-height: 1rem;
      cursor: pointer;
    }
    
    #container.hide-dots .square::after{
      display: none;
    }
    
    #container.hide-counters .column::before{
      display: none;
    }
    
    small{
      grid-column: 1 / -1;
      font-size:.8rem;
      text-align: center;
      width: 100%;
      margin-bottom: 1rem;
    }
    
    .column::before{
      position: absolute;
      counter-increment: itemnr; 
      content: counter(itemnr);
      font-size: .8rem;
      z-index: 10;
      font-weight: 600;
    }

    reply
    0
  • Cancelreply