Posts in the Tech Category

Specificity in :not(), :has(), and :matches()

Published 2 weeks, 5 days ago

A few years back, I wrote a short post on specificity, element proximity, and the negation pseudo-class.  Everything in it is still accurate and relevant, but I have some updates to share.

First off, I’d like to clarify something that some people may have found confusing.  In that post, I said:

But it turns out that the negation pseudo-class isn’t counted as a pseudo-class.

That might leave some people with the idea that the entire negation portion of the selector is ignored for the purposes of specificity, especially if you don’t speak spec.

So consider the following:

div:not(.one) p

In order from left to right, that’s an element selector (div), a negation pesudo-class (:not) a class selector (.one), and another element selection (p).  Two element selectors and one class selector are counted towards the specificity, yielding a total of 0,0,1,2.  That’s the same specificity as div.one p, though the two selectors select very different things.

In Ye Olden Days, that was easy enough to work out, because :not() could only ever contain a simple selector.  Things are looking to get more complicated, however—:not() is set to accept grouped selectors.  So we will at some point be able to say:

div:not(.one, .two, #navbar) p

So any p element that is not descended from a div that has a class containing either one or two (or both), or that has an id of navbar, will be selected.

But how do we calculate the specificity of that whole selector?  Just add up all the pieces?  No.  The Working Group recently decided that the specificity contributed from inside a :not() will be equal to the single selector with the highest specificity.  So given div:not(.one, .two, #navbar) p, the #navbar will contribute 0,1,0,0 to the overall specificity of the selector, yielding a total of 0,1,0,2.  The specificities of .one and .two are ignored.

This same approach will be taken with the :has() and :matches() pseudo-classes.  Thus we get the following:


:matches(nav, header, footer#pageend) a[href] {color: silver;}  /* 0,1,1,2 */
article:has(a.external, a img)  /* 0,0,1,2 */
input:not([type="radio"], [type="checkbox"])  /* 0,0,1,1 */

In the first instance, the bits that are added together are footer#pageend and a[href], so that’s one ID, one attribute, and two elements.  In the second, it’s article and a.external for one class and two elements.  And last, we add up input and either of the [type=""] attribute selectors, since their specificities are equal, which means we add up one attribute and one element.

There is still, so far as I’m aware, no concept of DOM-tree proximity in CSS.  I would still continue to wager that will remain true, though I’d put a fair bit less money down now than I would have six years ago.


Displaying CSS Breakpoint Information with Generated Content

Published 4 months, 1 day ago

In the course of experimenting with an example design for my talks at An Event Apart this year, I came up with a way to keep track of which breakpoint was in force as I tested the design’s responsiveness.  I searched the web to see if anyone else had written about this and didn’t come up with any results, so I’ll document it here.  And probably also in the talks.

What I found was that, since I was setting breakpoints in ems instead of pixels, the responsive testing view in browsers didn’t really help, because I can’t maintain realtime mapping in my head from the current pixel value to however many rems it equals.  Since I don’t think the browser has a simple display of that information, I decided I’d do it myself.

It starts with some generated content:

body::before {content: "default";
   position: fixed; top: 1px; right: 1px; z-index: 100; padding: 1ch;
   background: rgba(0,0,0,0.67); color: rgba(255,255,255,0.75);
   font: bold 0.85rem Lucida Grande, sans-serif;}

You can of course change these to some other placement and appearance.  You can also attach these styles to the html element, or your page wrapper if you have one, or honestly even the footer of your document—since the position is fixed, it’ll be viewport-relative no matter where it originates.  The real point here is that we’re generating a bit of text we can change at each breakpoint, like so:

@media (max-width: 38em) {
   body::before {content: "<38em";}
   /* the rest of the breakpoint styles here */
}
@media (max-width: 50em) {
   body::before {content: "<50em";}
   /* the rest of the breakpoint styles here */
}
@media (min-width: 80em) {
   body::before {content: ">80em";}
   /* the rest of the breakpoint styles here */
}

The labels can be any string you want, so you can use “Narrow”, “Wide”, and so on just as easily as showing the measure in play, as I did.

The downside for me is that we automatically can’t make the labels cumulative in native CSS.  That means the order the @media blocks appear will determine which label is shown, even if multiple blocks are being applied.  As an example, given the styles above, at a width of 25em, the label shown will be <50em even though both the 38em and 50em blocks apply.

There are ways around this, like switching the order of the max-width blocks so the 38em block comes after the 50em block.  Or we could play specificity games:

@media (max-width: 38em) {
   html body::before {content: "<38em";}
   /* the rest of the breakpoint styles here */
}
@media (max-width: 50em) {
   body::before {content: "<50em";}
   /* the rest of the breakpoint styles here */
}

That’s not a solution that scales, sadly.  Probably better to sort the max-width media blocks in descending order, if you think you might end up with several.

The upside is that it’s easy to find and remove these lines once the development phase moves to QA.  Even better, before that point, you get a fully customizable in-viewport indication of where you are in the breakpoint stack as you look at the work in progress.  It’s pretty trivial to take this further by also changing the background color of the little box.  Maybe use a green for all the block above the “standard” set, and a red for all those below it.  Or toss in little background image icons of a phone or a desktop, if you have some handy.

So that’s the quick-and-dirty little responsive development hack I came up with this morning.  I hope it’s useful to some of you out there—and, if so, by all means share and enjoy!


Addendum: Emil Björklund proposes a variant approach that uses CSS Custom Properties (aka CSS variables) to implement this technique.


“CSS Pocket Reference, 5th Edition” to Production

Published 4 months, 1 week ago

Just over an hour before I started writing this post, I handed off CSS Pocket Reference, 5th Edition to the Production department at O’Reilly.  What that means, practically speaking, is that barring any changes that the editors find need to be made, I’m done.

Besides all the new-new-NEW properties included in this edition (flexbox and grid being just two of the most obvious examples), we put a lot into improving the formatting for this edition.  Previous editions used a more sprawling format that led to the 4th edition getting up to 238 pages, which cast serious shade on the word “Pocket” there in the title.  After all, not all of us live in climates or cultures where 24/7 cargo pants are a viable option.

So with a few ideas from me and several more from the production team, we managed to add in all the new properties and still bring the page count down below 200.  My guess is the final copy will come in about 190 pages, but much will depend on how crazy the indexer gets, and how much the formatting gets changed in the final massaging.

We don’t have a firm release date yet; I’m pulling for April, but it’s really not up to me.  I’ll make announcements via all the usual channels when pre-order is available, and of course when publication day arrives.

For now, for the first time in many years, I don’t have a book project on my to-do list.  I don’t even have a book proposal on my to-do list.  It’s a slightly weird feeling, but not an unwelcome one.  I’ll be putting the extra time into my content for An Event Apart: I’m giving a talk this year on using the new CSS tools to make our jobs easier, and doing Day Aparts in Boston and San Francisco where I spend six hours diving deep into guts of stuff like flexbox Grid, writing modes, features queries, and a whole lot more.

So my time will continue to be fully spoken for, is what I’m saying.  It’ll all be fun stuff, though, and it’s hard to ask for more out of my work.


Headings and Labels

Published 5 months, 5 days ago

Following on my last two posts about accessibility improvements to meyerweb, I’ve made two more adjustments: better heading levels and added ARIA labels.

For the heading levels, the problem I face is one familiar to many authors: what makes sense as an <h1> in some situations needs to be an <h2> in others.  The most common example is the titles of blog posts like this one.  On its permalink page, the title of the page is the title of the post.  There, it should be an <h1>.  On archive pages, including the home page of meyerweb, there are a number of posts shown one after the other.  In those situations, each post title should be an <h2>.

Part of the redesign’s changes were to write a single PHP routine that generated posts and their markup, which I could then simply call from wherever.  So I added an optional function parameter that allowed me to indicate the context in which a post was being placed.  It goes something like this:

<?php blogpostMarkup("archive"); ?>
function blogpostMarkup($type = "standalone") {
    if ($type == "archive") $titletag = "h2"; else $titletag = "h1";
    // …markup is all generated here…
    echo $output;
}

Or code to that effect.  (I did not go copy-paste from my actual code base.)

So now, heading levels are what they should be, at least on most pages (I may have missed updating some of my old static HTML pages; feel free to point them out in the comments if you find one).  As a part of that effort, I removed the <h1> from the masthead except on the home page, being the one place it makes sense to be an <h1>.

As for ARIA labels, that came about due to a comment from Phil Kragnes on my last post, where he observed that pages often have multiple elements with a role of navigation.  In order to make things more clear to ARIA users, I took Phil’s suggestion to add aria-label attributes with clarifying values.  So for the page-top skiplinks, I have:

<nav role="navigation" aria-label="page" id="skiplinks">

Similarly, for the site-navigation bar, I have:

<nav role="navigation" aria-label="site" id="navigate">

The idea is that screen readers will say “Page navigation region” and “Site navigation region” rather than just repeating “Navigation region” over and over.

Other than cleaning up individual pages’ heading levels and the occasional custom layout fix (e.g., the Color Equivalents Table needed a local widening of the content column’s maximum size), I think the redesign has settled into the “occasional tinkering” phase.  I may do something to spruce up my old Web Review articles (like the very first, written when HTML tags were still uppercase!) and I’m thinking about adding subnavigation in certain sections, but otherwise I think this is about it.  Unless I decide to go really over the top and model my Tools page after Simon St. Laurent’s lovely new Grid design, that is…

Of course, if you see something I overlooked, don’t hesitate to let me know!  I can’t guarantee fast response, but I can always guarantee careful consideration.


Increasing Accessibility

Published 5 months, 1 week ago

Thanks to the fantastic comments on my previous post, I’ve made some accessibility improvements.  Chief among them: adding WAI-ARIA role values to various parts of the structure.  These include:

  • role="banner" for the site’s masthead
  • role="navigation" added to the navigation links, including subnavigation links like previous/next posts
  • role="main" for the main portion of a page
  • role="complementary" for sidebars in the blog archives
  • role="article" for any blog post, whether there are several on a page or just one

In addition, I restored skip links to the masthead of most pages (the rest will get them soon).  The links are revealed on keyboard focus, which I’m not sure I like.  I feel like these aren’t quite where they need to be.  A big limitation is the lack of :matches() (or similar) support in browsers, since I’d love to have any keyboard focus in the masthead or navigation links bring up the skip links, which requires some sort of parent selection.  I may end up using a tiny bit of enhancing Javascript to make the links’ UX more robust in JS situations, but still obviously available if JS fails.  And I may replicate them in the footer, as a way to quickly jump back up the page, especially to the navigation.

Speaking of the navigation links, they’ve been moved in the source order to match their place in the visual layout.  My instincts with regard to source order and layout placement were confirmed to be woefully out of date: the best advice now is to put the markup where the layout calls for the content to be.  If you’re putting navigation links just under the masthead, then put their markup right after the masthead’s markup.  So I did that.

The one thing I didn’t change is heading levels, which suffer all the usual problems.  Right now, the masthead’s “meyerweb.com” is always an <h1> and the page title (or blog post titles) are all <h2>.  If I demoted the masthead content to, say, a plain old <div>, and promoted the post headings, then on pages like the home page, there’d be a whole bunch of <h1>s.  I’ve been told that’s a no-no.  If I’m wrong about that, let me know!

There’s still more to do, but I was able to put these into place with no more than a few minutes’ work, and going by what commenters told me, these will help quite a bit.  My thanks to everyone who contributed their insights and expertise!


How Do I Increase Accessibility?

Published 5 months, 2 weeks ago

I have an accessibility question.  Okay, it’s a set of questions, but they’re really all facets of the same question:

How do I make my site’s structure the most accessible to the most people?

Which sounds a bit broad.  Let me narrow it down.  Here’s the basic layout order of most pages on meyerweb:

  1. Masthead
  2. Navigation (in a <nav> element)
  3. Main page content (in a <main> element), occasionally with a sidebar
  4. Footer (in a <footer> element)

But this is, at the moment, the source order of those pieces:

  1. Masthead
  2. Main page content (in a <main> element), occasionally with a sidebar
  3. Navigation (in a <nav> element)
  4. Footer (in a <footer> element)

The difference is the navigation.  I put it later in the source order because I want those using speaking browsers to be able to get the content quickly, without having to tab through the navigation on every page.

But is that actually a concern, given my use of a <main> element for the main content of the page?  And the <nav> and <footer> elements, do those also help with jumping around the page?  If not, what’s the best-practice structural order for those pieces?

If so, does that mean it’s okay to put the navigation back up there in the source order, and stop doing wacky things with the order element to place it visually where it isn’t, structurally?

I have the same questions for those who use keyboard tabbing of the visual layout, not speaking browsers.  What’s the best way to help them?  If it’s tabindex, how should I order the tabbing index?

And in either case, do I need skip links to get people around quickly?  Do I want skip links?  Do my assistive-technology users want skip links?

Maybe the real question is “Given this layout, and my desire to make getting to main content and other pieces of the page as easy as possible for those who rely on assistive technology, how should I structure and annotate the content to raise the fewest barriers for the fewest people?”

Unless, of course, the real question is one I don’t know enough to ask.

Can you help me out, accessibility hivemind?  I’d really appreciate some expert insight.  All my instincts are more than a decade out of date.


A Newwwyear Livestream

Published 5 months, 4 weeks ago

After I announced my newwwyear plans, a couple of people pinged me to ask if I’d do a Google Hangout or some such so that people could follow along as I live-redesign meyerweb in production.  And I thought, that sounds like the nerdiest Twitch stream ever, but what the heck, why not?

So!  I’m going to livestream at least part of my process over on YouTube Live, spread over a few days.  I picked YTL over Twitch because YouTube will archive the streams to my channel, whereas Twitch only holds onto them for 14 days, and one of my weaknesses is that I’m a data hoarder—I hate to throw away anything digital.  So I’ll keep copies on someone else’s computer.

During the stream, I’ll keep up a running commentary on what I’m doing and why.  This sounds artificial, but honestly, I talk to myself a lot as I work anyway.  I’ll also have the YouTube Live chat window open so people can ask questions about why I do what I do, and I’ll do my best to answer.  Or, you know, we can talk about the latest Star Wars movie or whatever.  I’m not going to put too many limits on it other than A) keep the language professional, since kids starting out in web design might join in; and B) treat everyone in the chat with respect.

My guess is any given stream session will be two hours, tops.  My tentative schedule is:

  1. December 26th (today!) starting around 2000 UTC (3:00pm EST, 12:00n PST)
  2. December 27th, 1830 UTC (1:30pm EST, 10:30am PST)
  3. December 28th, 1900 UTC (2:00pm EST, 11:00am PST)1500 UTC (10:00am EST, 7:00am PST)
  4. December 29th, 1500 UTC (10:00am EST, 7:00am PST)

I don’t have a specific structure for how I’m going to approach this, other than: first I turn off all site-wide styling and put up a note about it, maybe add a few very minimal styles, rework the page structures to be sane without CSS, then start building up a new design.  I have no idea how long each part of that sequence will take (except the “remove site-wide styles” part, that’ll take a few seconds).  But there isn’t a scheduled “Typography Day” or what-have-you: I’ll probably go wandering off on tangents, riffing on happy accidents, fiddling with stuff as I notice it and it bugs me, etc., etc.  In other words, how I normally work.

If any of this sounds interesting to you, please join in!  I’ll do my best to announce start times with an hour or so lead over on Twitter, and as comments on this post.


A Meyerweb Makeover

Published 6 months, 1 day ago

This coming week, I’m going to redesign meyerweb.com—which will be its first redesign in twelve and a half years.

I’m doing this as part of Jen Simmons’ #newwwyear initiative.  As Jen put it:

Ok, here’s the deal. Tweet your personal website plan with the hashtag #newwwyear (thanks @jamiemchale!): 1) When will you start? 2) What will you try to accomplish? 3) When is your deadline? Improve an existing site. Start a new one. Burn one down & start over. It’s up to you.

Many of us feel bad about our personal websites. Me included. We keep meaning to make one, improve what’s there, or burn it down and start over. We are busy. Afraid. Overwhelmed. Well, let’s do it. Maybe over the holidays. Maybe after, in the New Year. #newwwyear

On Friday, I announced my plan:

  1. I’ll start Wednesday, December 27th.
  2. I’ll redesign http://meyerweb.com  for the first time in a dozen years, and I’ll do it live on the production site.
  3. My deadline is Wednesday, January 3rd, so I’ll have a week.

I won’t be redesigning all day every day—I still have paying work to do, after all—but I’ll do my best to put in a couple of hours each weekday.

When say I’ll do it live, I mean I’ll be making all my changes here on the production site, with minimal or no testing beforehand—literally opening the style sheet(s) into BBEdit via Transmit, and saving changes up to the server to see what happens.  Stuff will break, and then I’ll fix it, live in the public eye.  It’s possible I’ll try out new ideas and then junk them before moving on to others.  I’m hoping that accidents spark inspiration, as they often do.

(There will be a local copy of the site in case things go so badly that I need to reset to the starting point.  I’m not completely insane, after all.)

I have a vague plan with all this, which is: realign the site’s appearance to be more inviting, more readable, and more visually engaging.  I do have a few past experiments that I’ll fold in, like using relative times (e.g., “Two months ago”) on posts, but a lot of this will be me doing free-association design.  And hopefully a little markup cleanup and enhancement as well.

I’m sticking with WordPress to drive the blog, given that it contains close to two decades of posts and makes it easy to allow comments, a feature I still value; and my hand-built old-school-standards-punk mostly-static templating system for the rest of the site, which let the site be static(ish) way before static was cool.  (No, I will not consider migrating to other CMSes or template systems: with a week set aside for this, I won’t have the time.)

So, that’s the plan: a week for a meyerweb makeover.  I don’t know if I’ll keep up a running commentary on Twitter while I do, or if I’ll take breaks and blog short entries chronicling my progress, or what.  If someone sets up a #newwwyear Slack team, I’ll probably join in.  If the #newwwyear idea excites you, I hope you’ll join in too!