JSFiddle - React, Tailwind, and code Playground

HTML

<div id="cb-root">
  <div class="checkbox-container">
    <label for="canada">Canada</label>
    <input id="canada" type="checkbox">
    <div class="checkbox-list">

      <div class="checkbox-container">
        <label for="ontario">Ontario</label>
        <input id="ontario" type="checkbox">
        <div class="checkbox-list">

          <div class="checkbox-container">
            <label for="ott">Ottawa</label>
            <input id="ott" type="checkbox">
            <div class="checkbox-list">
            </div>
          </div>

          <div class="checkbox-container">
            <label for="tor">Toronto</label>
            <input id="tor" type="checkbox">
            <div class="checkbox-list">
            </div>
          </div>

        </div>
      </div>

      <div class="checkbox-container">
        <label for="quebec">Quebec</label>
        <input id="quebec" type="checkbox">
        <div class="checkbox-list">

          <div class="checkbox-container">
            <label for="mor">Montreal</label>
            <input id="mor" type="checkbox">
            <div class="checkbox-list">
            </div>
          </div>

          <div class="checkbox-container">
            <label for="qc">Quebec City</label>
            <input id="qc" type="checkbox">
            <div class="checkbox-list">
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
</div>

CSS

.checkbox-list {
  margin: 5px 0 0 20px;
}

.checkbox-container {
  margin-top: 5px;
}

JavaScript

const UNCHECKED = 0;
const CHECKED = 1;
const IND = 2;

function Checkbox(root, onChange) {
  this.children = [];
  this.childrenChecked = 0;
  this.childrenInd = 0;
  this.currState = UNCHECKED;
  this.onChange = onChange;
  this.checkbox = root.querySelector("input");
  const list = root.querySelector(".checkbox-list");
  const onChildChange = (prevState, currState) => {
    if (prevState == CHECKED) {
      this.childrenChecked--;
    } else if (prevState == IND) {
      this.childrenInd--;
    }
    if (currState == CHECKED) {
      this.childrenChecked++;
    } else if (currState == IND) {
      this.childrenInd++;
    }
    if (this.childrenChecked == this.children.length) {
      this.checkbox.checked = true;
      this.checkbox.indeterminate = false;
      this.onChange(this.currState, CHECKED);
      this.currState = CHECKED;
    } else if (this.childrenInd != 0 || this.childrenChecked != 0) {
      this.checkbox.checked = false;
      this.checkbox.indeterminate = true;
      this.onChange(this.currState, IND);
      this.currState = IND;
    } else {
      this.checkbox.checked = false;
      this.checkbox.indeterminate = false;
      this.onChange(this.currState, UNCHECKED);
      this.currState = UNCHECKED;
    }
  }
  list.childNodes.forEach(node => {
  	if(node.className == 'checkbox-container') {
        this.children.push(new Checkbox(node, onChildChange));
    }
  });

  this.checkbox.addEventListener("click", (e) => {
    let newState;
    if(this.checkbox.checked) {
    	newState = CHECKED;
      this.childrenInd = 0;
      this.childrenChecked = this.children.length;
    } else {
    	newState = UNCHECKED;
      this.childrenInd = 0;
      this.childrenChecked = 0;
    }
    this.onChange(this.currState, newState);
    this.currState = newState;
    this.children.forEach(child => child.onParentUpdate(newState));
  });
}

Checkbox.prototype.onParentUpdate = function(parentState) {
	if(parentState == CHECKED) {
  		this.checkbox.checked =...