This article explains how I build "Categories" page on this site. Nothing special, really.

First version

First version of 'Categories' (or 'tags') page on this site was build with flex-box layout. Every "box" had a category logo, category name and amount of articles in that category. Unfortunately, that doesn't work well for me. Not only that it turns out to be hard to maintain (need to add new image for every new category), but there are also legal issues. Sometimes, logo's are copyrighted and you can not use them without permission. Sometimes, you must use logo as is, without converting it to single color version (looks better then grid with 20+ logos in different colors).

CSS columns

After some thinking, I figured out that something like a alphabetic index works best for this and the best way to do it is with CSS columns. Here is how it looks like in Middleman erb template:

  <div id="category-list">
    <% ('A'..'Z').to_a.each do |letter| -%>
      <% letter_hash = blog.tags.select {|k,v| k[0].upcase == letter} %>
      <% unless letter_hash.keys.empty? %>
        <div>
          <h2><%= letter -%></h2>
          <ul>
            <% letter_hash.each do |tag, articles| %>
              <li>
                <%= link_to "#{tag} (#{articles.size})", tag_path(tag),
                    class: "category category-"+tag.downcase.gsub(' ','_') %>
             </li>
            <% end %>
          </ul>
        </div>
      <% end %>
    <% end -%>
  </div>

Here is final SCSS:

div#category-list {
  columns: 275px;
  column-gap: 20px;
  margin: 0 15px 20px 50px;

  div {
    margin: 0 0 20px 0;
    padding: 30px 0 0 0;
    break-inside: avoid;
    min-height: 8rem;
    display: flex;

    h2 {
      z-index: 1;
      content: attr(data-letter);
      font-size: 6rem;
      line-height: 6rem;
      display: block;
      width: 105px;
      margin: 0;
      padding: 0;
      color: $simplify-grey;
      font-weight: 900;
      opacity: 0.10;
    }

    ul {
      z-index: 2;
      list-style: none;
      margin: 10px 0 0 -75px;
      padding: 0;

      a {
        display: block;
        font-size: 1.5rem;
        font-weight: 600;

        &:hover {
          text-decoration: underline;
        }
      }
    }
  }
}

Most important parts:

  • By specifying columns in pixel I'm sure that browser will calculate number of columns on the page and I don't need to use any @media queries for this.
  • Every div block will not brake when browser create columns on the page.
  • Every div block have minimal height of 8 rem's. Reason for this is that letter indicator size is 6rem and display is set to flex. In all browsers except IE/Edge 7rem was enough.
  • h2 element is set behind the list with z-index, and with opacity of 0.1 for better effect. Width is 105px, a bit wider then letter 'W'.
  • List have negative left margin and is positioned above h2 element.
  • Link in the list is a bit bigger then normal text on the site, and typeface is bold.
Share on: