// 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 declare the 'colors' variable after creating an
    // array of colors from the supplied dataset-attribute;
    // first splitting the string on commas:
    colors = c.split(/,/)
    // using Array.prototype.filter() to retain
    // only those Array entries that:
    .filter(
      // both have an entry, have a non-zero length 
      // and which is not composed of just white-space:
      (color) => color && color.trim().length > 0
    )
    // we then use Array.prototype.map() to create a
    // new Array based on the filtered Array (which
    // retains or discards Array-elements, but doesn't
    // modify the Array elements):
    .map(
      // here we use String.prototype.trim() to
      // remove leading and trailing white-space:
      (color) => color.trim()
    ),
    // creating a document-fragment in order that all
    // created elements are appended to the document at
    // once, minimising reflows and repaints:
    fragment = D.createDocumentFragment();
  // to enable the size to be used in CSS, we use
  // CSSStyleDeclaration.setProperty() to set a '--size'
  // property, initialised to the value of the size integer:
  source.style.setProperty(`--size`, size);
  // we use Array.from() to create an Array from the provided
  // Object, which sets the length equal to the square of the
  // size integer (using Math.pow(<integer>,<power>) to do
  // so, but size * size would work equally well):
  Array.from({
      length: Math.pow(size, 2)
    })
    // iterating over the created Array, which passes in
    // a reference to the current Array-element (which is
    // undefined), and the index of the current Array-
    // element in the created Array:
    // within the Arrow function, we create a <div> element,
    // set its classList property using a template-literal,
    // to add the classes of 'grid-cell', 'row-n' (where n
    // is the row-number), 'row-odd' or 'row-even',
    // 'column-n' (where n is an integer, and reflects the
    // column-number), and column-odd or column-even:
    .forEach((_, i) => fragment.append(create('div', {
      classList: `grid-cell row-${ Math.floor(i/size) + 1}
                  row-${ 0 === Math.floor(i/size)%2 ? 'even' : 'odd'}
                  column-${ Math.floor(i%size) + 1}
                  column-${ 0 === Math.floor(i%size) + 1 ? 'odd' : 'even' }`,
      // we also set the --backgroundColor CSS custom property, which
      // cycles through the Array of colors passed via the data-colors
      // attribute:
      style: `--backgroundColor: ${ colors[(i%size)%colors.length] }`
      // the created element (on each iteration of the forEach()) is
      // appended to the document fragment using the initial fragment.append()
    })));
  // appending the fragment to the source, using Element.append():
  source.append(fragment);
};
// calling the function, passing in the gridParent element:
createGrid(gridParent);
// the below is largely irrelevant, it's just to enable some editing:
// here we use an Array literal along with spread syntax to create an
// Array of the child elements of the <fieldset> element; we then use
// Array.prototype.forEach() to iterate over those child-elements:
[...get('fieldset').children].forEach(
  // passing a reference to the current child element to the function:
  (child) => {
    // caching a reference to the <input> element within the
    // current child element:
    let input = get('input', child);
    // setting the value of the <input> to be equal to
    // the data-[input.name] attribute-value of the
    // gridParent element; so the value of the <input>
    // is equal to the current attribute-values on page-
    // load:
    input.value = gridParent.dataset[input.name];
    // binding the anonymous function as the event-handler
    // for the 'input' event:
    input.addEventListener('input', (evt) => {
      // using destructuring assignment to set
      // the variable 'target' to be equal to the
      // 'target' property-value of the Event (evt)
      // Object:
      let {
        target
      } = evt, {
        name,
        value
      } = target;
      // setting the gridParent's dataset[input.name]
      // attribute value to be equal to the value of the
      // <input>:
      gridParent.dataset[name] = value;
      createGrid(gridParent);
    });
  });
*,
::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.5);
  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;
}
/*
.grid-cell:nth-child(odd) {
  background-color: red;
}
.grid-cell:nth-child(even) {
  background-color: green;
}
*/
.even {
  background-color: red;
}
.odd {
  background-color: green;
}
<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>