SO-76856866

by David Thomas

HTML

<form action="#" method="post">
  <fieldset>
    <label>
      <span class="labelText">Number of grid-cells:</span>
      <input type="number" min="1" max="20" step="1" name="size"></label>
    <label>
      <span class="labelText">grid-column colours:</span>
      <input type="text" name="colors"></label>
  </fieldset>
</form>

<!--
  data-size: an integer, to define the number of cells on each axis,
  data-colors: a comma-separated list of colors to cycle through:
-->
<div class="colored-grid" data-size="10" data-colors="red, green"></div>

CSS

*,
::before,
::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

body {
  margin: 0;
  display: flex;
  flex-flow: column nowrap;
  gap: 1rem;
  justify-content: center;
  align-items: center;
  min-height: 100vh;
  /* using syntax from level 4 of the
     CSS Colors Module: */
  background-color: rgb(255 255 255 / 0.9);
  font-family: Arial, sans-serif;
}

form {
  backdrop-filter: blur(3px) brightness(300%) hue-rotate(25deg);
  background-color: #fff7;
  position: sticky;
  top: 0;
}

fieldset {
  display: flex;
  gap: 1rem;
  justify-content: space-between;
}

label {
  display: grid;
  gap: 0.5rem;
  padding: 0.5rem;
}

.colored-grid {
  counter-reset: cell;
  display: grid;
  gap: 5px;
  height: auto;
  grid-auto-rows: 50px;
  grid-template-columns: repeat(var(--size), 50px);
}

.grid-cell {
  background-color: var(--backgroundColor);
  counter-increment: cell;
  display: grid;
  place-content: center;
}

.grid-cell::before {
  content: counter(cell, decimal-leading-zero);
}

.even {
  background-color: red;
}

.odd {
  background-color: green;
}

JavaScript

// some simple utilities to reduce typing;
// caching a reference to the document:
let D = document,
  // an alias for document.createElement(), with the option to provide
  // properties for the created element:
  create = (tag, props) => Object.assign(D.createElement(tag), props),
  // an alias for both document.querySelector() - by default - and
  // Element.querySelector(), if an Element node is passed as the
  // context argument:
  get = (selector, context = D) => context.querySelector(selector),

  // caching a reference to the element in which the grid should
  // be appended:
  gridParent = get('.colored-grid[data-size]');

// defining a function to create the grid, taking one argument,
// the element in which the created elements are to be placed:
let createGrid = (source) => {

  // if there is no HTMLElement passed to the function we
  // return false and quit the function:
  if (!source || 1 !== source.nodeType) {
    return false;
  }
  
  // while the source element has any firstChild (this includes
  // text-nodes, comment nodes, and elements:
  while (source.firstChild) {
    // we use parentNode.removeChild to remove that firstChild;
    // had the childNodes been only elements, we could have
    // used HTMLElement.remove(), but that would (probably) have
    // left an accumulation of text-nodes behind in the DOM (which
    // I personally find messy, as inconsequential as it may be):
    source.removeChild(source.firstChild);
  }
  
  // using destructuring assignment to declare the variables
  // 'sz' and 'c', as aliases for 'size' and 'color' from
  // the source.dataset Object, as the variables need to
  // be modified for use in the function and I prefer to
  // use the 'meaningful' names once they're ready for use:
  let {
    size: sz,
    colors: c
  } = source.dataset,
    // here we create the 'size' variable, after using
    // parseInt() to convert the 'sz' string to a base-10
    // integer:
    size = parseInt(sz, 10),
    // and...