Re-Working the CSS Almanac
Publikováno: 19.9.2024
Getting right to it: the CSS-Tricks Almanac got a big refresh this week!
I’m guessing you’re already familiar with this giant ol’ section of CSS-Tricks called the Almanac. This is where we publish references for CSS selectors and properties…
Re-Working the CSS Almanac originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.
Getting right to it: the CSS-Tricks Almanac got a big refresh this week!
I’m guessing you’re already familiar with this giant ol’ section of CSS-Tricks called the Almanac. This is where we publish references for CSS selectors and properties. That’s actually all we’ve published in there since the beginning of time… or at least since 2009 when most of the original work on it took place. That might as well be the beginning of time in web years. We might even call it Year 1 BR, or one year before responsive.
You don’t need me telling you how different writing CSS is today in the Year 14 AR. Quite simply, the Almanac hasn’t kept pace with CSS which is much, much more than properties and selectors. The truth is that we never really wanted to touch the Almanac because of how it’s configured in the back end and I’m pretty sure I spotted a ghost or two in there when I poked at it.
Visiting the Alamanc now, you’ll find a wider range of CSS information, including dedicated sections for pseudo class selectors, functions, and at-rules in addition to the existing properties and selectors sections. We’ve still got plenty of work to do filling those in (you should help!) but the architecture is there and there’s room to scale things up a little more if needed.
The work was non-trivial and as scary as I thought it would be. Let me walk you around some of what I did.
The situation
We’re proudly running WordPress and have since day one. There’s a lot of benefit to that, especially as templating goes. It may not be everyone’s favorite jam, but I’m more than cool with it and jumped in — damn the torpedoes!
If you’re familiar with WordPress, then you know that content is largely sliced up into two types: pages and posts. The difference between the two is fairly minimal — and nearly indistinguishable as they both employ the same editing interface. There are nuances, of course, but pages are largely different in that they are hierarchal, meaning they’re best for establishing parent-child page relationships for a nicely structured sitemap. Posts, meanwhile, are more meta-driven in the sense that we get to organize things by slapping tags on them or dropping them into a category group or whatever custom taxonomy we might have in reach.
The Almanac is built on pages, not posts. Pages are boss at hierarchy, and the Alamanc is a highly structured area that has a typical sitemap-like flow, and it just so happens to follow alphabetical order. For example, an entry for a CSS property, let’s say aspect-ratio
, goes: Almanac → Properties → A → Aspect Ratio.
That doesn’t sound like a bad thing, right? It’s not, but pages are tougher to query in a template than posts, which have a lot more meta we can use for filtering and whatnot. Pages, on the other hand, not so much. (Well, not obviously so much.) They’re usually returned as structured objects because, you know, hierarchy. But it also means we have to manually create all of those pages, unlike tags and categories that automatically generqte archives. It feels so dang silly creating an empty page for the letter “A” that’s a child of the “Properties” page — which is a child of the Almanac itself — just so there’s a logical place to insert properties that begin with the letter A. And that has to happen for both properties and selectors.
The real problem is that the Almanac simply tapped out. We want to publish other CSS-y things in there, like functions and at-rules, but the Almanac was only ever built to show two groups. That’s why we never published anything else. It’s also why general selectors and pseudo-selectors were in the same bucket.
Expanding the place to hold more content was the scope I worked with, knowing that I’d have some chances to style things along the way.
One template to rule them all
That’s how things were done. The original deal was a single template used for the Almanac index and the alphabetical pages that list selectors and properties. It was neat, really. The page first checked if the current page is the Almamac page that sits atop the page hierarchy. If it is that page, then the template spits out the results for selectors and properties on the same page, in two different columns.
The query for that is quite impressive.
<?php
function letterOutput($letter, $selectorID, $propertyID) {
$selector_query = new WP_Query(array(
'post_type' => 'page',
'post_status' => 'publish',
'post_parent' => $selectorID,
'posts_per_page' => -1,
'orderby' => 'title',
'order' => "ASC"
));
$html = '<div class="almanac-group">';
$html .= '<div class="group-letter"><a id="letter-' . $letter . '">' . $letter . '</a></div>';
$html .= '<div class="group-list">';
while ($selector_query->have_posts()) : $selector_query->the_post();
$html .= '<details id="post-' . get_the_id() . '" class="link-item"><summary>';
$html .= '<h2><code>';
$html .= get_the_title();
$html .= '</code></h2>';
$html .= '</summary>';
$html .= get_the_excerpt();
$html .= '<pre rel="CSS" class="almanac-example"><code class="language-css">';
$html .= get_post_meta(get_the_id(), 'almanac_example_code', true);
$html .= '</code></pre>';
$html .= '<a class="button" href="' . get_the_permalink() . '">Continue Reading</a>';
$html .= '</details>';
endwhile;
$html .= "</div>";
$html .= "</div>";
return $html;
}
That’s actually half the snippet. Notice it’s only marked up for a $selector_query
. It loops through this thing again for a $property_query
.
From there, the function needs to be called 26 times: one for each letter of the alphabet. It takes three parameters, namely the letter (e.g. A
) and the page IDs for the “A” pages (e.g. 14146, 13712
) that are children of the selectors and properties.
<?php
echo letterOutput("A", 14146, 13712);
// B, C, D, E F, G, etc.
?>
And if we’re not currently on the index page? The template spits out just the alphabetical list of child pages for that particular section, e.g. properties. One template is enough for all that.
Querying child pages
I could have altered the letterOutput()
function to take more page IDs to show the letter pages for other sections. But honestly, I just didn’t want to go there. I chose instead to reduce the function to one page ID argument instead of two, then split the template up: one for the main index and one for the “sub-sections” if you will. Yes, that means I wound up with more templates in my WordPress theme directory, but this is mostly for me and I don’t mind. I can check which sub-page I’m on (whether it’s a property index, selector index, at-rules index, etc.) and get just the child pages for those individually.
The other trouble with the function? All the generated markup is sandwiched inside a while()
statement. Even if I wanted to parse the query by section to preserve a single template architecture, it’s not like I can drop an if()
statement anywhere I want in there without causing a PHP fatal error or notice. Again, I had no interest in re-jiggering the function wholesale.
Letter archives
Publishing all those empty subpages for the letters of each section and then attaching them to the correct parent page is a lot of manual work. I know because I did it. There’s certainly a better, even programmatic, way but converting things from pages to posts and working from that angle didn’t appeal to me and I was working on the clock. We don’t always get to figure out an “ideal” way of doing things.
It’s a misnomer calling any of these letter pages “archives” according to WordPress parlance, but that’s how I’m looking at the child pages for the different sections — and that’s how it would have been if things were structured as posts instead of pages. If I have a section for Pseudo-Selectors, then I’m going to need individual pages for letters A through Z that, in turn, act as the parent pages for the individual pseudos. Three new sections with 26 letters each means I made 78 new pages. Yay.
You get to a letter page either through the breadcrumbs of an Almanac page (like this one for the aspect-ratio
property) or by clicking the large letter in any of the sections (like this one for properties).
We’ve never taken those pages seriously. They’re there for structure, but it’s not like many folks ever land on them. They’re essentially placeholders. Helpful, yes, but placeholders nonetheless. We have been so unserious about these pages that we never formally styled them. It’s a model of CSS inheritance, tell you what.
This is where I took an opportunity to touch things up visually. I’ve been working with big, beefy things in the design since coming back to this job a few months ago. Things like the oversized headings and thick-as-heck shadows you see.
It’s not my natural aesthetic, but I think it works well with CSS-Tricks… and maybe, just maybe, there’s a tear of joy running down Chris Coyier’s face because of it. Maybe.
Navigating
Another enhancement was added to the navigation displayed on the main index page. I replaced the alphabetical navigation at the top with a nav that takes you to each section and now we can edit the page directly in WordPress without having to dev around.
The only thing that bothers me is that I hardcoded the dang thing instead of making it a proper WordPress menu that I can manage from the admin. [Adds a TODO to his list.]
Since I freed up the Alamanc index from displaying the selector and property lists in full, I could truly use it as an index for the larger number of sections we’re adding.
There may be a time when we’ll want to make the main page content less redundant with the navigation but I’ll take this as a good start that we can build up from. Plus, it’s now more consistent with the rest of the “top-level” pages linked up in the site’s main menu as far as headers go and that can’t be bad.
Oh yeah, and while we’re talking about navigating around, the new sections have been added to the existing left sidebar on individual Almanac pages to help jump to other entries in any section without having to return to the index.
Quickly reference things
The last enhancement I’ll call out is minor but I think it makes a positive difference. If you head over to any subpage of the index — i.e., Selectors, Properties, Pseudos, Functions, At-Rules — a snippet and high-level definition is available for each item at the ready without having to jump to the full page.
We’ve always been big on “get to the examples quickly” and I think this helps that cause quite a bit.
“You could’ve also done [x]…”
Yeah, lots more opportunities to tighten things up. The only goal I had in mind was to change things up just enough for the Almanac to cover more than selectors and properties, and maybe take some styling liberties here and there. There’s plenty more I wanna do and maybe we’ll get there, albeit incrementally.
What sort of things? Well, that hardcoded index navigation for one. But more than that, I’d like to keep pushing on the main page. It was serving a great purpose before and I pretty much wiped that out. It’d be good to find a way to list all of the entries — for all sections — the way we did when it was only twe sections. That’s something I plan to poke at.
And, yes, we want to cover even more CSS-y items in there, like general terminology, media and user preference queries, possibly specifications… you get the idea. The Almanac is a resource for us here on the team as much as it is for you, and we refer to it on the daily. We want it flush with useful information.
That’s all.
You can stop reading now and just head on over to the Almanac for a virtual stroll.
Re-Working the CSS Almanac originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.