Christian Heilmann

Author Archive

HTML and CSS still isn’t about painting with code

Thursday, January 21st, 2021

The other day I had a friend come to my place with the request to “learn HTML and how web sites work”. We spent a few hours playing with the topic and she got excited and quite far into it. The whole thing reminded me about a real problem when it comes to teaching people HTML. We keep fast-tracking the learning to the fun part of building visual things with it.

People who started early with the web had platforms that allowed them to quickly publish things. Geocities, Neopets and many others. For many these were the only accessible ways to get on the web and learn about it. These platforms allowed you to write your first HTML and CSS. Without a doubt using platforms that allowed for inline CSS and HTML to style things was an awesome experience. It is fun to game a system and impress your peers with visual tricks they didn’t think possible. It is also fascinating to go on a trial and error journey to get to a goal.

None of that taught us HTML and CSS though. Both are much more than their syntax. It taught us not to understand how the web works, but take any shortcut necessary to get to a certain look and feel. We can’t only blame these platforms for that. WYSIWYG editors and woefully outdated education materials in schools and universities also taught us that HTML and CSS are there to paint in the browser and make something look a certain way. 

It also taught us to rely on browsers to do the job to convert our efforts into something that kind of works.This is why browsers are such complex beasts and why browser interoperability is such a hard task. We’ve accumulated years of shortcuts and hacks and old web products rely on them. Browsers can’t break the web. They need to support the bleeding edge but also the sins of the past.

Coming to HTML from a “do this and it will look like that” taught us to do things that should only be a last resort option. Specifically, this way of “learning” HTML taught us to:

  • Paint with HTML, mixing structure and display (font elements, b, i, em, blockquote to indent, layout tables)
  • Inline styles or style blocks in the body of the document
  • Make the least effort to get to a visual result.

Sometimes these make sense to still use. HTML emails come to mind. But the limitation of an environment shouldn’t dictate what we use. Inline styles are at best a maintainability issue and at worst a security attack vector (f.e. IE filters).

The least effort thing is one that sticks the most. Want proof? How many images do you encounter without any proper alternative text? As alternative text isn’t displayed, it feels like extra effort to a lot of people. Title attributes, on the other hand, get quite some usage as they do show as a tooltip. I even remember people using ASCII art in titles.

In essence, we learned to write HTML and CSS to style a single element, which is exactly opposite to what CSS is good at.

These examples are from a long time ago, but things haven’t changed much. These days we are bombarded with “CSS only solution” and “one DIV paintings”, all mixing structure and presentation. They are impressive, and I love the creativity they represent and the efforts people get into. But as a first example for a learner of the web, they are as distracting as they are ill-advised.

HTML isn’t about look and feel. It is about structure. It means giving text content structure and link the web. We should use HTML elements not because how they look. We should use them for what they represent. And what functionality they should trigger in a browser.

  • Headings allow assistive technology to provide a faster way to navigate the document
  • Paragraphs chunk up text into easier digestible parts
  • Lists group similar things together
  • Buttons allow you to trigger script functionality independent of input device
  • Form fields and labels allow to user to enter information and know what they should enter even if they can’t see
  • Links point to other resources and should send the user there when activated
  • Video and audio elements give us an accessible player interface

And so on. This, together with an HTML document to host the structure makes up a web page. It allows browsers to show it as a web page. It allows assistive technology to convert it to sound or information on a Braille keyboard.

The problem is, that this isn’t exciting for most people. When I started to explain HTML, I used this document:

<!DOCTYPE html>
 ^ This tells a browser or any other software that this is an HTML 
 document and that it should understand it as such and do things 
 with the content. For example, provide an interactive button 
 when it gets a <button> element.
<html lang="en">
 ^ You need to define the language to help people listening to your
 web site to get the proper voice to pronounce it. This also helps
 search engines to index your content.
 ^ Everything in the <head> is extra information for the document 
   or links to other resources it needs (like a stylesheet or an    
   icon). Nothing in here shows up in the web site as content 
 <meta charset="UTF-8">
 ^ It is important to define the charset you use to make sure that 
 special characters you might use, like "ü" display the right way 
 <meta name="viewport" content="width=device-width, initial-scale=1.0">
 ^ This meta definition makes sure that your web site scales on 
 mobile devices and doesn't need the user to zoom in first 
 <title>My web site</title>
 ^ This is the title of the document. It shows up in the title bar
 of the browser, when you bookmark or share it and it will be 
 the thing a search engine displays about your document.
 ^ This is where the content of the document goes

The comments are what I told my friend how and why these things need to be in there. I am pretty sure that none of us learned HTML that way. And that’s a shame. As, for example, the amount of web sites I encounter each day without a scaling meta tag is staggering. And that makes them annoying on a phone. Often these are signup pages for Wireless, which exacerbates the problem.

It was tough going starting this way to talk about HTML. The assumption was that you could learn some HTML elements and then go into styling things. The information what an HTML document needs to even allow the browser do its thing isn’t exciting.

But it is important. Not teaching HTML by explaining what it means and does results in people re-inventing it.
We’ve all seen DIV/SPAN constructs that are, in essence, an anchor. And they fail to be keyboard accessible. Then people add some ARIA and a tabindex to make them accessible. Well, not really. It is more important to not flag an automated accessibility test fail than making it accessible. We keep getting back into the “add whatever is needed to get to the result quickly” mode. The mode we learned when we got introduced to HTML/CSS as a technology stack to paint on the web.

In the best of worlds, we could get people excited with a visual first approach and get them to learn more later on. But that is a huge assumption. I’ve seen the shortcut mentality and reliance on browsers to be forgiving as the new normal. And every time we question it, there is an argument that we should get people excited first and rely on them to understand later. The state of the web right now makes me question the understanding part though.

I don’t want to take the fun out of learning the web. I want people to reach a result quickly. Tools help there. I got my friend to use Visual Studio Code with the Webhint extension and CodePen. That way my friend createded something fast and learned about CSS and HTML at the same time. Code autocompletion, linting and live insights are a powerful combination. And maybe that is a good way to get started.

More to come.

Seven ways to test for accessibility of your web site with browser Developer Tools

Monday, January 11th, 2021

There are lots of ways to test your web site for accessibility issues. Services, software packages, even human testing companies. They all have their place and often a test with real people is the best thing to do. However, for a quick, preliminary test you don’t need to install anything or pay for a service. Browsers come with developer tools built-in and these have great accessibility testing features. Here’s a list of the ones in the Developer Tools of browsers like Microsoft Edge and Google Chrome:

Issues Panel

The issues panel shows all kind of problems with the current web page. You can either open it directly or click the issues icon top right (the blue speech bubble). Issues are also announced in the Console.

In the panel you then navigate to the “Accessibility” section and can see if there are any problems. Each reported issue comes with an explanation what is wrong and why and a link to the parts of the developer tools where you can inspect and fix the problem.

The issues panel is powered by Webhint, a service that checks for all kind of problems (and NPM package in case you want to use it in your CI/CD workflow).

Issues Panel with accessibility parts highlighted

Element picker with accessibility info overlay

Information shown in the elements overlay

When you have the developer tools open, you can use the element picker tool to highlight and inspect parts of the page. The overlay shows all kind of information:

  • The type of HTML element and class/ID information
  • The dimensions of the element
  • The Text colour
  • The Font used
  • Margin information

In addition to that you also get accessibility information

  • The contrast ratio of foreground to background – if there is enough contrast you can see a green checkmark, otherwise a red exclamation icon
  • The name as announced to assistive technologies like screenreaders
  • The ARIA role
  • An indicator if the element is keyboard focusable or not (either a green checkmark or a grey circle with a line through it)

This information should get you pretty far. Once you found an element with not enough colour contrast you can use the colour picker to fix it.

Colour Picker with contrast check

Once you recognised that some colours on your page have contrast problems, you can use the colour picker of the elements tool to see how to fix them. You open the colour picker by clicking on any of the colour swatches in the CSS of the element.

Picking a colour from the styles pane

In the colour picker you get a section showing the contrast ratio and whether it is compliant with AA or AAA web guidelines or not. For colours that don’t have enough contrast the picker also suggests colours that are compliant. To pick those, just click on the colour swatches.

Colour picker showing possible colours with enough contrast

The colour picker also shows two lines over the colour selection box that indicate AA or AAA compliance. That way you can see if quickly if your desired colour falls into any of those sections.

However, there is an issue with this algorithm as it doesn’t take into consideration the font weight of a certain font but only the contrast ratio of foreground and background colour. A lightweight font with enough contrast may yet be unreadable. This is why the current contrast algorithm will soon be replaced by one that takes this into consideration and you can turn the new one on in the developer tools settings.

Turning on the new contrast algorithm

Once you turned the experiment on and reloaded developer tools, the colour picker contrast section looks different and only shows one line for valid colours.

The colour picker with the new contrast options showing only one line in the colour field

Vision Deficit Emulation

The way you perceive your web site isn’t how the world perceives it. This is why we added an option to emulate different vision deficiencies. Using this feature in the rendering pane of the developer tools you can see what your product looks like for different users:

Simulating different visual deficiencies using devtools

  • Blurred vision – The user has difficulty focusing on fine details.
  • Protanopia – The user is unable to perceive any red light.
  • Deuteranopia – The user is unable to perceive any green light.
  • Tritanopia – The user is unable to perceive any blue light.
  • Achromatopsia – The user is unable to perceive any color, which reduces all color to a shade of grey.

Choose from one of the options and the site will automatically change colours.

Showing fruit pictures with simulated colour deficits

Rendering Emulation (dark, light, high contrast, reduced motion)

The rendering view also has a few features to simulate different operating system settings. One of them is to simulate reduced motion which is great to ensure that your animations are not shown to users that don’t want or can’t cope with animations on the web.

You can also switch from dark to light mode without having to change the settings of your computer.

Soon you will also be able to simulate high contrast mode without having to change your operating system. I’ll blog more about this soon.

Reddit in simulated forced colour mode

Accessibility Tree

The accessibility pane of the developer tools also shows the accessibility tree of the document. This is different to what you see in the elements pane but is what assistive technology gets about your document. Using the accessibility tree you can check if an element is announced the way it should be to, for example, screen readers.

Source Order Viewer (experimental)

Using CSS you can change the order of elements how they appear on the screen. This change, however, is not part of the source code of the document and thus will not be announced by assistive technology like screen readers. By using the Source Order Viewer the developer tools will overlay the order number of each element in the browser and you can see if they are not only shown visually close to another but also for non-sighted users or search engines.

Source order viewer overlaying the order on HTML elements to show problems in the reading order

More to come

We are constantly working on improving the accessibility tooling in the developer tools and are open to feedback. Feel free to contact me or talk to @EdgeDevTools on Twitter.

Back to basics: adding a playback speed control to an audio player

Monday, December 28th, 2020

Currently I am publishing the new Developer Advocacy Handbook chapter by chapter and I also started recording audio versions of each chapter. A common request of people is to be able to control the speed of a audio/video recording. Luckily enough, using basic HTML5 and JavaScript, this is not that hard to achieve, so let’s do it.

You can see and hear the result in the following video:

You can check the final version on Codepen

See the Pen
Audio with speed control
by Christian Heilmann (@codepo8)
on CodePen.

The best start is always sensible HTML.

<div class="speedcontrolcontainer">
  <audio src="https://file-examp…xample_MP3_2MG.mp3" controls></audio>
    <label for="pbrate">Speed:</label>
    <input type="range" id="pbrate" min=.5 max=3 value=1 step=.1>

In this case, using the audio element with a controls attribute already gives us a play button, a scrubber, time display, volume and an overflow menu with download option. For the speed control, we need a slider with a label to keep it accessible and a span element to show the current value of the playback speed. The range control is good for this as it also gives us keyboard access and allows us to limit the speeds we want to allow. In this case, from half speed to four times the speed.

Let’s only go into basic CSS for styling here:

.scc {
  max-width: 30em;
  display: block;
  border: 1px solid #000;
  padding: 10px;
  font-family: Sans-serif;
.scc audio { 
  width: 100%; display: block; 
.scc div {
  display: flex;
  padding: .5em 0;
  gap: 5px;
.scc label { flex: 1; }
.scc input[type="range"] { flex: 5; }
.scc span {
  flex: 1;
  text-align: center;

We give the control a container element and set it to a max-width of 30em. We display the audio element as block and make it use up all the available space. We give the new speed control a bit of padding and use flexbox to lay it out.

The last bit is to use JavaScript to create the rest of the functionality:

const audio = document.querySelector('.scc  audio');
const playbackrate = document.querySelector('.scc input');
const display = document.querySelector('.scc span');
const displayvalue = val => {
  return parseInt(val * 100, 10) + '%'
display.innerText = displayvalue(audio.playbackRate);
playbackrate.addEventListener('change', e => {
  audio.playbackRate = playbackrate.value;
  display.innerText = displayvalue(playbackrate.value);

  • We get references to the audio element, the slider and the span we want to display the current value of the slide in.
  • As the playback speed in the audio API is a number, we need to convert it to a percentage. This is what the displayvalue method does for us.
  • We display the current playbackRate of the audio element in the span next to the slider.
  • We apply an event Listener to the input element that fires every time it changed.
  • When there is a change, we set the playbackRate of the audio element to the value of the slider
  • And we display the percentage in the span

One thing that is missing is that users may want to have their favourite speed stored so they don’t have to set it again chapter by chapter. To do this, we can use localStorage, set the playbackRate and value when we load the document and store it when the slider changed.

const audio = document.querySelector('.scc  audio');
const playbackrate = document.querySelector('.scc input');
const display = document.querySelector('.scc span');
const displayvalue = val => {
  return parseInt(val * 100, 10) + '%'
if (window.localStorage.pbspeed) {
  audio.playbackRate = window.localStorage.pbspeed;
  playbackrate.value = window.localStorage.pbspeed;
display.innerText = displayvalue(audio.playbackRate);
playbackrate.addEventListener('change', e => {
  audio.playbackRate = playbackrate.value;
  display.innerText = displayvalue(playbackrate.value);
  window.localStorage.pbspeed = playbackrate.value;

And that’s it! An audio playback control with playback speed option.

The new and improved Developer Advocacy Handbook is out – Read Chapters 1 & 2 now

Thursday, December 17th, 2020

Eleven years ago I wrote the Developer Evangelism Handbook and since then it helped a lot of of people start a career in Developer Relations. Now a publisher approached me if they could do a print version. The negotiations are still underway, but I was sure that it is not a good plan to release the book in the state it is in. So I spent the last few days cleaning up. I edited the chapters and removed outdated examples or links to defunct products. And I added new content that is more relevant to today’s world of developer advocacy. I will release chapters bit by bit over the next weeks. Once all is live I will open the GitHub repository for contribution and feedback.

For now, head over to and start with chapter one and two.

The book showing in the browser

You can see the whole Table of Contents and the chapters will light up as they become available.

I wrote the new book in Markdown and I am using GitHub Pages to publish and host it. It was a fun exercise to learn Jekyll and I am still in the middle of it. The book has an automatic dark/light theme detection and should be nice to read across all kind of form factors. Once done, I will also add a serviceworker and such to make the book installable. I am also considering generating an ebook version if you are interested.

Quick Tip: How to capture and replace newlines in liquid for Jekyll/GitHub Pages

Thursday, December 17th, 2020

As part of the re-write of The Developer Advocacy Handbook I needed to have blockquotes with different classes.

Fact display in the book

With GitHub Pages markdown you can do that using the > notation:

> **Fact:** There are no bad students or a bad audience --
only bad workshops and talks. Your mood, dedication and
enthusiasm do become those of the audience -- 
if you are not happy, they won\'t be happy.

The problem was that I couldn’t add CSS classes to the blockquote elements so I can show them in different styles. I wanted four of them: example, fact, warning and tip.

The good news is that even without any styling using the strong name should make it obvious. But as there is no text “contains” selector in CSS I couldn’t rely on that to change the blockquote element.

First solution: JavaScript

The first thing I thought was to use JavaScript to apply the classes, which is pretty straight forward:

let bqs = document.querySelectorAll('blockquote');
  bqs.forEach(b => {
  b.className = b.

This, however, felt dirty and I wanted to use the system itself to do that task.

Moving to liquid

So I wrote a liquid include to convert the HTML before rendering. In my layout template, this works by replacing the {{ content }} with {% include blockquotes.html html=content %}.

In the blockquotes.html, I thought it’d be easy to do a search and replace. Alas, there is the issue that liquid only does string replacement and doesn’t know any regular expressions.

The HTML generated from the markdown has a line-break in it:

  <p><strong>Fact:</strong> There are no bad students … </p>

This is where it didn’t get fun. The replace filter doesn’t allow you to concatenate strings and doesn’t know the concept of n. So, I tried to use the newline_to_br together with strip_newlines filters and then replace the br but it was messy.

Turns out, the main trick was to capture a newline in liquid and assemble the string to replace using that one.

{% capture newline %}
{% endcapture %}
{% capture tip %}<blockquote>{{newline}}  <p><strong>Tip:</strong>{% endcapture%}
{% capture example %}<blockquote>{{newline}}  <p><strong>Example:</strong>{% endcapture%}
{% capture warning %}<blockquote>{{newline}}  <p><strong>Warning:</strong>{% endcapture%}
{% capture fact %}<blockquote>{{newline}}  <p><strong>Fact:</strong>{% endcapture%}
{% assign newhtml = include.html | 
  replace: tip, '<blockquote class="tip"><p><strong>Tip:</strong>' |
  replace: example, '<blockquote class="example"><p><strong>Example:</strong>' |
  replace: warning, '<blockquote class="warning"><p><strong>Warning:</strong>' |
  replace: fact, '<blockquote class="fact"><p><strong>Fact:</strong>' 
{{ newhtml }}

This works fine. Seems superfluous, but it is a way. It might be that there is a much simpler way as I am new to this world, having used PHP before the build the old version of the book. Got a better option? Tell me :)