Front End Web Development

At Twitter I used the approach described here to publish the company’s SVG icon library in several different formats: optimized SVGs, plain JavaScript modules, React DOM components, and React Native components.

One thing different systems tend to share is some kind of build process to turn a folder full of SVG files into a more programmatically digestible format. For example, gulp-svg-sprite takes your folder of SVGs and creates a SVG sprite (chunk of <symbol>s) to use in that type of SVG icon system. Grunticon processes your folder of SVGs into a CSS file, and is capable of enhancing them into inline SVG. Gallagher's script creates React components out of them, and like he said, that's great for delivery to different targets as well as performance optimization, like code splitting.

This speaks to the versatility of SVG. It's just markup, so it's easy to work with.

Here are two ways to build a site (abstractly) that feel diametrically opposed to me:

Build a site as an SPA (Single Page App). The page loads a skeleton HTML page that executes JavaScript as quickly as it can. The JavaScript calls an API to get data, and then the page renders content. Navigation of the site is more API calls to get the data it needs and re-rendering.

Build a site as statically-generated. A build process runs in which the entire site is built out as static HTML files with all the content baked into them. JavaScript isn't required at all for the site to work.

That feels just about as different as can be. But weirdly, they kinda aren't:

They are both JAMstack. They can be hosted statically as neither of them needs backend languages running on the server they are hosted on.

They are both building content based on an API of data. It's more obvious in the first one, but you can think of a static site generator as hitting an API of data as it runs and builds itself. It's just that the API might be temporarily created from content files it finds on disk. Or it might be the exact same API used for the former site.

What's interesting now is to see what's important to these frameworks by honing in on their focus. Hummingbird is Flutter for the web. (There is a fun series on Flutter over on the Bendworks blog in addition to a post we published earlier this year.) The idea being you get super high performance ,thanks to the framework, and you've theoretically built one app that runs both on the web and natively. I don't know of any real success stories I can point to, but it does seem like an awesome possibility.

Working with data in React is relatively easy because React is designed to handle data as state. The hassle begins when the amount of data you need to consume becomes massive. For example, say you have to handle a dataset which is between 500-1,000 records. This can result in massive loads and lead performance problems. Well, we’re going to look at how we can make use of virtualized lists in React to seamlessly render a long list of data in your application.

We’re going to use the React Virtualized component to get what we need. It will allow us to take large sets of data, process them on the fly, and render them with little-to-no jank.

The setup

React Virtualized already has a detailed set of instructions to get it up and running, so please check out the repo to get started.

We’re going to want data to work with, so we will set up a function which uses faker to create a large data set.

Next, we will pass it the number of data records we want to create, like so:

const records = createRecord(1000);

Alright, now we have what we need to work on rendering a list of those records!

Creating a virtualized list

Here’s the list we want to create, sans styling. We could make use of the few presentational styles that the library includes by importing the included CSS file, but we're going to leave that out in this post.

You might wonder what the heck React Virtualized is doing behind the scenes to make that happen. Turns out it's a bunch of crazy and cool sizing, positioning, transforms and transitions that allow the records to scroll in and out of view. The data is already there and rendered. React Virtualized creates a window frame that allows records to slide in and out of view as the user scrolls through it.

To render a virtualized list in React Virtualized, we make use of its List component, which uses a Grid component internally to render the list.

First, we start by setting up rowRenderer, which is responsible for displaying a single row and sets up an index that assigns an ID to each record.

According to the documentation, a cell measurer is a higher-order component that is used to temporarily render a list. It’s not yet visible to the user at this point, but the data is held and ready to display.

Why should you care about this? The popular use case is a situation where the value of your rowHeight is dynamic. React Virtualized can render the height of the row on render then cache that height so it no longer needs to calculate as data scrolls out of view — it's always the right height, no matter the content it contains!

First, we create our cache, which can be done in our component’s constructor using CellMeasurerCache:

The value passed to deferredMeasurementCache will be used to temporarily rendering the data, then — as the calculated value for rowHeight comes in — additional rows will flow in like they were always there.

Next, though, we will make use of React Virtualized’s CellMeasurer component inside our rowRenderer function instead of the div we initially set up as a placeholder:

Now the data is fetched, cached and ready to display in the virtual window at will!

Virtualized table

Yeah, so the main point of this post is to cover lists, but what if we actually want to render data to a table instead? React Virtualized has you covered on that front, too. In this case, we will make use of Table and Column components that come baked into React Virtualized.

Here’s how we would put those components to use in our primary App component:

rowCount: This is the initial number of rows we want in the table. It’s the same as the way we defined the number of records we wanted to start with in the List component example.

rowGetter: This returns the data of a specific row by its index.

If you take a look at the Column component, you will notice that we put a dataKey parameter to use. That passes the data for each column we called in the dataKey, which receives a unique identifier for that data. Remember that in the function where we create our random data, we make use of two keys; username and email. This is why we have the dataKey of one column set as username and the other set as email.

In conclusion

Hopefully, this walkthrough gives you a good idea of what React Virtualized is capable of doing, how it can make rendering large data sets into lists and tables super fast, and how to put it to use in a project.

Automatically detect and diagnose JavaScript errors impacting your users with Bugsnag. Get comprehensive diagnostic reports, know immediately which errors are worth fixing, and debug in a fraction of the time.

Bugsnag detects every single error and prioritizes errors with the greatest impact on your users. Get support for 50+ platforms and integrate with the development and productivity tools your team already uses.

There is a sentiment that leaving math calculations in your CSS is a good idea that I agree with. This is for math that you could calculate at authoring time, but specifically chose not to. For instance, if you needed a 7-column float-based grid (don't ask), it's cleaner and more intuitive:

You could probably prove that the calc() takes the computer 0.0000001% longer, so explicitly defining the width is technically faster for performance reason — but that is about the equivalent of not using punctuation in sentences because it saves HTML weight.

That math can be a little more complicated as you continue. For example, like in our use cases for calc() article, what about columns in that 7-column grid that span?

Again, I'd say that's pretty readable, but it's also a good amount of repetition. This might call for using variables. We'll do it with CSS custom properties for fun. You have to pick what is worthy of a variable and what isn't. You might need fewer comments as the code becomes somewhat self-documenting:

Every single number has been given a variable in there. Too far? Maybe. It certainly makes those width declarations pretty hard to wrap your head around quickly. Ana Tudor does some serious stuff with calc(), as proof that everyone's comfort level with this stuff is different.

One of the things that made me think of all this is a recent article from James Nash — "Hardcore CSS calc()" — where he builds this:

While the solution took a heavily math-y road to get there, it ends up being only sort of medium-level calculation on the ol' complexity meter. And note that not everything gets a variable' only the most re-used bits:

Flickr announced not long ago that they are limiting free accounts to 1,000 photos. I don't particularly mind that (because it seems like sound business sense), although it is a bit sad that a ton of photos will be nuked from the internet. I imagine the Internet Archive will swoop in and get most of it. And oh hey, the Twitter account @FlickrJubilee is showcasing Flickr users that could really use a gifted pro account so their amazing photos are not lost, if you're feeling generous and want to contribute.

This change doesn't affect pro accounts. I've been pro forever on Flickr, so my photos were never at risk, but the big change has me thinking it's about time to spin down Flickr for myself. I've been keeping all my photos on iCloud/Photos for years now anyway so it seems kind redundant to keep Flickr around.

I went into the Flickr settings and exported all my photos, got a bunch of gigabytes of exported photos, and loaded them into Photos. Sadly, the exported photos have zero metadata, so there will forever be this obnoxious chunk of thousands upon thousands of photos in my Photos collection that all look like they were taken on the same day and with no location.

Anyway, that was way too long of an intro to say: I found a bunch of old website screenshots! Not a ton, but it looks like I used Flickr to store a handful of web designs I found interesting in some way a number of years back. What's interesting today is how dated they look when they were created not that long ago. Shows how fast things change.

Here they are.

It's not terribly surprising to me to hear people push back on the same-ness of web design these days, and to blame things like frameworks, component-driven architecture, and design systems for it. It wasn't long ago when it seemed like we were trying harder to be fancy and unique with our designs — things like shadow treatments, reflective images and skeuomorphic enhancements. I don't mean to make sweeping generalizations here... merely a difference between what we considered to be boring and fancy work back than compared to now, of course.

...every so often, we use the materials of front-end development—HTML, CSS, and JavaScript—to produce something that isn’t intended for production. I’m talking about prototyping.

What’s interesting is that—when it comes to prototyping—our usual front-end priorities can and should go out the window. The priority now is speed. If that means sacrificing semantics or performance, then so be it. If I’m building a prototype and I find myself thinking “now, what’s the right class name for this component?”, then I know I’m in the wrong mindset. That question might be valid for production code, but it’s a waste of time for prototypes.

I love the way that Jeremy phrases all of this and how he describes that these two environments require entirely separate mindsets. When prototyping, for instance, we can probably overlook optimizing for accessibility or performance and even let our CSS standards slip in order to get something in the browser and test it as quickly as possible.

I reckon that the first time a designer and/or front-end developer writes code, it should never be in a production environment. Having the leeway and freedom to go crazy with the code in a safe environment focuses your attention on the design and making it compatible with a browser’s constraints. After this, you can think about grooming the code from a hot, steaming heap of garbage into lovely, squeaky-clean, production-ready poetry. Translating the static mockups into an interactive prototype is the first step, but it’s vital to have a next step to enforce your code standards.

Will Microsoft’s decision make it harder for Firefox to prosper? It could. Making Google more powerful is risky on many fronts. [...] If one product like Chromium has enough market share, then it becomes easier for web developers and businesses to decide not to worry if their services and sites work with anything other than Chromium. That’s what happened when Microsoft had a monopoly on browsers in the early 2000s before Firefox was released. And it could happen again.

Before you lament the return to a Microsoft-like monopoly, remember what happened to Microsoft’s monopoly. In fact, remember what happened to the lineal descendant of that monopoly just last week. Near-monopolies do not necessarily mean the end of the web.

Browser diversity

Back then, Microsoft stopped developing IE because it thought it had won. Right now, Google is doing no such thing — in fact, I think it’s moving too fast rather than too slow.

Back then, Microsoft welcomed the IE-only badges that sprung up on countless websites. Right now, the Chrome devrel team does not. In fact, they don’t hesitate to criticise Chrome-only sites created by other parts of Google.

Also, don’t forget WebKit. Right now web developers pretend there are only two rendering engines left, Gecko and Blink, but there is in fact a third one, and especially on mobile it’s quite important. (When did you last create a site that didn’t really have to work on iOS?)

Today’s situation is very different from fifteen years ago — though I still think web developers switching to Firefox as their default browser is a good idea. (No, I haven’t yet done so either.)

Headcount

But I don’t really want to talk about browser diversity, or about the relative merits of present-day Google and past Microsoft.

Instead, I want to talk about headcount.

The IE team first asked for my regarded opinion somewhere in 2005 or so, when they were gearing up toward IE7. Since then I’ve been in touch with them, and followed their progress with interest, occasionally submitting feature lists or clarifying our web developer point of view.

During all those years, I had the distinct feeling the IE team was under-staffed — a feeling that was occasionally, if privately, confirmed by team members. It seemed that, while Microsoft had decided to continue the development of IE, it didn’t want to commit the full resources that the project warranted.

That’s why, in the immediate aftermath of Microsoft confirming the rumour, I was quite surprised to see a We’re hiring message. (Granted, the actual job descriptions don’t mention Chromium, but they were written in October or November.)

I understand why people are nervous about a Chrome monoculture. I think this case is a little different though. Microsoft has an army of engineers working on Edge. They’re one of the few companies who can go toe-to-toe with Google funding browser development.

Now I don’t follow Tom, and the only reason I saw his tweet is that it was retweeted by an Edge team member. Sure, RT !== endorsement, but this was a curious coincidence, to say the least.

Is one unexpected benefit of the switch to Chromium that the Edge team can actually expand? It’s easier to get Chromium engineers than EdgeHTML ones, that’s for sure.

Android

If Microsoft does solve its headcount problems, then things get interesting — especially on Android. Sure, Microsoft has more opportunities for expanding its market share elsewhere, notably Windows 7 (where EdgeHTML never ran), but Android is by far the most interesting one.

One huge advantage of moving to Chromium is that Edge can now be easily ported to Android. This tweet appears to confirm that an Android version is in the planning.

Let’s jump sideways for a moment. Google Services is a suite of Android apps such as Play, Search, Maps, YouTube, and other crucial services that pretty much define how useful a smartphone is. It also contains Google Chrome. Android vendors get the option of using Google Services for free, provided they use ALL of them. All non-Chinese ones actually do so, and Google Services is an important part of the hold Google has on the web and mobile markets. Also, it puts Google Chrome on every Android phone.

What if Microsoft offered the Android vendors an alternative? Microsoft has a search engine, maps, and other services. YouTube can be viewed in a browser as well as in an app — and now it has the browser. Only an alternative to the Play Store is missing — so far. It’s quite possible that some Android vendors would seriously consider such an offer. It would ease Google’s stranglehold.

In that light, I found it interesting that HTC is experimenting with Brave as its default browser on one phone model. (True, the HTC Exodus is a “blockchain phone” and when I recently visited the local phone store they didn’t have any HTC whatsoever on offer, and nomen is most definitely not an omen. Still, interesting.) And if this example doesn’t convince you, remember Samsung Internet. Non-Google Chromium browsers are a thing on Android. But they aren't part of a set of services — yet.

Anyway, IF the Edge team gets more people, and IF Microsoft decides to go the Android route, the switch to Chromium may become interesting very fast.

I came across this amazing Dribbble shot by Jakub Reis a while back. It caught my eye and I knew that I just had to try recreating it in code. At that moment, I didn’t know how. I tried out a bunch of different things, and about a year later, I finally managed to make this demo.

I learned a couple of things along the way, so let me take you on a little journey of what I did to make this because you may learn a thing or two as well.

I needed to extract as much info as I could out of the original GIF to have a good understanding of the animation, so I split it up into single frames. There actually are a lot of services that can do this for us. I used one at ezgif.com but it could have just as easily been something else. Either way, this enables us to get details such as the colors, sizes, and proportions of all the different elements we need to create.

Oh, and we still need to turn the fingerprint into an SVG. Again, there are plenty of apps that will help us here. I used Adobe Illustrator to trace the fingerprint with the pen tool to get this set of paths:

The general idea is this: Create an instance of this class for each path that we have in the fingerprint, and modify them in every frame. The paths will start with an offset ratio of -1 (fully invisible) and then will increase the offset ratio (which we’ll refer to as “offset” from here on) by a constant value each frame until they get to 0 (fully visible). The filling animation will be over at this point.

If you’ve never animated anything with this frame-by-frame approach, here’s a very simple demo to help understand how this works:

We should also handle the case where the user stops tapping or pressing the mouse button. In this case, we will animate in the opposite direction (subtracting a constant value from the offset each frame until it gets to -1 again).

Let’s create the function that calculates the offset increment for every frame — this’ll be useful later on.

Now it’s time to animate! We will keep the fingerprint paths in a single array:

let fprintPaths = [];
// We create an instance of Path for every existing path.
// We don't want the paths to be visible at first and then
// disappear after the JavaScript runs, so we set them to
// be invisible in CSS. That way we can offset them first
// and then make them visible.
for (let i = 0; i < $(fprintPathSelector).length; i++) {
fprintPaths.push(new Path(fprintPathSelector, i));
fprintPaths[i].offset(-1).makeVisible();
}

We will go through that array for each frame in the animation, animating the paths one by one:

let fprintTick = getPropertyIncrement(0, 1, TIME_TO_FILL_FPRINT);
function fprintFrame(timestamp) {
// We don't want to paint if less than 1000 / 65 ms elapsed
// since the last frame (because there are faster screens
// out there and we want the animation to look the same on
// all devices). We use 65 instead of 60 because, even on
// 60 Hz screens, `requestAnimationFrame` can sometimes be called
// a little sooner, which can result in a skipped frame.
if (timestamp - lastRafCallTimestamp >= 1000 / 65) {
lastRafCallTimestamp = timestamp;
curFprintPathsOffset += fprintTick * fprintProgressionDirection;
offsetAllFprintPaths(curFprintPathsOffset);
}
// Schedule the next frame if the animation isn't over
if (curFprintPathsOffset >= -1 && curFprintPathsOffset <= 0) {
isFprintAnimationInProgress = true;
window.requestAnimationFrame(fprintFrame);
}
// The animation is over. We can schedule next animation steps
else if (curFprintPathsOffset > 0) {
curFprintPathsOffset = 0;
offsetAllFprintPaths(curFprintPathsOffset);
isFprintAnimationInProgress = false;
isFprintAnimationOver = true;
// Remove the background with grey paths
$fprint.addClass('demo__fprint--no-bg');
// Schedule the next animation step - transforming one of the paths into a string
// (this function is not implemented at this step yet, but we'll do that soon)
startElasticAnimation();
// Schedule the fingerprint removal (removeFprint function will be implemented in the next section)
window.requestAnimationFrame(removeFprint);
}
// The fingerprint is back to the original state (the user has stopped holding the mouse down)
else if (curFprintPathsOffset < -1) {
curFprintPathsOffset = -1;
offsetAllFprintPaths(curFprintPathsOffset);
isFprintAnimationInProgress = false;
}
}

This part is pretty similar to the first one, only now we have to account for the fact that some of the paths remove in one direction and the rest of them in the other. That’s why we added the --removes-forwards modifier earlier.

First, we’ll have two additional arrays: one for the paths that are removed forwards and another one for the ones that are removed backwards:

You can see that, as the fingerprint is almost removed, some of its paths are longer than they were in the beginning. I moved them into separate paths that start animating at the right moment. I could incorporate them into the existing paths, but it would be much harder and at 60fps would make next-to-no difference.

Let’s create them:

<path class="demo__ending-path demo__ending-path--pinkish" d="M48.4,220c-5.8,4.2-6.9,11.5-7.6,18.1c-0.8,6.7-0.9,14.9-9.9,12.4c-9.1-2.5-14.7-5.4-19.9-13.4c-3.4-5.2-0.4-12.3,2.3-17.2c3.2-5.9,6.8-13,14.5-11.6c3.5,0.6,7.7,3.4,4.5,7.1"/>
<!-- and 5 more paths like this -->

This is a fun animation. First, we’ll create a couple of new divs that contain the particles that explode:

<div class="demo__logo-particles">
<div class="demo__logo-particle"></div>
<!-- and several more of these -->
</div>
<div class="demo__money-particles">
<div class="demo__money-particle"></div>
<!-- and several more of these -->
</div>

The two explosions are practically the same with the exception of a few parameters. That’s where SCSS mixins will come in handy. We can write the function once and use it on our divs.

Note the comment in the code that the particles don’t perform particularly well on less powerful devices such as phones. Perhaps there’s another approach here that would solve this if anyone has ideas and wants to chime in.

In my early attempts I tried using CSS transitions for all of the animation work. I found it virtually impossible to control the progress and direction of the animation, so shortly I abandoned that idea and waited a month or so before starting again. In reality, if I knew back then that the Web Animations API was a thing, I would have tried to make use of it.

I tried making the explosion with Canvas for better performance (using this article as a reference), but I found it difficult to control the frame rate with two separate requestAnimationFrame chains. If you know how to do that, then maybe you can tell me in the comments (or write an article for CSS-Tricks &#x1f642;).

After I got a first working prototype, I was really unhappy with its performance. I was hitting around 40-50fps on a PC, not to mention phones at all. I spent a lot of time optimizing the code and this article was a lot of help.

You can see that the graph has a gradient. I did that by declaring a gradient directly in the SVG defs block:

The whole process from start to finish — discovering the Dribbble shot and finishing the work — took me about a year. I was taking month-long breaks here and there either because I didn’t know how to approach a particular aspect or I simply didn’t have enough free time to work on it. The entire process was a really valuable experience and I learned a lot of new things along the way.

That being said, the biggest lesson to take away from this is that there’s no need to shy away from taking on an ambitious task, or feel discouraged if you don’t know how to approach it at first. The web is a big place and there is plenty of space to figure things out as you go along.

The least important skills for a front-end developer to have are technical ones.

The nuances of JavaScript. How to use a particular library, framework, or build tool. How the cascade in CSS works. Semantic HTML. Fizz-buzz.

Chris takes that a little farther than I would. I do think that with an understanding of HTML, CSS, and JavaScript, the deeper the better, and that it is an ingredient in making a good front-end developer. But I also agree it's much more than that. In fact, with solid foundational skills and really good soft skills (e.g. you're great at facilitating a brainstorming meeting), you could and should get a job before they even look at your language skills.

There is lots of speculation in that thread, but Bruce has a pretty clear answer:

AFAIK, <foo src=""> tells the browser to get something and insert it here - eg <img src="">, "<script src="">. Stylesheets aren't 'inserted', they are related to the current doc, but typically style more than 1 page. <style></style> declares a block of rules for this page only

I sort of get that. The location in the document matters with src, but not with <link> — that relates to the entire document instead. I guess the crack in that reasoning is that the order of stylesheets does matter for order-specificity, but I take the point.

The W3C chimed to confirm that logic:

(2/2) and the original <script> element in HTML 3.2 didn't have a src attribute. the REC for HTML 3.2 mentions <link> as a way to link to scripts, but it doesn't define any keywords for rel=.

If you've been writing JavaScript for some time now, it's almost certain you've written some scripts dealing with the Document Object Model (DOM). DOM scripting takes advantage of the fact that a web page opens up a set of APIs (or interfaces) so you can manipulate and otherwise deal with elements on a page.

But there's another object model you might want to become more familiar with: The CSS Object Model (CSSOM). Likely you've already used it but didn't necessarily realize it.

In this guide, I'm going to go through many of the most important features of the CSSOM, starting with stuff that's more commonly known, then moving on to some more obscure, but practical, features.

The CSS Object Model is a set of APIs allowing the manipulation of CSS from JavaScript. It is much like the DOM, but for the CSS rather than the HTML. It allows users to read and modify CSS style dynamically.

MDN's info is based on the official W3C CSSOM specification. That W3C document is a somewhat decent way to get familiar with what's possible with the CSSOM, but it's a complete disaster for anyone looking for some practical coding examples that put the CSSOM APIs into action.

MDN is much better, but still largely lacking in certain areas. So for this post, I've tried to do my best to create useful code examples and demos of these interfaces in use, so you can see the possibilities and mess around with the live code.

As mentioned, the post starts with stuff that's already familiar to most front-end developers. These common features are usually lumped in with DOM scripting, but they are technically part of the larger group of interfaces available via the CSSOM (though they do cross over into the DOM as well).

Inline Styles via element.style

The most basic way you can manipulate or access CSS properties and values using JavaScript is via the style object, or property, which is available on all HTML elements. Here's an example:

document.body.style.background = 'lightblue';

Most of you have probably seen or used that syntax before. I can add to or change the CSS for any object on the page using that same format: element.style.propertyName.

In that example, I'm changing the value of the background property to lightblue. Of course, background is shorthand. What if I want to change the background-color property? For any hyphenated property, just convert the property name to camel case:

document.body.style.backgroundColor = 'lightblue';

In most cases, a single-word property would be accessed in this way by the single equivalent word in lowercase, while hyphenated properties are represented in camel case. The one exception to this is when using the float property. Because float is a reserved word in JavaScript, you need to use cssFloat (or styleFloat if you're supporting IE8 and earlier). This is similar to the HTML for attribute being referenced as htmlFor when using something like getAttribute().

Here's a demo that uses the style property to allow the user to change the background color of the current page:

In the example above, I'm defining an inline style on the <body> element, then I'm logging that same style to the console. That's fine. But if I try to read another property on that element, it will return nothing — unless I've previously defined an inline style for that element in my CSS or elsewhere in my JavaScript. For example:

Using element.style is the simplest and most common way to add styles to elements via JavaScript. But as you can see, this clearly has some significant limitations, so let's look at some more useful techniques for reading and manipulating styles with JavaScript.

Getting Computed Styles

You can read the computed CSS value for any CSS property on an element by using the window.getComputedStyle() method:

Well, that's an interesting result. In a way, window.getComputedStyle() is the style property's overly-benevolent twin. While the style property gives you far too little information about the actual styles on an element, window.getComputedStyle() can sometimes give you too much.

In the example above, the background property of the <body> element was defined using a single value. But the getComputedStyle() method returns all values contained in background shorthand. The ones not explicitly defined in the CSS will return the initial (or default) values for those properties.

This means, for any shorthand property, window.getComputedStyle() will return all the initial values, even if none of them is defined in the CSS:

Similarly, for properties like width and height, it will reveal the computed dimensions of the element, regardless of whether those values were specifically defined anywhere in the CSS, as the following interactive demo shows:

Try resizing the parent element in the above demo to see the results. This is somewhat comparable to reading the value of window.innerWidth, except this is the computed CSS for the specified property on the specified element and not just a general window or viewport measurement.

There are a few different ways to access properties using window.getComputedStyle(). I've already demonstrated one way, which uses dot-notation to add the camel-cased property name to the end of the method. You can see three different ways to do it in the following code:

The first line uses the same format as in the previous demo. The second line is using square bracket notation, a common JavaScript alternative to dot notation. This format is not recommended and code linters will warn about it. The third example uses the getPropertyValue() method.

The first example requires the use of camel casing (although in this case both float and cssFloat would work) while the next two access the property via the same syntax as that used in CSS (with hyphens, often called "kebab case").

Here's the same demo as the previous, but this time using getPropertyValue() to access the widths of the two elements:

One little-known tidbit about window.getComputedStyle() is the fact that it allows you to retrieve style information on pseudo-elements. You'll often see a window.getComputedStyle() declaration like this:

window.getComputedStyle(document.body, null).width;

Notice the second argument, null, passed into the method. Firefox prior to version 4 required a second argument, which is why you might see it used in legacy code or by those accustomed to including it. But it's not required in any browser currently in use.

That second optional parameter is what allows me to specify that I'm accessing the computed CSS of a pseudo-element. Consider the following CSS:

.box::before {
content: 'Example';
display: block;
width: 50px;
}

Here I'm adding a ::before pseudo-element inside the .box element. With the following JavaScript, I can access the computed styles for that pseudo-element:

The above works in the latest Firefox, but not in Chrome or Edge (I've filed a bug report for Chrome).

It should also be noted that browsers have different results when trying to access styles for a non-existent (but valid) pseudo-element compared to a pseudo-element that the browser doesn't support at all (like a made up ::banana pseudo-element). You can try this out in various browsers using the following demo:

As a side point to this section, there is a Firefox-only method called getDefaultComputedStyle() that is not part of the spec and likely never will be.

The CSSStyleDeclaration API

Earlier when I showed you how to access properties via the style object or using getComputedStyle(), in both cases those techniques were exposing the CSSStyleDeclaration interface.

In other words, both of the following lines will return a CSSStyleDeclaration object on the document's body element:

document.body.style;
window.getComputedStyle(document.body);

In the following screenshot you can see what the console produces for each of these lines:

In the case of getComputedStyle(), the values are read-only. In the case of element.style, getting and setting the values is possible but, as mentioned earlier, these will only affect the document's inline styles.

setProperty(), getPropertyValue(), and item()

Once you've exposed a CSSStyleDeclaration object in one of the above ways, you have access to a number of useful methods to read or manipulate the values. Again, the values are read-only in the case of getComputedStyle(), but when used via the style property, some methods are available for both getting and setting.

In this example, I'm using three different methods of the style object:

The setProperty() method. This takes two arguments, each a string: The property (in regular CSS notation) and the value you wish to assign to the property.

The getPropertyValue() method. This takes a single argument: The property whose value you want to obtain. This method was used in a previous example using getComputedStyle(), which, as mentioned, likewise exposes a CSSStyleDeclaration object.

The item() method. This takes a single argument, which is a positive integer representing the index of the property you want to access. The return value is the property name at that index.

Keep in mind that in my simple example above, there are only two styles added to the element's inline CSS. This means that if I were to access item(2), the return value would be an empty string. I'd get the same result if I used getPropertyValue() to access a property that isn't set in that element's inline styles.

Using removeProperty()

In addition to the three methods mentioned above, there are two others exposed on a CSSStyleDeclaration object. In the following code and demo, I'm using the removeProperty() method:

In this case, after I set font-size using setProperty(), I log the property name to ensure it's there. The demo then includes a button that, when clicked, will remove the property using removeProperty().

In the case of setProperty() and removeProperty(), the property name that you pass in is hyphenated (the same format as in your stylesheet), rather than camel-cased. This might seem confusing at first, but the value passed in is a string in this example, so it makes sense.

Getting and Setting a Property's Priority

Finally, here's an interesting feature that I discovered while researching this article: The getPropertyPriority() method, demonstrated with the code and CodePen below:

In the first line of that code, you can see I'm using the setProperty() method, as I did before. However, notice I've included a third argument. The third argument is an optional string that defines whether you want the property to have the !important keyword attached to it.

After I set the property with !important, I use the getPropertyPriority() method to check that property's priority. If you want the property to not have importance, you can omit the third argument, use the keyword undefined, or include the third argument as an empty string.

And I should emphasize here that these methods would work in conjunction with any inline styles already placed directly in the HTML on an element's style attribute.

So if I had the following HTML:

<div class="box" style="border: solid 1px red !important;">

I could use any of the methods discussed in this section to read or otherwise manipulate that style. And it should be noted here that since I used a shorthand property for this inline style and set it to !important, all of the longhand properties that make up that shorthand will return a priority of important when using getPropertyPriority(). See the code and demo below:

In the demo, even though I explicitly set only the border property in the style attribute, all the associated longhand properties that make up border will also return a value of important.

The CSSStyleSheet Interface

So far, much of what I've considered deals with inline styles (which often aren't that useful) and computed styles (which are useful, but are often too specific).

A much more useful API that allows you to retrieve a stylesheet that has readable and writable values, and not just for inline styles, is the CSSStyleSheet API. The simplest way to access information from a document's stylesheets is using the styleSheets property of the current document. This exposes the CSSStyleSheet interface.

For example, the line below uses the length property to see how many stylesheets the current document has:

document.styleSheets.length; // 1

I can reference any of the document's stylesheets using zero-based indexing:

document.styleSheets[0];

If I log that stylesheet to my console, I can view the methods and properties available:

The one that will prove useful is the cssRules property. This property provides a list of all CSS rules (including declaration blocks, at-rules, media rules, etc.) contained in that stylesheet. In the following sections, I'll detail how to utilize this API to manipulate and read styles from an external stylesheet.

Working with a Stylesheet Object

For the purpose of simplicity, let's work with a sample stylesheet that has only a handful of rules in it. This will allow me to demonstrate how to use the CSSOM to access the different parts of a stylesheet in a similar way to accessing elements via DOM scripting.

There's a number of different things I can attempt with this example stylesheet and I'll demonstrate a few of those here. First, I'm going to loop through all the style rules in the stylesheet and log the selector text for each one:

A couple of things to take note of in the above code and demo. First, I cache a reference to the cssRules object for my stylesheet. Then I loop over all the rules in that object, checking to see what type each one is.

In this case, I want rules that are type 1, which represents the STYLE_RULE constant. Other constants include IMPORT_RULE (3), MEDIA_RULE (4), KEYFRAMES_RULE (7), etc. You can view a full table of these constants in this MDN article.

When I confirm that a rule is a style rule, I print the selectorText property for each of those style rules. This will produce the following lines for the specified stylesheet:

*
body
main
.component
a:hover
code

The selectorText property is a string representation of the selector used on that rule. This is a writable property, so if I want I can change the selector for a specific rule inside my original for loop with the following code:

In this example, I'm looking for a selector that defines :hover styles on my links and expanding the selector to apply the same styles to elements in the :active state. Alternatively, I could use some kind of string method or even a regular expression to look for all instances of :hover, and then do something from there. But this should be enough to demonstrate how it works.

Accessing @media Rules with the CSSOM

You'll notice my stylesheet also includes a media query rule and a keyframes at-rule block. Both of those were skipped when I searched for style rules (type 1). Let's now find all @media rules:

As you can see, after I loop through all the rules to see if any @media rules exist (type 4), I then loop through the cssRules object for each media rule (in this case, there's only one) and log the selector text for each rule inside that media rule.

So the interface that's exposed on a @media rule is similar to the interface exposed on a stylesheet. The @media rule, however, also includes a conditionText property, as shown in the following snippet and demo:

This code loops through all media query rules and logs the text that determines when that rule is applicable (i.e. the condition). There's also a mediaText property that returns the same value. According to the spec, you can get or set either of these.

Accessing @keyframes Rules with the CSSOM

Now that I've demonstrated how to read information from a @media rule, let's consider how to access a @keyframes rule. Here's some code to get started:

In this example, I'm looking for rules that have a type of 7 (i.e. @keyframes rules). When one is found, I loop through all of that rule's cssRules and log the keyText property for each. The log in this case will be:

"0%"
"20%"
"100%"

You'll notice my original CSS uses from and to as the first and last keyframes, but the keyText property computes these to 0% and 100%. The value of keyText can also be set. In my example stylesheet, I could hard code it like this:

Thus, the name property allows me to read the custom name chosen for that @keyframes rule. This is the same name that would be used in the animation-name property when enabling the animation on a specific element.

One final thing I'll mention here is the ability to grab specific styles that are inside a single keyframe. Here's some example code with a demo:

In this example, after I find the @keyframes rule, I loop through each of the rules in the keyframe (e.g. the "from" rule, the "20%" rule, etc). Then, within each of those rules, I access an individual style property. In this case, since I know color is the only property defined for each, I'm merely logging out the color values.

The main takeaway in this instance is the use of the style property, or object. Earlier I showed how this property can be used to access inline styles. But in this case, I'm using it to access the individual properties inside of a single keyframe.

You can probably see how this opens up some possibilities. This allows you to modify an individual keyframe's properties on the fly, which could happen as a result of some user action or something else taking place in an app or possibly a web-based game.

Adding and Removing CSS Declarations

The CSSStyleSheet interface has access to two methods that allow you to add or remove an entire rule from a stylesheet. The methods are: insertRule() and deleteRule(). Let's see both of them in action manipulating our example stylesheet:

In this case, I'm logging the length of the cssRules property (showing that the stylesheet originally has 8 rules in it), then I add the following CSS as an individual rule using the insertRule() method:

article {
line-height: 1.5;
font-size: 1.5em;
}

I log the length of the cssRules property again to confirm that the rule was added.

The insertRule() method takes a string as the first parameter (which is mandatory), comprising the full style rule that you want to insert (including selector, curly braces, etc). If you're inserting an at-rule, then the full at-rule, including the individual rules nested inside the at-rule can be included in this string.

The second argument is optional. This is an integer that represents the position, or index, where you want the rule inserted. If this isn't included, it defaults to 0 (meaning the rule will be inserted at the beginning of the rules collection). If the index happens to be larger than the length of the rules object, it will throw an error.

In this case, the method accepts a single argument that represents the index of the rule I want to remove.

With either method, because of zero-based indexing, the selected index passed in as an argument has to be less than the length of the cssRules object, otherwise it will throw an error.

Revisiting the CSSStyleDeclaration API

Earlier I explained how to access individual properties and values declared as inline styles. This was done via element.style, exposing the CSSStyleDeclaration interface.

The CSSStyleDeclaration API, however, can also be exposed on an individual style rule as a subset of the CSSStyleSheet API. I already alluded to this when I showed you how to access properties inside a @keyframes rule. To understand how this works, compare the following two code snippets:

The first example is a set of inline styles that can be accessed as follows:

document.querySelector('div').style

This exposes the CSSStyleDeclaration API, which is what allows me to do stuff like element.style.color, element.style.width, etc.

But I can expose the exact same API on an individual style rule in an external stylesheet. This means I'm combining my use of the style property with the CSSStyleSheet interface.

So the CSS in the second example above, which uses the exact same styles as the inline version, can be accessed like this:

document.styleSheets[0].cssRules[0].style

This opens up a single CSSStyleDeclaration object on the one style rule in the stylesheet. If there were multiple style rules, each could be accessed using cssRules[1], cssRules[2], cssRules[3], and so on.

So within an external stylesheet, inside of a single style rule that is of type 1, I have access to all the methods and properties mentioned earlier. This includes setProperty(), getPropertyValue(), item(), removeProperty(), and getPropertyPriority(). In addition to this, those same features are available on an individual style rule inside of a @keyframes or @media rule.

Here's a code snippet and demo that demonstrates how these methods would be used on an individual style rule in our sample stylesheet:

// Grab the style rules for the body and main elements
let myBodyRule = document.styleSheets[0].cssRules[1].style,
myMainRule = document.styleSheets[0].cssRules[2].style;
// Set the bg color on the body
myBodyRule.setProperty('background-color', 'peachpuff');
// Get the font size of the body
myBodyRule.getPropertyValue('font-size');
// Get the 5th item in the body's style rule
myBodyRule.item(5);
// Log the current length of the body style rule (8)
myBodyRule.length;
// Remove the line height
myBodyRule.removeProperty('line-height');
// log the length again (7)
myBodyRule.length;
// Check priority of font-family (empty string)
myBodyRule.getPropertyPriority('font-family');
// Check priority of margin in the "main" style rule (!important)
myMainRule.getPropertyPriority('margin');

For full details on those features and a glimpse into the syntax, be sure to check out the full article.

As of this writing, CSS Typed OM is supported only in Chrome. You can see the progress of browser support in this document.

Final Words

Manipulating stylesheets via JavaScript certainly isn't something you're going to do in every project. And some of the complex interactions made possible with the methods and properties I've introduced here have some very specific use cases.

If you've built some kind of tool that uses any of these APIs I'd love to hear about it. My research has only scratched the surface of what's possible, but I'd love to see how any of this can be used in real-world examples.

I've put all the demos from this article into a CodePen collection, so you can feel free to mess around with those as you like.

I think it's kinda cool to see Google dropping repos of interesting web components. It demonstrates the possibilities of cool new web features and allows them to ship them in a way that's compatible with entirely web standards.

I wanted to give it a try, so I linked up their example two-up-min.js script in a Pen and used the element by itself to see how it works. They expose the component's styling with custom properties, which I'd say is a darn nice use case for those.

What naming scheme do you use for color variables? Have you succeeded at writing CSS that uses color variables in a manner agnostic to the colors they represent?I've tried all of the following, and I have yet to succeed at writing CSS that works well with any color scheme. ☹️

I remember the very first time I tried Sass on a project. The first thing I wanted to do was variablize my colors. From my naming-things-in-HTML skillz, I knew to avoid classes like .header-blue-left-bottom because the color and position of that element might change. It's better for the to reflect it what it is than what it looks like.

So, I tried to make my colors semantic, in a sense — what they represent not what they literally are:

I found that to be much more intuitive with little if any negative side effects. After all, this isn't crossing the HTML-CSS boundary here; this is all within CSS and developer-only-facing, which puts more of a narrow scope on the problem.

In a similar fashion, I've tried keeping colors within a Sass map, like:

$colors: (
light: #ccc,
dark: #333
);

But the only vague goal there was to clean up the global namespace, which probably isn't worth the hassle of needing to map-get all the time. Namespacing like $-c-orange is probably an easier approach if you need to do anything at all.

I've largely stuck with that just-use-color-names approach today in Sass. As the shift toward CSS custom properties happens, I think having a --c-orange and --c-gray-5 is similarly appropriate.

I've used that kind of thing for media query breakpoints before, as the numbering seems to make sense there (i.e. low numbers are smaller screens, big numbers are bigger screens). I could also see that being nice for tints or shades of the same color, but then why not regular numbers?

How might you pick names for colors? You might get a kick out of what to call a sunny yellow versus a sunflower yellow, or you might just want some help. Here's one project for that, and here's another:

Compound components in React allow you to create components with some form of connected state that’s managed amongst themselves. A good example is the Form component in Semantic UI React.

To see how we can implement compound components in a real-life React application, we’ll build a compound (multi-part) form for login and sign up. The state will be saved in the form component and we’ll put React’s Context AP to use to pass that state and the method from the Context Provider to the component that needs them. The component that needs them? It will become a subscriber to Context Consumers.

Here’s a rough outline that shows how the following steps fit together:

Before treading any further, you may want to brush up on the React Context API if you haven’t already. Neal Fennimore demonstrates the concept in this post and my primer on it is worth checking out as well.

The provider, FormProvider, will hold the application state, making it available to components that subscribe to FormConsumer.

Step 2: Implement provider

One panel contains the form to log in and the other contains the form to sign up. In the provider, we want to declare the state, which determines the active panel, i.e. the form currently in display. We’ll also create a method to switch from one panel to another when a heading is clicked.

By default, the login panel will be shown to the user. When the signup panel is clicked, we want to make it the active panel by setting the state of activePanel to signup using the method handlePanelSwitch().

Step 3: Implement Consumers

We’ll use FormConsumer to make context available to the components that subscribe to it. That means the FormPanel component that handles displaying panels will look like this:

To understand what is happening, let’s understand the approach here. The login and signup panels will have unique IDs that get passed via props to the Panel component. When a panel is selected, we get the ID and and use it to set activePanel to swap forms. The FormPanel component also receives the name of the panel via the isActive prop and we then check to see if the returned value is true. If it is, then the panel is rendered!

Ultimately, we want to make the web experience better for many different audiences. People using Microsoft Edge (and potentially other browsers) will experience improved compatibility with all web sites, while getting the best-possible battery life and hardware integration on all kinds of Windows devices. Web developers will have a less-fragmented web platform to test their sites against, ensuring that there are fewer problems and increased satisfaction for users of their sites; and because we’ll continue to provide the Microsoft Edge service-driven understanding of legacy IE-only sites, Corporate IT will have improved compatibility for both old and new web apps in the browser that comes with Windows.

We are making this decision for the long term. We expect our engineers to learn and over time become experts in the Chromium project and grow into active and responsible members of the community. We are eager to increase our contributions to the Chromium project and will continue to maintain any contributions we make.

When seeking improvements in the web platform, our default position will be to contribute. We are focused on delivering a world class browser with Microsoft Edge through its differentiated user experience features and connected services, but where new platform capabilities are concerned, we will seek a ‘rising tide that floats all boats’. We will get started with bug fixes and meaningful contributions in such areas as ARM64 support, accessibility, security, touch input and power enhancements on Windows.

We recognize and will respect the architecture requirements and engineering approach that are intrinsic in web open-source projects and have made Chromium successful. There are many aspects that have governed Chromium OSS and other projects: multi-device support, multi-OS support, rigorous real-time engineering, etc. Although our company has historically had a focus on Windows PCs and we believe we can make contributions that improve browsers on Windows, we also understand that web OSS projects embrace a wide range of device-types, including Android, and that contributions must accommodate this device diversity. We will contribute in a way that is consistent with the architectural design that meets Chromium’s cross-platform and cross-device needs.

We believe the evolution of the open web is best served though the standards communities, and the open web benefits from open debate from a wide variety of perspectives. We will remain deeply and vigorously engaged in the standards discussions in the context of the W3C, ECMA and the WHATWG where the perspectives of vendors developing competing browsers and the larger web community can be heard and considered.

Nothing terribly surprising here. We're doing this. We think it'll be good for everybody. I'm slightly surprised they didn't attempt to answer everyone's main worry: is the web actually better off with less engine diversity? We'll never know I guess.

Even though Opera, Beaker and Brave are all doing very good work, it is still Chrome engine behind them and that limits the amount of stuff they can build and innovate. It is like as if they were building cars, there is a lot they can do without actually changing the engine itself, and thats what the Web Browsers are becoming, everyone is working on parts of the car but all the engines are now Chrome and believe me, you don’t want all the engines to be the same, not even if they are all Gecko or if somehow we resurrect Presto, we want diversity of engines and not monoculture.

I can understand the logic. Microsoft can’t put as many folks on Edge (including EdgeHTML for rendering and Chakra for JavaScript) as Google has done with Chromium (using Blink for rendering and V8 for JavaScript), so keeping up was always going to be a challenge. Now they can contribute to the same codebase and try to focus on the user-focused features. Whether this gets people to pay more attention to their next browser or not remains to be seen, but I get the thinking behind the move.

The big concern here is we’ve lost another voice from an engine perspective.

Edge is doomed. It was doomed and its next version will be equally doomed from the start. For the simple reason that Microsoft has close to no say in how browsers get installed: on mobile as a default app, and on desktop via web services under the control of Google. Switching to Chromium makes no difference in market share, as the only way to compete now is through the browser’s UI, not via the engine. Which isn’t a competition at all, since browser UI is a commodity.

By adopting Chromium, Microsoft hands over control of even more of online life to Google.

This may sound melodramatic, but it’s not. The “browser engines” — Chromium from Google and Gecko Quantum from Mozilla — are “inside baseball” pieces of software that actually determine a great deal of what each of us can do online. They determine core capabilities such as which content we as consumers can see, how secure we are when we watch content, and how much control we have over what websites and services can do to us. Microsoft’s decision gives Google more ability to single-handedly decide what possibilities are available to each one of us.

This is the second post in a two-part series that looks into the way CSS variables can be used to make the code for complex layouts and interactions less difficult to write and a lot easier to maintain. The first installment walks through various use cases where this technique applies. This post covers the use of fallbacks and invalid values to extend the technique to non-numeric values.

The strategy of using CSS Variables to drive the switching of layouts and interactions that we covered in the first post in this series comes with one major caveat: it only works with numeric values — lengths, percentages, angles, durations, frequencies, unit-less number values and so on. As a result, it can be really frustrating to know that you're able to switch the computed values of more than ten properties with a single CSS variable, but then you need to explicitly switch the non-numeric values of properties like flex-direction or text-align from row to column or from left to right or the other way around.

One example would be the one below, where the text-align property depends on parity and the flex-direction depends on whether we are viewing the front end in the wide screen scenario or not.

Screenshot collage.

I complained about this and got a very interesting suggestion in return that makes use of CSS variable fallbacks and invalid values. It was interesting and gives us something new to work with, so let's start with a short recap of what these are and go from there!

Fallback values

The fallback value of a CSS variable is the second and optional argument of the var() function. For example, let's consider we have some .box elements whose background is set to a variable of --c:

.box { background: var(--c, #ccc) }

If we haven't explicitly specified a value for the --c variable elsewhere, then the fallback value #ccc is used.

Now let's say some of these boxes have a class of .special. Here, we can specify --c as being some kind of orange:

.special { --c: #f90 }

This way, the boxes with this .special class have an orange background, while the others use the light grey fallback.

Secondly, a comma separated list is a perfectly valid fallback value. In fact, everything specified after the first comma inside the var() function constitutes the fallback value, as seen in the example below:

We also want the odd paragraphs to be right-aligned, while keeping the even ones left-aligned. In order to achieve this, we introduce a --parity variable which we don't set explicitly in the general case — only for even items. What we do set in the general case is our previous variable, --i. We set it to the value of --parity with a fallback of 0:

So far, this achieves exactly the same as the previous version of our code. However, if we take advantage of the fact that, we can use different fallback values in different places for the same variable, then we can also set text-align to the value of --parity using a fallback of... right!

text-align: var(--parity, right)

In the general case, where we're not setting --parity explicitly; text-align uses the fallback right, which is a valid value, so we have right alignment. For the even items however, we're setting --parity explicitly to 1, which is not a valid value for text-align. That means text-align reverts to its initial value, which is left.

We set a switch --i that changes value with the parity — it's 0 for the odd items and 1 for the even ones.

p {
/* same code as before */
--i: 0;
&:nth-child(2n) { --i: 1 }
}

Next, we want the numbering to be on the left for the odd items and on the right for the even ones. We achieve this via the order property. The initial value for this property is 0, for both the :before pseudo-element and the paragraph's text content. If we set this order property to 1 for the numbering (the :before pseudo-element) of the even elements, then this moves the numbering after the content.

Next, we want to give these items a proper background. This is a grey to orange gradient, going from left to right (or along a 90deg angle) in the case of odd items (parity switch --i: 0) and from right to left (at a -90deg angle) in the case of even items (parity switch --i: 1).

This means the absolute value of the gradient angle is the same (90deg), only the sign is different — it's +1 for the odd items (--i: 0) and -1 for the even items (--i: 1).

In order to switch the sign, we use the approach we covered in the first post:

The odd items are translated a bit to the right (in the positive direction of the x axis) and rotated a bit in the clockwise (positive) direction, while the even items are translated a bit to the left (in the negative direction of the x axis) and rotated a bit in the other (negative) direction.

The translation and rotation amounts are the same; only the signs differ.

The next step is to round the card corners. For the odd cards, we want the corners on the left side to be rounded to a radius of half the height. For the even items, we want the corners on the right side to be rounded to the same radius.

Given we don't know the heights of our cards, we just use a ridiculously large value, say something like 50vh, which gets scaled down to fit due to the way border-radius works. In our case, this means scaled down to whichever is smaller between half the item height (since going vertically has both a top and bottom rounded corner on the same side) and the full item width (since going horizontally has one rounded corner; either on the left or on the right, but not on both the right and the left).

This means we want the corners on the left to have this radius ($r: 50vh) for odd items (--i: 0) and the ones on the right to have the same radius for even items (--i: 1). As a result, we do something pretty similar to the numbering margin case:

Now comes the truly interesting part — text alignment! We want the text in the odd items to be aligned right, while the text in the even items is aligned left. The only problem is that text-align doesn't take a number value so, no addition or multiplication tricks can help us here.

What can help is combining the use of fallback and invalid values for CSS variables. To do this, we introduce another parity variable --p and it's this variable that we actually set to 1 for even items. Unlike --i before, we never set --p explicitly for the general case as we want different fallback values of this variable to be used for different properties.

As for --i, we set it to --p with a fallback value of 0. This fallback value of 0 is the value that actually gets used in the general case, since we never explicitly set --p there. For the even case, where we explicitly set --p to 1, --i becomes 1 as well.

At the same time, we set the text-align property to --p with a fallback value of right in the general case. In the even case, where we have --p explicitly set to 1, the text-align value becomes invalid (because we have set text-align to the value of --p and --p is now 1, which is not a valid value for text-align), so the text reverts to being aligned to the left.

While our cards example looks great on wider screens, the same can't be said when shrink things down.

The wide screen result (left) vs. the narrow screen result (right)

In order to fix this, we introduce two more custom properties, --wide and --k to switch between the wide and narrow cases. We set --k to --wide with a fallback value of 0 in the general case and then set --wide to 1 if the viewport width is anything 340px and up.

Since we only want our items to be transformed and have rounded corners in the wide case, we multiply the translation, rotation and radius values by --k (which is 0, unless the viewport is wide, which switches its value to 1).

This is slightly better, but our content still overflows in narrow viewports. We can fix this by only placing the numbering (the :before pseudo-element) on the left or right side only in the wide case then moving it above the card in the narrow case.

In order to do this, we multiply both its order and its lateral margin values by --k (which is 1 in the wide case and 0 otherwise).

This means the flex-direction value is column in the general case (since we haven't set --wide explicitly elsewhere). However, if the viewport is wide (min-width: 340px), then our --wide variable gets set to 1. But 1 is an invalid value for flex-direction, so this property reverts back to its initial value of row.

Just like the cards example we completed together, we can use a :before pseudo-element for the numbering and a flex layout on the paragraphs. The sliced disc effect is achieved using clip-path.

The paragraph elements themselves — the horizontal offsets, the position and intensity of the radial-gradient() creating the shadow effect, the direction of the linear-gradient() and the saturation of its stops, the color and the text alignment — all depend on the --parity variable.

For the numbering (the :before pseudo-elements of the paragraphs), we have that both the margin and the order depend on the --parity in the exact same way as the cards example.

If the viewport width is smaller than the disc diameter $d plus twice the horizontal slice offset in absolute value $x, then we're not in the --wide case anymore. This affects the width, padding and margin of our paragraphs, as well as their horizontal offset and their shape (because we don't clip them to get the sliced disc effect at that point).

The parity determines each paragraph's text alignment, which lateral border gets a non-zero value, and the position and direction of the border gradient. Both the parity and whether we're in the wide screen case or not determine the lateral margins and paddings.

The icon is created using the :before pseudo-element, and its order depends on the parity, but only if we're not in the narrow screen scenario — in which case it's always before the actual text content of the paragraph. Its lateral margin depends both on the parity and whether we are in the wide screen case or not. The big-valued component that positions it half out of its parent paragraph is only present in the wide screen case. The font-size also depends on whether we're in the narrow screen case or not (and this influences its em dimensions and padding).

The ring is created using an absolutely positioned :after pseudo-element (and its placement depends on parity), but only for the wide screen case.

content: var(--wide, '');
The two-dimension case
Screenshot collage (live demo, no Edge support due to CSS variable and calc() bugs).

Here we have a bunch of article elements, each containing a heading. Let's check out the most interesting aspects of how this responsive layout works!

On each article, we have a two-dimensional layout (grid) — but only if we're not in the narrow screen scenario (--narr: 1), in which case we fall back on the normal document flow with the numbering created using a :before pseudo-element, followed by the heading, followed by the actual text. In this situation, we also add vertical padding on the heading since we don't have the grid gaps anymore and we don't want things to get too crammed.

For the grid, we create two columns of widths depending both on parity and on whether we're in the wide screen scenario. We make the numbering (the :before pseudo-element) span two rows in the wide screen case, either on the second column or the first, depending on the parity. If we're not in the wide screen case, then the paragraph spans both columns on the second row.

We set the grid-auto-flow to column dense in the wide screen scenario, letting it revert to the initial value of row otherwise. Since our article elements are wider than the combined widths of the columns and the column gap between them, we use place-content to position the actual grid columns inside at the right or left end depending on parity.

Finally, we place the heading at the end or start of the column, depending on parity, and we as well as the paragraph's text alignment if we're in the wide screen scenario.

We also have numerical values such as grid gaps, border radii, paddings, font-sizes, gradient directions, rotation and translation directions depending on the parity and/or whether we're in the wide screen scenario or not.

Even more examples!

If you want more of this, I've created an entire collection of similar responsive demos for you to enjoy!