skip to Main Content

I have the HTML and CSS as follows

.flex {
  font: 14px Arial;
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
}

.flex>div {
  flex: 1 1 auto;
  width: 50%;
  box-sizing: border-box;
  border: 1px solid black;
  text-align: center;
  line-height: 20px;
  padding: 5px;
}
<div class="flex">
  <div>{data['player1-pre']}</div>
  <div>{data['player2-pre']}</div>
  <div>{data['player1-post']}</div>
  <div>{data['player2-post']}</div>
</div>

This renders as follows:enter image description here

I would like to add labels on the grid lines like this enter image description here

Is there any way to achieve this? Thanks!

2

Answers


  1. I feel like I brute forced it, but here’s something:

    .flex-container {
      position: absolute;
      width: 100%;
    }
    .flex {
      font: 14px Arial;
      display: flex;
      flex-direction: row;
      flex-wrap: wrap;
    }
    .flex > div {
      flex: 1 1 auto;
      width: 50%;
      box-sizing:border-box;
      border:1px solid black;
      text-align:center;
      line-height:20px;
      padding:5px;
    }
    .grid-lines {
      position: absolute;
      top: 0px;
      height: 100%;
      width: 100%;
      display: flex;
      flex-direction: column;
    }
    .grid-lines > div {
      box-sizing:border-box;
      text-align:center;
      line-height:20px;
    }
    .space {
      display: flex;
      justify-content: space-between;
    }
    .inline {
      display: inline;
      text-align: center;
      width: 50%;
    }
    <div class="flex-container">
      <div class="flex">
        <div>abcd</div>
        <div>efg</div>
        <div>hijk</div>
        <div>lmnop</div>
      </div>
      <div class="grid-lines">
        <div>top</div>
        <div class="space">
          <div class="inline">middle 1</div>
          <div class="inline">middle 2</div>
        </div>
        <div>bottom</div>
      </div>
    </div>

    Lot’s of flex displays and a bit of absolute positioning to get the text on the gridlines in place.

    Login or Signup to reply.
  2. You can use three grids to achieve this; one for the items, one for vertical labels, one for horizontal labels:

    <div id="wrapper">
      <div class="grid" id="actual">
        <!-- <div class="item"><span class="text"></span></div> -->
        <!-- ... -->
      </div>
      <div class="grid" id="vertical"></div>
      <div class="grid" id="horizontal"></div>
    </div>
    

    …of which the latter two will overlay the first.

    Here’s a visualization using ASCII art (1 means 1fr, etc.):

    #actual:                                     #vertical:                       
        1       1       1       1                    1       1       1       1    
    ┌───────┬───────┬───────┬───────┐            ┌───────┬───────┬───────┬───────┐
    │       │       │       │       │          1 │       │       │       │       │
    │  abc  │  abc  │  abc  │  abc  │ 1          ├───────┼───────┼───────┼───────┤
    │       │       │       │       │            │       │       │       │       │
    ├───────┼───────┼───────┼───────┤          2 │  _B_  │  _B_  │  _B_  │  _B_  │
    │       │       │       │       │            │       │       │       │       │
    │  abc  │  abc  │  abc  │  abc  │ 1          ├───────┼───────┼───────┼───────┤
    │       │       │       │       │            │       │       │       │       │
    ├───────┼───────┼───────┼───────┤          2 │  _B_  │  _B_  │  _B_  │  _B_  │
    │       │       │       │       │            │       │       │       │       │
    │  abc  │  abc  │  abc  │  abc  │ 1          ├───────┼───────┼───────┼───────┤
    │       │       │       │       │          1 │       │       │       │       │
    └───────┴───────┴───────┴───────┘            └───────┴───────┴───────┴───────┘
    
    
    #horizontal:                                 #wrapper:                        
      1     2       2       2     1                  1       1       1       1    
    ┌───┬───────┬───────┬───────┬───┐            ┌───────┬───────┬───────┬───────┐
    │   │       │       │       │   │            │       │       │       │       │
    │   │  A_C  │  A_C  │  A_C  │   │ 1        1 │  abc A_C abc A_C abc A_C abc  │
    │   │       │       │       │   │            │       │       │       │       │
    ├───┼───────┼───────┼───────┼───┤            ├──_B_──┼──_B_──┼──_B_──┼──_B_──┤
    │   │       │       │       │   │            │       │       │       │       │
    │   │  A_C  │  A_C  │  A_C  │   │ 1        1 │  abc A_C abc A_C abc A_C abc  │
    │   │       │       │       │   │            │       │       │       │       │
    ├───┼───────┼───────┼───────┼───┤            ├──_B_──┼──_B_──┼──_B_──┼──_B_──┤
    │   │       │       │       │   │            │       │       │       │       │
    │   │  A_C  │  A_C  │  A_C  │   │ 1        1 │  abc A_C abc A_C abc A_C abc  │
    │   │       │       │       │   │            │       │       │       │       │
    └───┴───────┴───────┴───────┴───┘            └───────┴───────┴───────┴───────┘
    

    We can clearly see that #vertical has more than one row, and #horizontal one column, comparing to #actual. Let --rows and --columns be the number of rows and columns of #actual, here’s our CSS skeleton:

    /* Create layers */
    
    #wrapper {
      position: relative;
    }
    
    #vertical,
    #horizontal {
      position: absolute;
      top: 0;
      left: 0;
      height: 100%;
      width: 100%;
    }
    
    
    /* Base styles for all 3 grids and their items */
    
    .grid {
      display: grid;
      align-items: stretch;
    }
    
    .item {
      display: flex;
      align-items: center;
      justify-content: center;
      text-align: center;
    }
    
    
    /* grid-template-areas */
    
    #actual {
      grid-template-rows: repeat(var(--rows), 1fr);
      grid-template-columns: repeat(var(--columns), 1fr);
    }
    
    #vertical {
      grid-template-rows: 1fr repeat(calc(var(--rows) - 1), 2fr) 1fr;
      grid-template-columns: repeat(var(--columns), 1fr);
    }
    
    #horizontal {
      grid-template-rows: repeat(var(--rows), 1fr);
      grid-template-columns: 1fr repeat(calc(var(--columns) - 1), 2fr) 1fr;
    }
    

    To prevent vertical and horizontal labels from overlapping grid lines, use isolation: isolate on .item and mix-blend-mode: multiply on .text:

    #vertical .item,
    #horizontal .item {
      isolation: isolate;
    }
    
    #vertical .text,
    #horizontal .text {
      background-color: #fff;
      padding: 0 var(--gap);
      color: #f00;
      mix-blend-mode: multiply;
    }
    

    Note that, since we are skipping the first and last row of #vertical, along with the first and last column of #horizontal, we need to fix the padding of both those .grids and their corresponding items. The math behind this is left as an exercise for the reader:

    :root {
      --gap: 2rem;
    }
    
    .grid {
      padding: var(--gap);
    }
    
    #vertical {
      grid-template-rows: 1fr repeat(calc(var(--rows) - 1), 2fr) 1fr;
      grid-template-columns: repeat(var(--columns), 1fr);
      padding: calc(var(--gap) / 2) var(--gap);
    }
    
    #horizontal {
      grid-template-rows: repeat(var(--rows), 1fr);
      grid-template-columns: 1fr repeat(calc(var(--columns) - 1), 2fr) 1fr;
      padding: var(--gap) calc(var(--gap) / 2);
    }
    
    function fixPadding() {
      const style = document.createElement('style');
      style.textContent = `
    #vertical .item:nth-child(-n + ${columns}),
    #vertical .item:nth-last-child(-n + ${columns}),
    #horizontal .item:nth-child(${columns + 1}n),
    #horizontal .item:nth-last-child(${columns + 1}n) {
      padding: 0;
    }
    `;
      document.head.appendChild(style);
    }
    

    Try it (randomized content and dimension):

    /* The only thing necessary is fixPadding() */
    
    const [wrapper, actual, vertical, horizontal] = [
      '#wrapper',
      '#actual',
      '#vertical',
      '#horizontal'
    ].map(selector => document.querySelector(selector));
    
    const randomIntInRange_0_10 = randomIntSmallerThan(10);
    const randomIntInRange_0_27 = randomIntSmallerThan(27);
    
    const [height, width] = [8, 4].map(max => 2 + randomIntSmallerThan(max)());
    const gap = randomIntInRange_0_10() * 0.2;
    
    wrapper.style.setProperty('--gap', gap + 'rem');
    wrapper.style.setProperty('--rows', height);
    wrapper.style.setProperty('--columns', width);
    
    fillGrid(actual, height, width, randomText);
    fillGrid(
      vertical, height + 1, width,
      i => i % height ? 'vertical' : ''
    );
    fillGrid(
      horizontal, height, width + 1,
      (_, j) => j % width ? 'horizontal' : ''
    );
    
    fixPadding();
    
    
    /* Functions */
    
    function randomIntSmallerThan(number) {
      return () => (Math.random() * number) | 0;
    }
    
    function randomText() {
      const length = randomIntInRange_0_10() + 5;
    
      const text = Array.from({ length }, () => {
        const code = randomIntInRange_0_27();
    
        return (code ? String.fromCharCode(code + 96) : ' ')[
          `to${Math.random() < 0.5 ? 'Upper' : 'Lower'}Case`
        ]();
      });
    
      return text.join('');
    }
    
    function fillGrid(element, height, width, callback) {
      for (let i = 0; i < height; i++) {
        for (let j = 0; j < width; j++) {
          const item = document.createElement('div');
          const text = document.createElement('span');
    
          text.textContent = callback(i, j);
    
          item.classList.add('item');
          text.classList.add('text');
    
          item.appendChild(text);
          element.appendChild(item);
        }
      }
    }
    
    /**
     * Remove padding for:
     * * First {width} and last {width} #vertical items
     * * First and last item of each #horizontal row.
    **/
    function fixPadding() {
      const style = document.createElement('style');
      style.textContent = `
    #vertical .item:nth-child(-n + ${width}),
    #vertical .item:nth-last-child(-n + ${width}),
    #horizontal .item:nth-child(${width + 1}n),
    #horizontal .item:nth-last-child(${width + 1}n) {
      padding: 0;
    }
    `;
      document.head.appendChild(style);
    }
    :root {
      --padding: 1em;
    }
    
    * {
      box-sizing: border-box;
    }
    
    #wrapper {
      position: relative;
    }
    
    .grid {
      display: grid;
      align-items: stretch;
      gap: var(--gap);
      padding: var(--gap);
    }
    
    .item {
      display: flex;
      align-items: center;
      justify-content: center;
      padding: var(--padding);
      text-align: center;
      word-break: break-all;
      white-space: pre-wrap;
    }
    
    #actual {
      grid-template-rows: repeat(var(--rows), 1fr);
      grid-template-columns: repeat(var(--columns), 1fr);
    }
    
    #vertical {
      grid-template-rows: 1fr repeat(calc(var(--rows) - 1), 2fr) 1fr;
      grid-template-columns: repeat(var(--columns), 1fr);
      padding: calc(var(--gap) / 2) var(--gap);
    }
    
    #horizontal {
      grid-template-rows: repeat(var(--rows), 1fr);
      grid-template-columns: 1fr repeat(calc(var(--columns) - 1), 2fr) 1fr;
      padding: var(--gap) calc(var(--gap) / 2);
    }
    
    #actual,
    #actual .item {
      border: 1px solid black;
    }
    
    #vertical,
    #horizontal {
      position: absolute;
      top: 0;
      left: 0;
      height: 100%;
      width: 100%;
    }
    
    #vertical .item,
    #horizontal .item {
      isolation: isolate;
    }
    
    #vertical .text,
    #horizontal .text {
      background-color: #fff;
      padding: 0 var(--gap);
      color: #f00;
      mix-blend-mode: multiply;
    }
    <div id="wrapper">
      <div class="grid" id="actual"></div>
      <div class="grid" id="vertical"></div>
      <div class="grid" id="horizontal"></div>
    </div>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search