How can I add an accent-color to indeterminate checkbox state

Viewed 182

Chrome version 102.

How to add an accent-color to indeterminate checkbox state?

<input type="checkbox" id="my-checkbox"/> 

#my-checkbox {
   accent-color: blue
}

//checkbox is blue when checked

const checkbox = document.getElementById("my-checkbox");
checkbox.indeterminate = true; 

//checkbox color turns grey, but I want it to be blue.
3 Answers

:indeterminate, When a Form Control is Confused ‍

It's a semi-state given to checkboxes under a specific circumstance in which there is a "parent" or "primary" ("boss"?) checkbox that can un/check all subordinate checkboxes. We know that if the primary checkbox is checked then all of the subordinates are checked and they become all unchecked when the primary is unchecked. But what if the user checks or unchecks one of the subordinates instead? The primary shouldn't uncheck itself because whatever state it changes to should always dictate what the subordinates will do. So that's when the indeterminate should apply.

The checkbox in indeterminate state seems to be untouchable so if you want to directly style it, you'll need to hide the real checkbox and style an associated label as the surrogate:

HTML

<!-- You won't see indeterminate attribute displayed in devtool -->
<input id="MAJOR" type="checkbox" indeterminate> 
<label for="MAJOR">❎</label>
<input id="MINORa" type="checkbox" checked>
<label for="MINORa">✅</label>
<input id="MINORb" type="checkbox">
<label for="MINORb"></label>

CSS

[type="checkbox"] {
  display: none;
}
label::before {
  content: '';
}
[type="checkbox"]:checked + label::before {
  content: '✅';
}
[type="checkbox"]:indeterminate + label::before {
  content: '❎';
}

This old hack works great, but if you have to consider accessibility, you'll need to resort to plugins, a framework, a library, etc.. Or if you are willing to put in the research and effort it is possible to use the hack and maintain accessibility.

In the following example, Font-Awesome 6 is used to replace the checkboxes. Styling them is actually easier than native elements.

CodePen

Details are commented in example

// Register form#root to the "change" event
document.forms.root.onchange = status;

function status(event) {
  // Reference the element user un/checked 
  const pick = event.target;
  // Reference all form controls (fieldsets and inputs)
  const tree = this.elements;
  let trunk, branch, leaves;
  /*
  If pick has class .leaf...
  ...find the primary checkbox it belongs to...
  ...find the fieldset it belongs to...
  ...make an array of all checkboxes in the fieldset...
  ...find the total amount of checked boxes
  */
  /*
  If no boxes are checked, uncheck primary...
  ...if all checkboxes are checked check primary...
  ...otherwise uncheck primary and set it to indeterminate
  */
  /* 
  If the checkbox the user picked was a "trunk"...
  ...make an array of all of it's subordinate checkboxes...
  ...if primary is checked check all of the checkboxes and
  if unchecked, theen all subordinates will be unchecked as well
  */
  if (pick.matches(".leaf")) {
    trunk = tree[pick.name.toUpperCase()];
    branch = tree[pick.name + "X"];
    leaves = [...branch.elements];
    
    let checked = leaves.filter(
      (leaf) => leaf.checked).length;
      
    let state = 
      !checked ? [false, false] : 
      checked === leaves.length ? 
      [true, false] : 
      [false, true];
    trunk.checked = state[0];
    trunk.indeterminate = state[1];
  }

  if (pick.name === "trunk") {
    leaves = [...tree[pick.id.toLowerCase()]];
    let checkAll = pick.checked;
    leaves.forEach((leaf) => (leaf.checked = checkAll));
  }
}
@import url("https://fonts.googleapis.com/css2?family=Raleway:wght@500&display=swap");

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

html {
  font: 2ch/1.2 Consolas;
}

html,
body {
  width: 100%;
  min-height: 100%;
}

form {
  margin: 0 auto;
  padding: 5vh;
}

header {
  margin-bottom: 0.75rem;
}

h1 {
  margin-bottom: 0.5rem;
  font-family: Raleway;
  font-size: 1.25rem;
}

[name="trunk"],
.leaf {
  display: none;
}

[name="branch"] {
  width: max-content;
  margin: -0.5rem 1rem 1rem 0.75rem;
  padding: 0 1.5em;
}

legend {
  margin-bottom: 1rem;
}

menu {
  display: flex;
  flex-flow: column nowrap;
  list-style: none;
  margin-top: 0;
  padding-left: 0;
}

label {
  display: block;
  margin-bottom: 1rem;
}

menu label:last-of-type {
  margin-bottom: 0.25rem;
}

code {
  font-family: Consolas;
}

.far {
  font: var(--fa-font-regular);
}

/* Replace .far with .fas for inversed versions of the icons */
.fas {
  font: var(--fa-font-solid);
}

.far::before {
  content: "\f0c8";
  color: blue;
}

.leaf:checked + .far::before,
[name="trunk"]:checked + .far::before {
  content: "\f14a";
}

[name="trunk"]:indeterminate + .far::before {
  content: "\f146";
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.1.1/css/all.min.css" rel="stylesheet">

<form id="root" name="tree">
  <header>
    <h1>Indeterminate Checkboxes </h1>
    <code>id="root" name="tree" ⽊</code>
  </header>

  <input id="A" name="trunk" type="checkbox">
  <label for="A" class="far">
    <code> id="A" name="trunk" ⽙</code>
  </label>
  <fieldset id="aX" name="branch">
    <legend>
      <code> id="aX" name="branch" ⽀</code>
    </legend>
    <menu>
      <input id="a1" name="a" class="leaf" type="checkbox">
      <label for="a1" class="far">
        <code> id="a1" name="a" class="leaf" ⾴</code>
      </label>
      <input id="a2" name="a" class="leaf" type="checkbox">
      <label for="a2" class="far">
        <code> id="a2" name="a" class="leaf" ⾴</code>
      </label>
      <input id="a3" name="a" class="leaf" type="checkbox">
      <label for="a3" class="far">
        <code> id="a3" name="a" class="leaf" ⾴</code>
      </label>
    </menu>
  </fieldset>

  <input id="B" name="trunk" type="checkbox">
  <label for="B" class="far">
    <code> id="B" name="trunk" ⽙</code>
  </label>
  <fieldset id="bX" name="branch">
    <legend>
      <code> id="bX" name="branch" ⽀</code>
    </legend>
    <menu>
      <input id="b1" name="b" class="leaf" type="checkbox">
      <label for="b1" class="far">
        <code> id="b1" name="b" class="leaf" ⾴</code>
      </label>
      <input id="b2" name="b" class="leaf" type="checkbox">
      <label for="b2" class="far">
        <code> id="b2" name="b" class="leaf" ⾴</code>
      </label>
      <input id="b3" name="b" class="leaf" type="checkbox">
      <label for="b3" class="far">
        <code> id="b3" name="b" class="leaf" ⾴</code>
      </label>
    </menu>
  </fieldset>
</form>

try this:

::ng-deep .mat-checkbox-indeterminate.mat-accent .mat-checkbox-background {
accent-color: black !important;
}

Use the :indeterminate psuedo class

.my-checkbox:indeterminate {
    /* … */
}
Related