Static Checklist

  • Create CSS for static checklists
  • Create facebook share image for static checklists
  • Write blog post about static checklist CSS

I decided that the next feature I wanted on my blag was the ability to add static checklists. I had an initial concept for how I wanted them to work. It revolved around using [data-*] attributes to specify the checked state of list items, which would override the default list item styling.

My plan was to use <li data-checked> to specify that the item was to be checked off. I could then use [data-checked="true"] and [data-checked="false"] to show the empty checkbox or checked checkbox.

<ul>
    <li data-checked="true">Item 1</li>
    <li data-checked="false">Item 2</li>
</ul>

Writing the CSS took a bit more effort. I considered trying to leverage list-style-image, but I wasn't fond of the idea of needing to generate my own images. Worse yet, I'd need to scale the images to work for different font sizes.

Generated content seemed like the right way to go, the difficulty was figuring out how to render a checkbox from a font. I considered using an icon font, but decided that I'd rather not import another dependency into my website just for silly little checkboxes. With that I decided to google the unicode characters for checkboxes. As it turns out U+2610 and U+2611 represent "ballot box" and "ballot box with check" respectively. That was easy.

The next step was to get the styles working. I needed to disable the default list styles and add the generated content before each of the list items:

li {
    &[data-checked] {
        list-style: none;
    }

    &[data-checked="false"]:before {
        content: '\2610';
    }

    &[data-checked="true"]:before {
        content: '\2611';
    }
}

This worked great for making the checkboxes visible, but they were pushing the rest of the contents of the li elements off to the right. The next trick was to absolutely position the checkboxes off to the left of the content, which turned the code into:

li {
    &[data-checked] {
        list-style: none;
        position: relative;

        &:before {
            position: absolute;
            right: 100%;
        }
    }

    &[data-checked="false"]:before {
        content: '\2610';
    }

    &[data-checked="true"]:before {
        content: '\2611';
    }
}

The li elements needed to be the relative parents of the :before pseudo element if the positioning was going to work right. right: 100% was used so that the right side of the checkbox was always exactly flush with the left side of the contents. This is especially important when the font-size is scaled. Unfortunately this left the checkboxes a little too close to the content still, so I added a small static push to align the checkboxes with the standard list item bullets.

The finalized code that I'm using is:

li {
    &[data-checked] {
        list-style: none;
        position: relative;

        &:before {
            margin-right: 7px;
            position: absolute;
            right: 100%;
        }
    }

    &[data-checked="false"]:before {
        content: '\2610';
    }

    &[data-checked="true"]:before {
        content: '\2611';
    }
}

Not bad for 20 lines of LESS.