Chaarts

Every charts in this project relies solely on semantic markup — <table> based — and a spread of CSS variables carried by the tags.. No JavaScript required for display, and styles are progressively enhanced depending on your browser's capabilities.

Note : by virtue of the experimental nature of these techniques and a solid foundation enhanced progressively, I don't mention browser support for each example — but it goes without saying that this is not magic, and only modern browsers handle this right, with the notable exception of Edge which does not (yet) support clip-path. Other browsers display a properly styled table, and that's nice.

Accessibility

A major effort has been made to ensure accessibility. As mentioned above, semantic and structured markup is a prerequisite — but it's not enough. CSS is being applied as gradually as possible, in order to guarantee the best possible display of data for each user.

Accessible table

Wakeup call :

For other useful tips, I warmly recommend reading Data Tables Inclusive Component by Heydon Pickering, which is a real gold mine.

Patterns

To distinguish the different areas other than by colour, an svg pattern is applied in css — you can find some of them on the Hero Patterns website:

  1. in order to improve blending with background colors or gradients, the background-blend-mode proerty is used with the hard-light value;
  2. pattern's size and position depends directly on the value and scale of the chart, depending on the type of chart;
  3. in order not to embed too many external files, we use a technique proposed by Trys Mudford to include each svg in data URi in a css variable; thus, a finite list of patterns is used in the theme, without ever repeating the svg.

What to encode?

Taylor Hunt a comprehensive article on optimizing past svg in URi data. In summary, only the chevrons and the "#" character need to be encoded in the css. Don't bother with the other characters, their encoding seriously affects readability.

.chaarts {
  --stripes: url("data:image/svg+xml,%3Csvg width='6' height='6' viewBox='0 0 6 6' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='%23ffffff99' fill-rule='evenodd'%3E%3Cpath d='M5 0h1L0 6V5zM6 5v1H5z'/%3E%3C/g%3E%3C/svg%3E");
}

.chaarts tr:nth-child(2n + 2) {
  --background: var(--stripes);
}

.chaarts td {
  --size: calc( var(--scale, 100) * 100% );
  --position: calc( var(--value, 0) / var(--scale, 100) * 100% );
  background:
    linear-gradient( right, Window, ButtonFace, ButtonText, highlight ) var(--position) 0% / var(--size) 100%,
    var(--background) center / contain;
  background-blend-mode: hard-light;
}

User preferences

In order to respect as much as possible the preferences of the visitors, many elements have been adapted:

  1. dimensions are in relatives units (em or rem as the case may be), in order to fit coherently with the body of text inherited from the browser and to be able to be enlarged or reduced without loss;
  2. colors are adjusted when Windows High Contrast Mode is detected using -ms-high-contrast: active, inspired by Greg Whitworth article on the topic. I also recommend you Test of System Colors Specified in CSS 2 rustproof page written by Ian Graham, published in… 2000!
  3. animations and transitions are disabled when the system exposes this preference through prefers-reduced-motion: reduce ;
  4. hover effects whose initial state consists in hiding content are activated contextually in the @media (hover: hover) { … } media query.

display and semantics

Adrian Roselli explains that playing with a <table> or <dl> element's display endangers its semantics. The latter is therefoore "locked" using dedicated aria roles — as he explains in a detailed article.

That's why each table is preceded by a switch — based on Heydon Pickering inclusive toggle button — whose one and only role is to disable additional styles:

document.addEventListener("DOMContentLoaded", function() {
  var switches = document.querySelectorAll('[role="switch"]');

  Array.prototype.forEach.call(switches, function(el, i) {
    el.addEventListener('click', function() {
      var checked = this.getAttribute('aria-checked') == 'true' || false;
      this.setAttribute('aria-checked', !checked);

      var chart = this.parentNode.nextElementSibling;
      chart.classList.toggle('table-charts');
    });
  });
});

Well, we're ready to get to the heart of the matter.
Warm up your dev tools!