grack.com

Blog

(Ab)using CSS3's :nth-child selector to invent new ones

If you find this interesting, read more about CSS Selectors Level 4 which will offer you even more tools for stylesheet development.

CSS3’s :nth-child and :nth-last-child selectors are powerful: not only can they replace :first-child and :last-child, but they can style more complex patterns like the first (or all but the first) three children, every fourth child, or combinations of the pattern “a*n+b”.

But did you know that you can do more interesting things with the selectors? For example, you can style the third element, but only when it’s one of five child (the virtual :nth-of-m-child selector we’ll discuss below). Or that you can style all of the children of an element with m children (another virtual selector we’ll call :family-of-m).

You might ask why we’d want to do this – the particular use case that I had in mind was a Javascript-free, automatically-sizing image gallery that I could toss in the stylesheet for my Jekyll-based site and have it “just work” regardless of the number of images I threw at it.

Here’s an example of what it looks like (click here to view it full-page). Note how the images automatically size to a regular grid without having to use Javascript:

:nth-of-m-child

Thanks to xantys on HN for pointing me at much earlier work in this area here and here.

The first virtual selector we’ll construct is something I call :nth-of-m-child. This will allow us to style the nth child when it’s one of m children, and will be our building block for further work.

So, how do we get this selector? Easy: we combine :nth-child and :nth-last-child on a single element to select when the element to be styled is in the correct position from the start and end of the list of children. For example, we can style the third element, if and only if it’s one of five children:

span:nth-child(3):nth-last-child(3) { ... }

Breaking that down: that’s the third child and the third-last child. Given ‘n’ and ‘m’, the general formula is :nth-child(n):nth-last-child(m+1-n).

Here’s an example of this in action (click here to view it full-page):

:family-of-m

Now that we have the ability to style n-of-m, we can expand that out to style all of the children where there are m of them directly underneath a parent node. The secret to this is using the CSS3 ~ non-adjacent sibling selector which will continue matching elements across siblings. For example, the selector:

img ~ span { ... }

will match a <span> if and only if one of its previous siblings was an <img> element regardless of the number of siblings between them. We’ll combine this selector with our :nth-of-m-child pattern like so:

span:nth-child(1):nth-last-child(5) ~ span { ... }

This pattern will match any adjacent siblings to the first element of five, ie: the second through the fifth of five. We can make it match the entire row by using a comma to match the first element as well.

span:nth-child(1):nth-last-child(5), 
	span:nth-child(1):nth-last-child(5) ~ span { ... }

Here’s an example of this in action (click here to view it full-page):

Advanced techniques

Alright, now we have the tools in place for us to style images as seen in the example.

The first grouping of one through four are simple applications of the technique. When we get to five we want to start using a pattern where the first line or two are larger pairs and the remainder of the images are in triplets. This pattern should repeat regardless of the number of images.

Here’s a commented example. Note that this might potentially be simplified using flexbox and wrapping, but that’s an exercise for the reader.

Let’s start with fifth, eighth, eleventh and the remainder of this pattern. Instead of using the :nth-child(1):nth-last-child(...) as we did before, we’ll use :nth-child(1):nth-last-child(3n+5). This will match the first element of a grouping of 5, 8, 11, …:

/* First two are half-sized (99% / 2) */
img:first-child:nth-last-child(3n+5) ~ img, img:first-child:nth-last-child(3n+5) {
	max-width: 49.5%;
	margin-right: 1%;
}

/* Last n - 2 are (98% / 3) */
img:first-child:nth-last-child(3n+5) + img ~ img {
	max-width: 32.6%;
	margin-right: 1%;
}

/* But second, fifth, eighth, ... have no right margin */
img:first-child:nth-last-child(3n+5) ~ img:nth-child(3n+2) {
	margin-right: 0;
}

Six, nine, and twelve are much simpler.

/* Six, nine, twelve, ... are all (98% / 3) */
img:first-child:nth-last-child(3n+6) ~ img, img:first-child:nth-last-child(3n+6) {
	max-width: 32.6%;
	margin-right: 1%;
}

/* Every third one of these has no right margin. */
img:first-child:nth-last-child(3n+6) ~ img:nth-child(3n) {
	margin-right: 0;
}

Seven, ten, thirteen, and the remainder of the pattern are similar to the 5/8/11 pattern:

/* First four are half-sized (99% / 2) */
img:first-child:nth-last-child(3n+7) ~ img, img:first-child:nth-last-child(3n+7) {
	max-width: 49.5%;
	margin-right: 1%;
}

/* Last n - 4 are (98% / 3) */
img:first-child:nth-last-child(3n+7) + img + img + img ~ img {
	max-width: 32.6%;
	margin-right: 1%;
}

/* The second and fourth, seventh, tenth, ... have no right margin */
img:first-child:nth-last-child(3n+7) + img, 
	img:first-child:nth-last-child(3n+7) ~ img:nth-child(3n+4) {
	margin-right: 0;
	outline: 1px solid red;
}

Conclusions, notes, and further work

We can do some really interesting things making using of :nth-child and sibling selectors. The techniques in this post will also work for the related selectors :nth-of-type and :nth-last-of-type.

Browser performance doesn’t appear to be noticeably affected on mobile or desktop using this technique. If you plan on scaling this up it’s obviously something you’ll want to test.

If you were to combine this with flexbox and flex-wrap you might be able to simplify the example even further and might be able to handle images of different sizes more elegantly.

You also can also use this to create other interesting patterns, like matching only when the total number of children is even or odd, or other factor-based qualifiers.

I’d love to hear of any ideas or improvements you might have. Play around with the JSFiddle here.

Thanks to Webucator for creating a video for this post as part of their CSS3 training course series:

Comments? Follow me on Twitter @mmastrac and let me know.

Welcome to the new grack.com

I’m now on my fifth – though I’m sure not my last – iteration of blog management, Jekyll. It’s a slick static site generator and from what I can tell, the most popular.

One of the benefits of a static site generation tool is that you can aggressively cache the content. For that I’ve thrown Cloudflare in front of everything and have it both caching and optimizing all of my assets.

As part of the re-launch I wanted to offer some basic site search. I looked into a few options for static search in pure Javascript, but the most convenient turned out to be Swiftype which was not much more work than clicking a switch on Cloudflare’s app page.

While I was somewhat happy on Wordpress, my biggest issue with it was that everything lived in a database rather than source control, and that it required constant attention updating it for each release. I spent nearly five years on the platform, however, which puts it at the longest I’ve stayed on any one tool.

Obviously, publishing posts with static site generators isn’t quite as convenient. For that I’m making use of Travis CI and a private GitHub repo. It runs linklint over everything, then pushes it out to the static host via rsync.

The Next Decade: part 2

Five years ago I wrote a set of predictions about 2020. In some ways I think I was too unambitious for the progress of technology: browser technology has probably already reached the levels I had expected for a decade later. Some of them look like they might be off-target, like the increase in storage capacity of mobile devices. We still have another five years to see how they all pan out.

I thought I’d take another swing at a set of predictions. This time I’m looking out towards 2025.

Manufacturing

3D printing is the next major transformative technology wave. In 2025 we’ll be seeing 3D printers in the homes of at least a quarter of North Americans. Home 3D printer quality in 2025 will be amazing. A consumer will be able to print in multiple colors and plastic/rubber-like materials, with a final print quality that will often rival that of mold-formed products.

There will be multiple marketplaces for things to print: some open-source ones that will contain basic shapes and designs, and a number of licensed repositories for printing designer items like toys and kitchenware from well-known names. There will also be a healthy 3D piracy community.

When you look around a room in 2014, a lot of what you see will be printed in the home or a rapid-manufacturing facility in 2025. 3D printing will eat the manufacturing lifecycle from the bottom up. Products where the first 100 might be built with rapid prototyping today will have the first 100,000 produced on-demand. Companies like IKEA will choose to print and cut their designs on-demand in warehouses rather than stocking parts built elsewhere.

Local distribution centers will replace overseas manufacturing almost completely. North America will import processed materials from China, India and Africa for use in the new local centers: plastics and rubbers, and metals with binder. The local manufacturing centers will use a combination of 3D printer, laser/plasma cutting and generic robot assembly to build everything from one-off orders to runs of thousands as needed.

To support rapid manufacturing at these levels, generic robotics will replace the manufacturing line of today. A generic robot will be able to be taught to do a task like a human does and will coordinate with other generic robots to build a product like a fixed line would do today. Generic robotics will also find itself at home in other fields currently employing humans doing repetitive tasks like food preparation, laundry and cleaning.

Amusingly 3D printers will be displacing regular document printers which will have mostly died out at that point. Paper will take much longer than 2025 to replace, but the majority of financial, legal and educational work will be entirely electronic. Electronic signatures will be commonplace and some form of e-signature using mobile devices and biometrics will likely be used to validate documents. Paper in the home will be well on its way out and even children will be coloring on electronic devices with a stylus rather than coloring books.

Home

In 2025 we’ll have maxed out our technologies for 2D display and audio. Screens on cheap devices will have the same effective DPI as paper and the pixel will be something very few people will see. Virtual reality won’t be pervasive yet, but it’ll be in a significant number of homes for entertainment purposes. There will be devices small enough for portable use.

The convergence of devices will continue, and will consume a number of devices for entertainment at home. The cable box and dedicated game consoles will be almost dead, replaced with streaming from personal devices Chromecast-style. TV will still be going strong and there will be advancements in display technology that will allow basic 3D technology without glasses using light-field manipulation.

Transport

Vehicles will be radically transformed by advances in self-driving technology. While we still won’t have pervasive self-driving vehicles, there will be a large number of autonomous vehicles on the road. These will mainly replace long-haul trucking and transport, but some higher-end vehicles will be autonomous enough to allow individuals to safely commute to work or run errands hands-free.

Car ownership will be dramatically declining by 2025. With the ability to summon transport from an autonomous or human-driven vehicle at the tip of everyone’s fingertips, it won’t make sense for most people to own vehicles. High-end cars will continue to be a status symbol, but the low-end market will be decimated in the transition.

Electric vehicles will have captured around half of the market of vehicles sold, including everything from passenger cars to transport trucks. Fossil fuels and hydrogen will be included as “backup” options on some models, used only to increase range if needed by charging the batteries.

Energy

With solar getting cheaper and more efficient year-by-year, we’ll see some changes in the way that energy is supplied to home. Neighborhood grids will replace nation-wide grids for home use. Dozens of houses will pool their solar power into a cooperative with battery storage that they’ll use for electricity, heating and charging of vehicles.

More nuclear power will come online in the 2020’s, mainly driven by new reactor designs that produce very little waste and advances in fail-safe technology. Thorium-based reactors will be just starting to appear and safe designs like the CANDU plant will become significantly more popular.

Much of the new power brought online will be fueling desalinization and water-reclamation facilities to stabilize the fresh water supply. Fresh water will dominate the news cycle as energy does today.

Conclusion

With all of the changes described above, North America will see a significant effect on the number of low-end jobs. Manufacturing, food and transport industries will be radically changed, jobs moving from many low-skilled positions to a few high-end positions.

This will require thought on how we can re-architect the economy. I think we’ll see concepts like minimum guaranteed income replacing welfare and minimum wage. This is much more difficult to predict than the pace of technology, but we’ll have to do something to ensure that everyone is able to live in the new world we’re building.

Colossal Cave Adventure

This is a fun little project I’ve been working on: a port of the Colossal Cave Adventure to the web. The monitor itself is nearly 100% CSS (with the exception of one image for the masking tape/signature and the diffuse reflection of the background).

The interpreter is all Java and compiled to JS via GWT. It can save its state to localStorage right now when you save in-game (although I’d like to automatically persist the state of the engine continuously before I release it).

And, FWIW, the Colossal Cave Adventure is a surprisingly hard game. 

UPDATE: Source is on github and you can play it here.

On Google Chromebooks

This is a follow-up to my week with a ChromeOS netbook post.

The Google Chromebook is an interesting product to watch. I’ve been a fan of and using them since the early Cr-48 days. In fact, two Chromebook laptops were in service in our household until just a few weeks ago when the Samsung Chromebook broke (although I hope to repair it soon).

These laptops sit next to our couch in a stack as a set of floater laptops we use for random surfing. If any of us are just looking for a quick bite of information, we generally pull out the Chromebook rather than walking over to the Macbook that sits on our kitchen counter. The Chromebook is also great for our son to use when building LEGO from PDF instructions.

Browsing is far better on the Chromebook than it is on any Android or iOS device I’ve used, hands down. I find the browsing experience to be frustrating on an iPad or my Galaxy 10”, while the Chromebook experience is flawless. The device is basically ready-to-use for browsing as soon as you lift the lid, in contrast to the fair amount of time it takes to get logged into the Macbook (especially if another user has a few applications open in their session).

The hardware itself in the early models was slightly underpowered, but that doesn’t really seem to matter much unless you’re playing a particularly intensive Flash video or HTML5 game. Scrolling is fairly slow on complex sites like Google+ as well, but it’s never been a showstopper. The touchpads have also been hit-and-miss in the early models. For what we use it for, the hardware is pretty decent. I imagine that the next generations will gradually improve on these shortcomings.

What makes these devices a hard sell is the price point. The cheapest Chromebook experience you can get today is the Acer (@ $300). Considering the fact that you are buying a piece of hardware that effectively does less than a laptop, I would find it hard to justify spending that amount if I were looking at hardware today. Even though I prefer to use the Chromebook when surfing over the tablets or the full laptop, I feel like the cost is just too much for a single-purpose device like this.

For Chromebooks to really take off in the home market, I think that a device with the equivalent power to the Samsung Chromebook 5 needs to be on the market at a $199 price point. I could see myself buying them without a second thought at that price. Alternatively, if we saw some sort of Android hybrid integration with the Chromebook, I think that this could radically change the equation and add significant perceived value to the device.

I don’t see the Chromebox being popular in households ever - I believe that we’ll see the decline of the non-portable computer going forward at home. Now, if I were running a business where a large subset of employees could get by with just web access, I would definitely consider rolling these out. The Chromebox looks like it could be a real game changer for business IT costs.