<my-grid cols="a,b,c,d,e" rows="1,2,3,4,5">
  <!-- you can slot in as many or as few cells as you like -->
  <span slot="cell:b3">foo</span>
  <span slot="cell:d4">bar</span>
  <span slot="cell:e2">baz</span>
</my-grid>
body {
  font: 16px sans-serif; 
  padding: 1rem;
}
import { LitElement, html, css } from "https://cdn.skypack.dev/lit@2.7.0";

export class MyGrid extends LitElement {
  static styles = css`
    table {
      border-collapse: collapse;
    }
      
    table,
    th,
    td {
      border: solid 1px silver;
    }
    
    th,
    td {
      min-width: 40px;
      height: 40px;
      text-align: center;
      padding: 1rem;
    }
    
    td slot {
      color: silver;
    }
      
    td ::slotted(*) {
      color: red;  
    }
  `;

  static properties = {
    rows: {},
    cols: {}
  };
  
  constructor() {
    super();
    this.rows = '1,2,3';
    this.cols = 'a,b,c';
  }
    
  render() {
    const cols = this.cols?.split(',') ?? '';
    const rows = this.rows?.split(',') ?? '';
    
    return html`
      <table>
        <thead>
          <tr>
           <th></th>
           ${cols.map(col => html`<th>${col}</th>`)}
          </tr>
        </thead>
        <tbody>
          ${rows.map(row => html`
            <tr>
              <th>${row}</th>
              ${cols.map(col => html`
                <td><slot name="cell:${col + row}">${col + row}</slot></td>  
              `)}
            </tr>
          `)}
        </tbody>
      </table>
    `;
  }
}

customElements.define('my-grid', MyGrid);

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.