Mediator Component in React — Robin Wieruch’s article made me think just how un-opinionated React is and how architecturally on-your-own you are. It’s tough to find the right abstractions.
No One Ever Got Fired for Choosing React — Jake Lazaroff’s article is a good balance to the above. Sometimes you pick a library like React because it solves problems you’re likely to run into and lets you get to work.
A React “if component — I kinda like how JSX does conditional rendering, but hey, a lightweight abstraction like this might just float your boat.
State of the React Ecosystem in 2021 — Hooks are big. State management is all over the place, and state machines are growing in popularity. webpack is still the main bundler. Everyone is holding their breath about Suspense… so suspenseful.
Blitz.js — “The Fullstack React Framework” — interesting to see a framework built on top of another framework (Next.js).
Introducing Zero-Bundle-Size React Server Components — Feels like it will be a big deal, but will shine brightest when frameworks and hosts zero-in on offerings around it. I’m sure Vercel and Netlify are all 👀.
Font-size: An Unexpectedly Complex CSS Property — From Manish Goregaokar in 2017. Of many oddities, I found the one where font: medium monospace renders at 13px where font: medium sans-serif renders at 16px particularly weird.
The good line-height — Since CSS supports unitless line-height, you probably shouldn’t be setting a hard number anyway.
Time to Say Goodbye to Google Fonts — Simon Wicki doesn’t mean don’t use them, they mean self-host them. Browsers are starting to isolate cache on a per-domain basis so that old argument that you buy speed because “users probably already have it cached” doesn’t hold up. I expected to hear about stuff like having more control over font loading, but this is just about the cache.
My Favorite Typefaces of 2020 — John Boardley’s picks for the past year. Have you seen these “color fonts”? They are so cool. Check out LiebeHeide, it looks like real pen-on-paper.
How to avoid layout shifts caused by web fonts — We’ve got CLS (Cumulative Layout Shift) now and it’s such an important performance metric that will soon start affecting SEO. And because we have CSS control over font loading via font-display, that means if we set things up such that font loading shifts the page, that’s bad. I like Simon Hearne’s suggestion that we tweak both our custom font and fallback font to match perfectly. I think perfect fallback fonts are one of the best CSS tricks.
How to pick a Typeface for User Interface and App Design? — Oliver Schöndorfer makes the case for “functional text” which is everything that isn’t body text (e.g. paragraphs of text) or display text (e.g. headers). “Clarity is key.”
Back/forward cache — I always assumed browsers just do fancy stuff with the back/forward buttons and us developers had very little control. Philip Walton tells us it’s critical that we understand “what makes pages eligible (and ineligible) for bfcache to maximize their cache-hit rates.” For example, if you use the unload event, the page is instantly disqualified from the cache.
Beyond fast with new performance features — Jake Archibald gets into the CSS content-visibility property (and a few other things) and how it can lead to incredible performance boosts (you use it to tell the browser that it’s straight-up OK not to render things). Right this minute, content-visiblity makes me nervous as it has issues with scrollbar jankiness and accessiblity problems. I found it a smidge confusing at first glance, and Tim Kadlec has reservations.
Image Decode & Visual Timings — Image performance isn’t only about the size of the image. Different formats take different amounts of time to decode and render. GIF never wins.
How to increase CSS-in-JS performance by 175x — The trick, readers, is shipping CSS. You can still use CSS-in-JS as you author, and have the build process create the CSS. They call that “Zero-Runtime” like Linaria.
Testing Performance — Kelly Sutton: “The best approach that I have found to preventing performance regressions is to employ qualitative assessments of the code.” Performance is such an unwieldy beast, that only in production will you truly know what happens.
Here’s a little hodgepodge of videos I’ve recently bookmarked (and watched). I couldn’t decide if each one of them should be a separate blog post or if I should do this combined list post thing. If I had a lot more to say about each video I would have split them off, but I went with the combined post and fewer words here. Let me know in the comments if you have a preference on that kind of thing.
Here’s Tom Scott explaining why the web is such a mess
Cookies. It all cookies fault. Kinda. Also humans.
This is the same Tom that did that super neat video that updated it’s own title with the view count.
I don’t know this person’s name but I appreciated how it gets the details right in this recreation of the Discord sidebar
Except the fact that the :hover was on the <li> itself rather than a link that went somewhere sensible, but hey, I guess it’s slightly better than the actual Discord markup where it’s <div class="listItem_">.
Heydon gets all weird about progressive enhancment
I can’t embed it here because, even though it looks like Heydon switched over to Vimeo rather than just straight up <video> tags, the privacy settings have it locked to briefs.video only.
I liked the point about “the basic layout is not a broken layout,” except, doesn’t it seem like in that exact case it wouldn’t matter if you wrapped the grid-template-rows in a @supports or not? I love that @supports is a thing, and even more so now that we’ve passed the awkward years where @supports itself didn’t have full browser support, but I don’t find myself reaching for it that much, as it’s only really useful if you need to do something different than “just let it not work,” which I don’t find terribly common.
Houssein Djirdeh and Jason Miller get into “modern JavaScript”
We’ve gotta be so careful about compiled code. There is an example where one line of JavaScript gets compiled into 7000 bytes, which is bigger and far slower than intended. Taking care of older browsers when you have a significant number of users using that old browser is a really great thing, but you might be surprised at the browser support of “modern JavaScript” and find you are compiling more than you need to.
The kicker is that you only really control what you write, but probably most of what you ship is third-party. That means npm, which is absolutely loaded with very non-modern JavaScript. The sweet spot, they say, is calling ES 2017 the compile target in general. If you need even older support, use the ol’ differential serving trick.
They go back and forth trying to figure out how to make this little spiky virus-looking thing. I totally relate to their approaches! Neither of them are like amazingly clever with either the HTML or CSS — they just try to get it done. That’s why I could never get into CSSBattle myself. I appreciate people’s trickery (duh), but my actual CSS writing style is almost like verbose-on-purpose.
I’ve always liked tag clouds. I like the UX of seeing what tags are most popular on a website by seeing the relative font size of the tags, popular tags being bigger. They seem to have fallen out of fashion, though you do often see versions of them used in illustrations in tools like Wordle.
How difficult is it to make a tag cloud? Not very difficult at all. Let’s see!
Let’s start with the markup
For our HTML, we’re going to put each of our tags into a list, <ul class="tags"><ul>. We’ll be injecting into that with JavaScript.
If your tag cloud is already in HTML, and you are just looking to do the relative font-size thing, that’s good! Progressive enhancement! You should be able to adapt the JavaScript later on so it does just that part, but not necessarily building and injecting the tags themselves.
I have mocked out some JSON with a certain amount of articles tagged with each property. Let’s write some JavaScript to go grab that JSON feed and do three things.
First, it we’ll create an <li> from each entry for our list. Imagine the HTML, so far, is like this:
Third, and last, we’ll create a link around each tag that goes to the correct place. This is where we can set the font-size property for each item depending on how many articles that property is tagged with, so animation that has 13 articles will be much bigger than background-color which only has one article.
const dataURL = "https://gist.githubusercontent.com/markconroy/536228ed416a551de8852b74615e55dd/raw/9b96c9049b10e7e18ee922b4caf9167acb4efdd6/tags.json"; const tags = document.querySelector(".tags"); const fragment = document.createDocumentFragment(); const maxFontSizeForTag = 6; fetch(dataURL) .then(function (res) { return res.json(); }) .then(function (data) { // 1. Create a new array from data let orderedData = data.map((x) => x); // 2. Order it by number of articles each tag has orderedData.sort(function(a, b) { return a.tagged_articles.length - b.tagged_articles.length; }); orderedData = orderedData.reverse(); // 3. Get a value for the tag with the most articles const highestValue = orderedData[0].tagged_articles.length; // 4. Create a list item for each result from data. data.forEach((result) => handleResult(result, highestValue)); // 5. Append the full list of tags to the tags element tags.appendChild(tag); });
The JavaScript above uses the Fetch API to fetch the URL where tags.json is hosted. Once it gets this data, it returns it as JSON. Here we seque into a new array called orderedData (so we don’t mutate the original array), find the tag with the most articles. We’ll use this value later on in a font-scale so all other tags will have a font-size relative to it. Then, forEach result in the response, we call a function I have named handleResult() and pass the result and the highestValue to this function as a parameter. It also creates:
a variable called tags which is what we will use to inject each list item that we create from the results,
a variable for a fragment to hold the result of each iteration of the loop, which we will later append to the tags, and
a variable for the max font size, which we’ll use in our font scale later.
Next up, the handleResult(result) function:
function handleResult(result, highestValue) { const tag = document.createElement("li"); tag.classList.add("tag"); tag.innerHTML = `<a class="tag__link" href="$ {result.href}" style="font-size: $ {result.tagged_articles.length * 1.25}em">$ {result.title} ($ {result.tagged_articles.length})</a>`; // Append each tag to the fragment fragment.appendChild(tag); }
This is pretty simple function that creates a list element set to the variable named tag and then adds a .tag class to this list element. Once that’s created, it sets the innerHTML of the list item to be a link and populates the values of that link with values from the JSON feed, such as a result.href for the link to the tag. When each li is created, it’s then added as a string to the fragment, which we will later then append to the tags variable. The most important item here is the inline style tag that uses the number of articles—result.tagged_articles.length—to set a relative font size using em units for this list item. Later, we’ll change that value to a formula to use a basic font scale.
I find this JavaScript just a little bit ugly and hard on the eyes, so let’s create some variables and a simple font scale formula for each of our properties to tidy it up and make it easier to read.
function handleResult(result, highestValue) { // Set our variables const name = result.title; const link = result.href; const numberOfArticles = result.tagged_articles.length; let fontSize = numberOfArticles / highestValue * maxFontSizeForTag; fontSize = +fontSize.toFixed(2); const fontSizeProperty = `$ {fontSize}em`; // Create a list element for each tag and inline the font size const tag = document.createElement("li"); tag.classList.add("tag"); tag.innerHTML = `<a class="tag__link" href="$ {link}" style="font-size: $ {fontSizeProperty}">$ {name} ($ {numberOfArticles})</a>`; // Append each tag to the fragment fragment.appendChild(tag); }
By setting some variables before we get into creating our HTML, the code is a lot easier to read. And it also makes our code a little bit more DRY, as we can use the numberOfArticles variable in more than one place.
Once each of the tags has been returned in this .forEach loop, they are collected together in the fragment. After that, we use appendChild() to add them to the tags element. This means the DOM is manipulated only once, instead of being manipulated each time the loop runs, which is a nice performance boost if we happen to have a large number of tags.
Font scaling
What we have now will work fine for us, and we could start writing our CSS. However, our formula for the fontSize variable means that the tag with the most articles (which is “flex” with 25) will be 6em (25 / 25 * 6 = 6), but the tags with only one article are going to be 1/25th the size of that (1 / 25 * 6 = 0.24), making the content unreadable. If we had a tag with 100 articles, the smaller tags would fare even worse (1 / 100 * 6 = 0.06).
To get around this, I have added a simple if statement that if the fontSize that is returned is less than 1, set the fontSize to 1. If not, keep it at its current size. Now, all the tags will be within a font scale of 1em to 6em, rounded off to two decimal places. To increase the size of the largest tag, just change the value of maxFontSizeForTag. You can decide what works best for you based on the amount of content you are dealing with.
function handleResult(result, highestValue) { // Set our variables const numberOfArticles = result.tagged_articles.length; const name = result.title; const link = result.href; let fontSize = numberOfArticles / highestValue * maxFontSizeForTag; fontSize = +fontSize.toFixed(2); // Make sure our font size will be at least 1em if (fontSize <= 1) { fontSize = 1; } else { fontSize = fontSize; } const fontSizeProperty = `$ {fontSize}em`; // Then, create a list element for each tag and inline the font size. tag = document.createElement("li"); tag.classList.add("tag"); tag.innerHTML = `<a class="tag__link" href="$ {link}" style="font-size: $ {fontSizeProperty}">$ {name} ($ {numberOfArticles})</a>`; // Append each tag to the fragment fragment.appendChild(tag); }
Now the CSS!
We’re using flexbox for our layout since each of the tags can be of varying width. We then center-align them with justify-content: center, and remove the list bullets.
We’ll also use flexbox for the individual tags. This allows us to vertically align them with align-items: center since they will have varying heights based on their font sizes.
I find this is handy on small screens especially for people who might find it harder to tap on links. The initial text-decoration is removed as I think we can assume each item of text in the tag cloud is a link and so a special decoration is not needed for them.
I’ll just drop in some colors to style things up a bit more:
The color scheme for this was stolen directly from Chris’ blogroll, where every fourth tag starting at tag one is yellow, every fourth tag starting at tag two is red, every fourth tag starting at tag three is purple. and every fourth tag starting at tag four is blue.
We then set the focus and hover states for each link:
I could probably have created a custom variable for the colors at this stage—like --yellow: #ffd560, etc.—but decided to go with the longhand approach for IE 11 support. I love the box-shadow hover effect. It’s a very small amount of code to achieve something much more visually-appealing than a standard underline or bottom-border. Using em units here means we have decent control over how large the shadow would be in relation to the text it needed to cover.
OK, let’s top this off by setting every tag link to be black on hover:
It’s called The Clearleft Podcast if you can believe that. It gets into new (at least to me) concepts like Design Ops and Design Sprints, which are loaded terms and need nuanced discussion. It’s really well-edited, pulling in clips from relevant talks and such. A cut above the hit-record-hit-stop ‘n’ polish podcasts that I typically do.
The fellas (Wes and Scott) do a good job of covering the landscape of CSS in 2020. I particularly like the pragmatism here, as they poo-poo on nothing, but rather attempt to pair many of the approaches the discuss to particular development situations.
It’s called Remotely Interesting. You’d think it would be the all-Jamstack-all-the-time show (which I’d totally listen to) but the gang over there tackles broad topics, talking about stuff like blogging, passion, and communication (in addition to Jamstack-y stuff, of course).
Igalia Chats: Web Ecosystem Health
An episode with a very tough discussion about what is good for the web and what isn’t.
Igalia’s Brian Kardell sits down with Jeremy Keith and Stuart Langridge to chat about rendering engine diversity, history and the health of the web browser ecosystem.
Solving Solved Problems
That’s the name of a blog post by Ahmad Nassri that was discussed in an episode of JS Party. It was sweet to hear the backstory of Amal and Ahmad and all the mutual respect there. It’s also valuable to bask in the classic build vs. buy conversations that all software companies need to take seriously.
Mine
Dave and I have been extremely consistent, publishing new episodes of ShopTalk Show every Monday. And Marie publishes new CodePen Radio episodes every week as well.
I’ve bookmarked some icon sets lately, partly because I can never find a nice set when I need to. I figured I’d even go the extra mile here and blog them so I can definitely find them later. Aside from being nice, cohesive, and practical sets of icons, I find it interesting that literally all of them:
are SVG, and thus easily resizeable
are built with rather efficient <path> elements
are stroked instead of filled (at least optionally)
have a click-to-copy SVG feature on their site
are free and open source
Good job, all! Seems like people are coming around to the idea of an SVG icon system where you just… put the SVG in the HTML.
Puppeteer is an Node library for spinning up a copy of Chrome “headlessly” (i.e. no UI) and controlling it. People use it for stuff like taking a screenshot of a website or running integration tests. You can even run it in a Lambda.
Another use case is running synthetic (i.e. not based on real-users) performance tests, like some of these new Web Core Vitals
Addy Osmani lists out a bunch of these “recipes” for measuring certain performance things in Puppeteer. These would be super useful as part of a build process alongside other tests. Did the unit tests pass? Did the integration tests pass? Did the accessibility tests pass? Did the performance metrics tests pass?
BrowserStack released a thing to measure your site and give you a performance score.
You get the tests back super quick which is cool. I can see how tools like this are good for starting conversations with teams about improving performance.
But… that number seems a little weird. They don’t exactly document how it’s calculated, but it seems to be based on stuff like Time to First Byte (TTFB) and the page load event, which aren’t particularly useful performance metrics.
It’s not bad that this tool exists or anything, but I don’t think it’s for practitioners doing performance work.
Karolina Szczur from Calibre documents some common team struggles like, for example, having a team be able to identify real issues from variability noise.
Many people from different backgrounds can view performance dashboards. Not knowing what constitutes a meaningful change that needs investigation can result in false positives, lack of trust in monitoring and cycles spent looking for reasons for performance regressions or upgrades that aren’t there.
50ms. That’s how long until any particular JavaScript task starts affecting user experience. Might as well track and (ideally) fix them.
When the browser’s main thread hits max CPU for more than 50ms, a user starts to notice that their clicks are delayed and that scrolling the page has become janky and unresponsive. Batteries drain faster. People rage click or go elsewhere.
I just can’t stop opening excellent typography-related articles, which means I need to subject you to blog posts that round them up so I can clean up my open tabs.
Vistaserve is “a grass-roots web hosting initiative hailing from Thornbury, Australia. Inspired by the quirky web of the 90s, we allow users to create home pages, your own little sandbox on the World Wide Web, as it were.” Caitlin & Paul (I think the no-last-name thing is part of the aesthetic) wanted to get the fonts right, which meant removing anti-aliasing (the thing that makes fonts look good on screens!). CSS was no help. Turned out to be quite a journey involving literally rebuilding the fonts.
Thomas Bohm makes the point that the kerning around punctation may require special attention. For example, a question mark needing a little extra space or moving a superscript number away from butting against a letter.
You could do it manually with stuff like   or &hairsp in between characters. But I’m far too lazy for that unless I’m working on a very special piece. Personally, I just cross my fingers that the font I’m using is high quality enough to have thought of and implemented this sort of attention to detail.
I’m sure we’ve all seen, “The quick brown fox jumps over the lazy dog” as a tester string for type, because it uses all the characters in the alphabet. Jonathan Hoefler created some new proofing text that is much more helpful for typographers like him.
That’s deep in the type nerd weeds there. More useful perhaps is another recent post from Jonathan on pairings. I’ve probably read dozens of posts on font pairings in my life, but this one resonates the most.
Some of the most dazzling typographic pairings — and certainly my favorites — are those that use unexpected fonts together. At left, the grey flannel suit that is Tungsten Compressed is paired with crimson silk doublet of the St. Augustin Civilité, a fiery sixteenth century typeface that demands a good foil.
If you’ve got macOS Catalina, you’ve got access to some really nice fonts you might not know about that need to be manually downloaded. Ralf Herrmann has the story on what you get:
I get Erik Kennedy’s Learn UI Design newsletter, and he mentions using Calena in it…
Overall, Canela walks this balance between the warmth of human handwriting and stately details. It makes me think of something literary, which is why I used it for project in one of the new video lessons in Learn UI Design.
Type specimens are curious objects. They aim to inspire designers. They are tools with which to make design decisions. They are also marketing material for foundries. This project will dig into specimens from these three perspectives: as artefacts made by and for font designers to evolve type culture; as tools for font users to make decisions about choosing and using type; and as effective marketing tools.
The idea came while watching a mandatory training video on bullying in the workplace. I can just hear High School Geoff LOL-ing about a wimp like me have to watch that thing.
But here we are.
The video UI was actually lovely, but it was the progress bar that really caught my attention – or rather the [progress].value. It was a simple gradient going from green to blue that grew as the video continued playing.
If only I had this advice in high school…
I already know it’s possible to create the same sort gradient on the <progress> element. Pankaj Parashar demonstrated that in a CSS-Tricks post back in 2016.
I really wanted to mock up something similar but haven’t played around with video all that much. I’m also no expert in JavaScript. But how hard can that actually be? I mean, all I want to do is get know how far we are in the video and use that to update the progress value. Right?
My inner bully made so much fun of me that I decided to give it a shot. It’s not the most complicated thing in the world, but I had some fun with it and wanted to share how I set it up in this demo.
The max attribute tells us we’re working with 100 as the highest value while the value attribute is starting us out at zero. That makes sense since it allows us to think of the video’s progress in terms of a percentage, where 0% is the start and 100% is the end, and where our initial starting point is 0%.
Styling
I’m definitely not going to get deep into the process of styling the <progress> element in CSS. The Pankaj post I linked up earlier already does a phenomenal job of that. The CSS we need to paint a gradient on the progress value looks like this:
/* Fallback stuff */ progress[value] { appearance: none; /* Needed for Safari */ border: none; /* Needed for Firefox */ color: #e52e71; /* Fallback to a solid color */ } /* WebKit styles */ progress[value]::-webkit-progress-value { background-image: linear-gradient( to right, #ff8a00, #e52e71 ); transition: width 1s linear; } /* Firefox styles */ progress[value]::-moz-progress-bar { background-image: -moz-linear-gradient( to right, #ff8a00, #e52e71 ); }
The trick is to pay attention to the various nuances that make it cross-browser compatible. Both WebKit and Mozilla browsers have their own particular ways of handling progress elements. That makes the styling a little verbose but, hey, what can you do?
Getting the progress value from a video
I knew there would be some math involved if I wanted to get the current time of the video and display it as a value expressed as a percentage. And if you thought that being a nerd in high school gained me mathematical superpowers, well, sorry to disappoint.
I had to write down an outline of what I thought needed to happen:
Get the current time of the video. We have to know where the video is at if we want to display it as the progress value.
Get the video duration. Knowing the video’s length will help express the current time as a percent.
Calculate the progress value. Again, we’re working in percents. My dusty algebra textbook tells me the formula is part / whole = % / 100. In the context of the video, we can re-write that as currentTime / duration = progress value.
That gives us all the marching orders we need to get started. In fact, we can start creating variables for the elements we need to select and figure out which properties we need to work with to fill in the equation.
// Variables const progress = document.getElementById( "progress" ); // Properties // progress.value = The calculated progress value as a percent of 100 // video.currentTime = The current time of the video in seconds // video.duration = The length of the video in seconds
Not bad, not bad. Now we need to calculate the progress value by plugging those things into our equation.
I’ll admit: I forgot that the equation would result to decimal values. That’s where Math.round() comes into play to update those to the nearest whole integer.
That actually gets the gradient progress bar to animate as the video plays!
I thought I could call this a win and walk away happy. Buuuut, there were a couple of things bugging me. Plus, I was getting errors in the console. No bueno.
Showing the current time
Not a big deal, but certainly a nice-to-have. We can chuck a timer next to the progress bar and count seconds as we go. We already have the data to do it, so all we need is the markup and to hook it up.
Let’s add a wrap the time in a <label> since the <progress> element can have one.
Extra credit would involve converting the timer to display in HH:MM:SS format.
Adding a play button
The fact there there were two UIs going on at the same time sure bugged me. the <video> element has a controls attribute that, when used, shows the video controls, like play, progress, skip, volume, and such. Let’s leave that out.
But that means we need — at the very minimum — to provide a way to start and stop the video. Let’s button that up.
I know it seems weird to take out the rich set of controls that HTML5 offers right out of the box. I probably wouldn’t do that on a real project, but we’re just playing around here.
Cleaning up my ugly spaghetti code
I really want to thank my buddy Neal Fennimore. He took time to look at this with me and offer advice that not only makes the code more legible, but does a much, much better job defining states…
// States const PAUSED = 'paused'; const PLAYING = 'playing'; // Initial state let state = PAUSED;
…doing a proper check for the state before triggering the progress function while listening for the play, pause and click events…