Front End Web Development

Old is Solid; New Gets Talked About

Css Tricks - Tue, 12/15/2020 - 9:52am

When Chris asked me to write about “one thing I learned about building websites this year” I admit my brain immediately went through a list of techniques and CSS properties I started using this year. But then I paused. Other people can write about that much better than I can. What’s something that I specifically have learned?

Then I realized that I’ve been “learning” the same lesson for the last five years, yet I keep falling into the same trap time and time again. I always think that far more people are using the latest, coolest technology out there than there really are.

I think most of you feel the same. If you follow Twitter or any web development blog, it’s almost like everyone is using the latest and the greatest. And the latest and the greatest also seems to change weekly, if not daily. “What’s your favorite React state library? Well it’s Redux, no wait MobX, no wait Unstated, no wait Recoil, no wait, Jotai, no wait, Valtio, no wait…” The constant change can be exhausting, like you’re always falling behind compared to your peers.

But that’s not remotely the case. The vast majority of web developers use “boring” or “old” technology. That makes sense intuitively: most of the stuff on the web today was built …before today. When that needs to be maintained, it’s in the technologies that were in use when it was built. And herein lies the crux: We all maintain old things more than we build new things.

“The best bet for 2030” by CommitStrip

So you feel like everyone else gets to play with cool stuff like “auto-reloading serverless static deploys” while you’re still updating your Grunt configuration. Trust me, there are way more people updating their Grunt configuration right now than doing a serverless static deploy (whatever that may be).

That web dev you admire that’s all-in on Tailwind 2.0? They’re still maintaining a Bootstrap 2.3 website. That JavaScript guru that switches state libraries every week? They’re still maintaining a huge application using Flow. New just gets talked about more often.

I could mention the percentage of websites that run WordPress versus the percentage of sites that run React, but that’s not really the point. If you spend time in the web dev community, it feels like one is old-hat and the other isn’t.

Old can be solid, it can be dependable and it can be predictable. There are times where it’s fun to try new stuff and tell people about it, and there’s times to reach` for the technology you know so you can get stuff done. 

My guess is that I’ll keep thinking, “Well no one really uses $foo anymore,” well into 2021 and beyond—it’s such an automatic thought. But I have to keep reminding myself that it’s wrong. For whatever value of $foo, there are tons of people still using it, and it’s still valuable.

The post Old is Solid; New Gets Talked About appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.

What’s New in WCAG 2.1: Label in Name

Css Tricks - Tue, 12/15/2020 - 5:47am

WCAG 2.1 Recommendations rolled out in 2018. It’s been a couple years now and there are some new Success Criterion. In this article, I will discuss Label in Name, which is how we visually label components. We’ll take a look at what some failure states look like, how to fix them, and examples of how to do them correctly.

You lost me at Success Criterion…

Success Criterion are testable statements that aren’t technology-specific. They’re the baseline from which we determine whether our work is “accessible.” In this case, “Label in Name” is the thing being evaluated, which is among a whole slew of other criterion. WCAG 2.1 is the current version of the spec and “Label in Name” is item 2.5.3, indicating it is in the second category (“Operable”) of criterion, under the fifth section (“Input Modalities”) of that category, marked as the third item in the section.

WCAG 2.1 is backwards-compatible with WCAG 2.0, meaning it’s an extension of WCAG 2.0. Further, the releases of WCAG 2.1 and 2.2 are in conjunction with each other and they all work together.

Label in Name

So, getting back to things, 2.5.3 Label in Name (Level A) is new and defined in the WCAG 2.1 Success Criterion. Here’s how it’s described:

For user interface components with labels that include text or images of text, the name contains the text that is presented visually.

The intent of this Success Criterion (SC) is to ensure the words which a label has visually on the component are also included in the words that are associated with the component programatically. This helps ensure that anyone — whether it’s someone using voice recognition software or someone who is visually abled — can rely on labels to describe the intent of a component, or how to interact with it. The visual text label and the programmatic name do not have to be exact, mind you, but they should contain a common work that associates them (e.g., “Submit” with “Submit Now”).

The point is that there isn’t confusion, because of a discrepancy, between what is read and what is seen.

Assistive technology in action

Let’s use the example of an HTML contact form. A user may use voice recognition software to fill out a form and come to the end where the form is submitted and the form is sent.

Say the label of the button and the visual text in the button are inconsistent:

<form> <label> Message: <textarea name="message"></textarea> </label> <button aria-label="Submit">Send</button> </form>

In the above example, the button doesn’t function properly for assistive technology. The button contains the text “Send” but its aria-label is “Submit.” This is where the failure lies. The visual label (“Send”) is inconsistent with the programmatic name (“Submit”), providing no association between the two.

When these match or have a common term, users of speech recognition software can navigate by speaking the visible text labels of components such as links, buttons, and menus. In this case, we could fix it by matching the label and the text. But since the aria-label adds no value, removing it altogether is a better fix:

<form> <label> Message: <textarea name="message"></textarea> </label> <button>Send</button> </form>

Sighted users that use screen readers will also have a better experience if the text they hear is the text that’s similar to the text they see on the screen.

When the label and visual text don’t match, speech-input users attempting to use the visible text label as a means of navigation (e.g. “move to First Name”) or selection will get stuck because the visible label spoken by the user does not match or is not part of the accessible name that is enabled as part of the speech-input command.

Also, when the accessible name is different from the visible label, it may function as a hidden command that can be activated accidentally by speech-input users. SC does not apply where a visible text label does not exist for a component.

Code in action

Here are three different failure states.

Again, these are all examples of poor practices, according to the 2.5.3 Label in Name SC.

In 2020 the WebAIM Million project evaluated 4.2 million form inputs and found that 55% were improperly unlabeled, either via <label>, aria-label, or aria-labelledby.

When working with forms, most of us are pretty used to pairing a <label> with an <input> or some other form control. That’s awesome and a great way to indicate what the control does, but there’s also the control’s programmatic name, which is also known as the “accessible name” using an aria-label.

We get a better user experience when the name of the <label> can be associated with the programmatic (or accessible) name in the aria-label. For example, if we’re using “First Name” for an input’s <label>, then we probably want our aria-label to be “First Name” or something to that effect as well. A failure to draw a connection between programmatic names and visible labels can be more of a challenge for users with cognitive challenges. It requires additional cognitive load for speech-input users who must remember to say a speech command that is different from the visible label they see on a control. Extra cognitive load is also created when a text-to-speech user needs to absorb and understand speech output that can’t be connected to the visible label. These forms will submit, but it comes at a cost to accessibility and disabled users.

Here are those three examples from above fixed up!

CodePen Embed Fallback Text in Label specifics

Per the WCAG SC, text should not be considered a visible label if it is used in a symbolic manner instead of expressing it directly in human language. A rich text editor is a good example of this because an editor might use images as text (which is included in 1.4.5 Images of Text).

To match the label text and accessible name with one another, it is important to determine which text should be considered the label for any component for any given control. There are often multiple text strings in a user interface that may be relevant to a control. There are reasons why the label in close proximity should be considered the text label. It’s about establishing a pattern of predictability for users interacting with a component. Those reason suggest that visible labels should be positioned:

  • immediately to the left of text inputs, dropdown boxes, and other widgets or components.
  • immediately to the right of checkboxes and radio buttons.
  • inside buttons or tabs or immediately below icons serving as buttons.
Labels to the left of inputs and dropdown select menus

Labels to the right of checkbox and radio buttons Labels inside or below a button, depending on the symbol

Punctuation and capitalization may also be considered optional if used in a symbolic manner. For example, “First Name” is just fine instead of “First Name:” and “Next” is okay instead of “Next…” and so on.

Another thing to consider: components without a visual label are not considered by the WCAG SC.

Proper labeling has its perks

The core benefit of matching a component’s labels with its corresponding accessible name is that it gives speech-input users the ability to activate controls on a page without having to change focus or make guesses between two different terms.

In the end, using clear, consistent terminology between what is seen and what is spoken provides a more enjoyable user experience — for everyone — because the labels that get read by assistive technologies match the visible labels that can also be seen and read. This is what we talk about with inclusive design — everyone wins and no one is left out.


We just broke down some of the finer points of the WCAG 2.5.3 Success Criterion for labels in names. It sounds like a simple thing to follow. But as we’ve seen, there are situations where it’s not so clear-cut.

The goal of adhering to this criterion is, of course, to make our work accessible and inclusive for all people. The WCAG helps us know if we’re successful not only by providing guidelines, but by settings grades of compliance (A, AA, AAA, where AAA is the highest). Text in Label falls into the A grade, meaning it’s a base level of compliance. To earn the grade, the WCAG is looking for:

[…] user interface components with labels that include text or images of text, the name contains the text that is presented visually.

We can test and make certain that our code is complete and correct by looking at the source code of the site, using a browser’s DevTools, such as Chrome or Firefox, or running an accessibility audit using such tools as the WAVE browser extension (Chrome and Firefox) and Axe from Deque Systems (Chrome).

In short, there are real people on the other side of the glass and there are things we can do in our code and designs to help them enjoy interacting with the components we make. Text in Label is just one of many criterion outlined in the WCAG and, while it may seem like a small detail, adhering to it can make a big impact on our users.

The post What’s New in WCAG 2.1: Label in Name appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.

Hell Yes! CSS!

Css Tricks - Tue, 12/15/2020 - 5:45am

Speaking of cool CSS stuff you can buy, Julia Evans’ zine Hell Yes! CSS! is hot off the presses. A “zine” being 28 pages of “short, informative, and fun comics which will quickly teach you something useful.” Some parts of it are like cheat sheets. Some parts of it are like concepts made digestible through the relaxed format. Some parts of it are like mini-tutorials. There is definitely some uhmmmm wow weird moments in there that might stump even CSS smarties, like this inline-block behavior.

Direct Link to ArticlePermalink

The post Hell Yes! CSS! appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.

user-scalable=no and suppressing zoom suppression

QuirksBlog - Tue, 12/15/2020 - 2:31am

The story goes as follows: Once upon a time there was a meta viewport property called user-scalable=no that suppressed pinch zoom on the page it was on. As a result users could not zoom in, even if they needed to. This was obviously a bad idea, and therefore browsers stopped supporting it.

At least, I thought that was the story. Turns out it isn’t.

This weekend I was alerted that suppressing zoom still works in Android browsers, though not on iOS. A quick test determined that Chrome, Edge, Firefox, and UC on Android obey user-scalable=no and do not allow the user to zoom. Only Samsung Internet keeps its cool and allows zooming.

<meta name="viewport" content="width=device-width, user-scalable=no"> Awful. Also, gatekeeping

Just in case you need it spelled out: suppressing user zooming is awful.

Despite the continuing perfection of my 50-year-old eyesight, the vast worldwide conspiracy that is resizing all fonts to smaller and smaller values forces me to zoom in on sites whose clueless designers — who I believe should be relegated to designing physical flyers for third-rate coke-fueled ad agencies, even though that thought makes me a gatekeeper, which is much worse than shitting all over basic web accessibility, so let me stress how wonderfully welcome to our world these designers are — think a 6px font is the most fitting choice.

When I can’t zoom in I get cranky and write snarky comments on my blog.

Technical details

Let’s return to the positive and praise Apple for a short while. On iOS, suppressing zoom has been disabled since iOS 10, though an exception was made for the WebView, because it might not be fitting to zoom a part-native-part-WebView application. Mmmm ... ok, I kind-of see the point here.

Still, it’s possible to turn off zoom suppression programmatically in the WebView, and rather to my surprise the creators of Chrome on iOS — which, remember, uses the iOS WebView, and not Blink, as its rendering engine — chose to do so. Or maybe the default has changed, I don’t know. In any case Apple is mostly on the right track.

So is Samsung; by default, zooming is enabled. Yay! My choice for Samsung Internet as my default mobile browser has been vindicated. I’ll stick to it, thanks so much.

The other Android browsers, Chrome, Edge, UC, and Firefox, all suppress user zooming. The fun part is that the Chromium-based browsers only need user-scalable, while the =no bit is strictly optional. To prove this point I ran a test with user-scalable=anti­disestablish­mentaria­nism. It suppresses zoom in the Chromium browsers. Apparently pinch zoom favours dis­esta­blish­ment.

Firefox has a somewhat more strict syntax, where user-scalable without arguments suppresses zooming, as do the arguments no and 0, but any other value is rejected and does not suppress zooming.

Fortunately an explicit user-scalable=yes or 1 works in all browsers, and zooming is not suppressed.

On the whole, double-tap zooming is also suppressed in these instances. I must admit I did not test this for all possible arguments in all browsers, but since it makes sense that all types of user zooming would be suppressed, I am willing to believe there is no difference between pinch zoom and double-tap zoom.

User settings

I seem to remember that Google said at one point that they’d remove zoom suppression, and, judging from the thread, so did others. I searched and searched, but could only find the 2013 bug report that caused user-scalable to be added to the browser. So I think I just misremember; or maybe it was floated as an idea at one point but never got implemented.

For a moment I thought the default had changed several times, when an old Chrome 47 I found on one phone did allow me to pinch-zoom, but on further investigation it turned out I had switched on the setting that allows me to zoom. After I switched it off zoom suppression was enabled.

Let’s talk about settings. Chrome, Edge, and Firefox allow users to override user-scalable=no and suppress zoom suppression. (Does that last clause sound complicated to you? Congrats, you now understand part of the problem.)

Here are the settings:

  • Chrome on Android: Settings -> Accessibility -> Force enable zoom
  • Edge on Android: Settings -> Accessibility -> Force zoom
  • Firefox on Android: Settings -> Accessibility -> Zoom on all web sites
  • Samsung Internet: Settings -> Appearance -> Control the zoom on web pages; turned ON by default
  • UC on Android: Tools -> Settings (unclear gear icon) -> Browser Settings -> Font & Layout -> Force page to Zoom; BUT turning this on does nothing

Both Samsung Internet and UC have a setting as well, but Samsung Internet’s is turned on by default (i.e. it suppresses zoom suppression), while switching UC’s setting does nothing: zooming is still suppressed.

Point is: no user uses these settings, or will even know where to find them. (Be fair: would you have found them in all browsers? Would you have looked?)

OK, if you have really desperate need you might find the setting you require, but average run-of-the-mill users do not use settings, do not suppress zoom suppression, and don’t even know that they’re missing something.

In order to emulate the true user experience I never touch the settings on my test browsers. You should do the same. You should see what users see.

So user settings are not the solution. The default enabling of zoom suppression is wrong because users will not be aware they can change it.

Google, Mozilla, UC, Microsoft, up your game. Make suppression of zoom suppression the default. Make sure users can always zoom. As an added bonus we can throw all those third-rate designers who cannot design for the web under the train. (Or bus. Or car. I’m flexible.)

Representation Matters

Css Tricks - Mon, 12/14/2020 - 2:05pm

This year I had the pleasure of re-launching The Accessibility Project. I spend a lot of time researching and writing about accessibility and inclusive design, so this felt like the cumulation of a lot of that effort. The site now uses all sorts of cool web features like CSS Grid, @supports, and media features, aria-current, Service Workers, and Eleventy. But that’s really not the important bit.

The important bit I learned this year is the same thing I learn over and over again: When it comes to disability, representation matters.

In my exploration, the importance of representation is a layered truth I find myself re-internalizing as I learn more about the different communities that make up the accessibility space. I am extraordinarily thankful to be welcomed into these communities, and grateful to be able to participate in them. 

We must, however, acknowledge that it is a lot easier for me to enter into these communities than the other way around. Disabled people frequently face many barriers towards representation in many industries, ours included. Considering that, I thought I’d ask disabled people who work on the web what they’ve learned. Here’s what they told me:

Developer Jennilee Rose comments on increased awareness of accessibility in the framework space:

As an advocate for accessibility in web design/development, something I’ve noticed (not exactly learned per-say, but a trend I’ve seen) is that in probably the last 2-3 years there has been a shift in prioritizing accessibility in JavaScript libraries. I think some of it is that there are devs out there like me who care and are holding devs who create these libraries accountable and helping to create change.

Software Engineer Nadhim Orfali comments on their experience working with design systems, accessibility, and documentation:

After a company-wide shift to Vue, it’s easier and faster for teams to adopt the design system. Due to the release of scoped packages along with CI/CD architecture and intertwined with documentation, the process is more streamlined with most of the accessibility built-in. I’m seeing teams much more aware and interested in all matters relating to accessibility, which can only be a good thing!

User experience designer Francis C. Rupert comments on how quarantine has affected everyone:

In 2020 everyone was struck with a shared Situational Disability by everyone wearing a mask. Hearing loss isn’t necessarily always about volume, but speech discrimination. We collectively lost the ability to distinguish between consonants and vowels, and everyone else sounds garbled through their masks.

Speaking of quarantine, web designer Jen Diaz tells us about some benefits that come with remote work becoming mainstream:

Clients are super-keen to work with remote business partners now that they have little or no choice not to. Which is great — it truly levels the playing field. On a Zoom call, nobody knows that my hands are shaped like lobster claws or that I physically cannot participate in the company bowling league — both things that have raised eyebrows for me at in-office jobs.

Anne Berlin, technical SEO and web manager, also comments on some remote work benefits:

I don’t have to worry about someone coming in with strong fragrance, which can send my brain into haywire. I can control the light level of the room and brightness level of my monitors, and a bit more control over the noise level.

It’s not all good vibes, however. Web developer Olu also chimes in about remote work: 

Quarantine has also shown how adaptable companies can be when their backs are against the wall. It’s funny how disabled people can ask for accommodations for years and then when they have no choice suddenly these accommodations are becoming permanent for everyone. 

Anne also mentions:

Pressure to pass as abled due to ignorance about “invisible” disabilities or lack of proactively inclusive culture for a range of things including cognitive styles, or issues with overstimulation.

Disability is more than physical access. Managing Director Josh Clayton mentions the cognitive fatigue that comes with framework churn:

The continued use of React is concerning. The JavaScript ecosystem is fragmented, with a lot of people doing a lot of work and nobody producing anything new. I don’t mind investing in technology where I feel like I’m getting something. There’s just so much churn, I don’t bother to keep up. I would like to think it’s not sacrificing future employability if I was going to look for a new job, but when it comes to other person’s money, there’s the “newshiny,” or “is this actually the right thing you should be doing right now?”

Developer EJ Mason doesn’t mince words:

What I have learned about the industry is that it is unrepentantly ableist.

While I’m happy to see progress being made on some fronts, we need to understand that doing technical work to make websites accessible is only part of the picture. We need to realize that usable products can be created in exclusionary spaces. Only by including disabled people in the product creation process can we truly improve as an industry.

Thank you to Jennilee Rose, Nadhim Orfali, Francis C. Rupert, Jen Diaz, Anne Berlin, Olu, Josh Clayton, EJ Mason, and everyone else who shared their experiences with me.

The post Representation Matters appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.

Netlify & Next.js

Css Tricks - Mon, 12/14/2020 - 12:23pm

Cassidy Williams has been doing a Blogvent (blogging every day for a month) over on the Netlify Blog. A lot of the blog posts are about Next.js. There is a lot to like about Next.js. I just pulled one of Cassidy’s starters for fun. It’s very nice that it has React Fast-Refresh built-in. I like how on any given “Page” you can import and use a <Head> to control stuff that would be in a <head>. This was my first tiny little play with Next so, excuse my basicness.

But the most compelling thing about Next.js, to me, is how easily it supports the entire rendering spectrum. It encourages you to do static-file rendering by default (smart), then if you need to do server-side rendering (SSR), you just update any given Page component to have this:

export async function getServerSideProps() { // Fetch data from external API const res = await fetch(`https://.../data`) const data = await res.json() // Pass data to the page via props return { props: { data } } }

The assumption is that you’re doing SSR because you need to hit a server for data in order to render the page, but would prefer to do that server-side so that the page can render quickly and without JavaScript if needed (great for SEO). That assumes a Node server is sitting there ready to do that work. On Netlify, that means a function (Node Lambda), but you barely even have to think about it, because you just put this in your netlify.toml file:

[[plugins]] package = "@netlify/plugin-nextjs"

Now you’ve got static where you need it, server-rendered where you need it, but you aren’t giving up on client-side rendering either, which is nice and fast after the site is all booted up. I think it shoots some JSON around or something, framework magic.

I set up a quick SSR route off my homepage to have a play, and I can clearly see that both my homepage (static) and /cool route (SSR) both return static HTML on load.

I even had to prettify this source, as you HTML minification out of the box

I admit I like working in React, and Next.js is a darn nice framework to do it with because of the balance of simplicity and power. It’s great it runs on Netlify so easily.

The post Netlify & Next.js appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.

Not Much

Css Tricks - Mon, 12/14/2020 - 10:35am

What’s one thing I learned about building websites this year? Not all that much.

This year, unlike most previous years, I didn’t explore a lot of new technologies. For obvious reasons, it’s been a difficult year to be as engaged in the hot new topics and to spend time playing around with new things. So, for the most part, I’ve tried to keep calm and carry on.

That said, I did try a couple of things that were new to me. I built my first React application (after stubbornly holding out for so long). This was a bit of a struggle for me, coming from using a fully fledged framework, like Angular, to a strictly UI library, like React, and I learned a lot about the difference between libraries and frameworks in the process.

I also trialled Tailwind to see what a radically different way of writing CSS (or in this case, not writing CSS) could be like. This was a really fun trial and although I’ve gone back to my good-ole-SCSS, I have taken with me the idea of utility CSS classes. For example, I now create utility classes for things like aligning text, so the line text-align: center; is only written once in my CSS file.

But ultimately, I’ve found that my way of building websites hasn’t changed all that much this year. And more importantly, it didn’t have to.

The post Not Much appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.

Debugging CSS

Css Tricks - Mon, 12/14/2020 - 10:32am

High five to Ahmad Shadeed for releasing his new book, Debugging CSS. I think that’s a neat angle for a book on CSS. There are a ton of books on the general subject of CSS already, so not that they can’t be fresh takes on that, but this feels equally important and less trodden territory.

Browser DevTools help us a ton these days in debugging CSS, but there isn’t exactly a step-by-step guide about about it that I know of. This book leans into that, showing off how to debug really practical and understandable CSS issues with the help of DevTools when appropriate.

I bought it because I’m a CSS nerd obviously — not really because I feel like I need to bone up on my CSS debugging. I really wanna support Ahmad because he really got the #1 gold star ⭐️ for CSS blogging this year. Every post of his was an extremely well done deep dive into some important area of CSS.

Direct Link to ArticlePermalink

The post Debugging CSS appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.

MDN on GitHub

Css Tricks - Mon, 12/14/2020 - 8:33am

Looks like all the content of MDN is on GitHub now. That’s pretty rad. That’s been the public plan for a while. Chris Mills:

We will be using GitHub’s contribution tools and features, essentially moving MDN from a Wiki model to a pull request (PR) model. This is so much better for contribution, allowing for intelligent linting, mass edits, and inclusion of MDN docs in whatever workflows you want to add it to (you can edit MDN source files directly in your favorite code editor).

Looks like that transition is happening basically today, and it’s a whole new back-end and front-end architecture.

Say you wanted to update the article for :focus-within. There will be a button on that page that takes you to the file in the repo (rather than the wiki editing page), and you can edit it from the GitHub UI (or however you like to do Git, but that seems like right-on-GitHub will be where the bulk of editing happens). Saving the changed document will automatically become a Pull Request, and presumably, there is a team in place to approve those.

We think that your changes should be live on the site in 48 hours as a worst case scenario.

Big claps from me here, I think this is a smart move. I can’t speak to the tech, but the content model is smart. I’d maybe like to see the content in Markdown with less specialized classes and such, but I suspect that kind of thing can evolve over time and this is already a behemoth of an update to ship all at once.

In August 2020, the entire MDN (writers) team was laid off, so it looks like the play here is to open up the creation and editing of these technical docs to the world of developers. Will it work? It super didn’t work for the “Web Platform Docs” (remember those?). But MDN has way more existing content, mindshare, and momentum. I suspect it will work great for the maintenance of existing docs, decently for new docs on whatever is hot ‘n’ fresh, but less so for anything old and “boring”.

Seems a little risky to fire all the writers before you find out if it works, so that speaks to the product direction. Things are changing and paying a content creation team directly just ain’t a part of whatever the new direction is.

The post MDN on GitHub appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.

Give Users Control: The Media Session API

Css Tricks - Mon, 12/14/2020 - 5:47am

Here’s a scenario. You start a banging Kendrick Lamar track in one of your many open browser tabs. You’re loving it, but someone walks into your space and you need to pause it. Which tab is it? Browsers try to help with that a little bit. You can probably mute the entire system audio. But wouldn’t it be nice to actually have control over the audio playback without necessarily needing to find your way back to that tab?

The Media Session API makes this possible. It gives media playback access to the user outside of the browser tab where it is playing. If implemented, it will be available in various places on the device, including:

  • the notifications area on many mobile devices,
  • on other wearables, and
  • the media hub area of many desktop devices.

In addition, the Media Session API allows us to control media playback with media keys and voice assistants like Siri, Google Assistant, Bixby, or Alexa.

The Media Session API

The Media Session API mainly consists of the two following interfaces:

  • MediaMetadata
  • MediaSession

The MediaMetadata interface is what provides data about the playing media. It is responsible for letting us know the media’s title, album, artwork and artist (which is Kendrick Lamar in this example). The MediaSession interface is what is responsible for the media playback functionality.

Before we take a deep dive into the topic, we would have to take note of feature detection. It is good practice to check if a browser supports a feature before implementing it. To check if a browser supports the Media Session API, we would have to include the following in our JavaScript file:

if ('mediaSession' in navigator) { // Our media session api that lets us seek to the beginning of Kendrick Lamar's &quot;Alright&quot; } The MediaMetadata interface

The constructor, MediaMetadata.MediaMetadata() creates a new MediaMetadata object. After creating it, we can add the following properties:

  • MediaMetadata.title sets or gets the title of the media playing.
  • MediaMetadata.artist sets or gets the name of the artist or group of the media playing.
  • MediaMetadata.album sets or gets the name of the album containing the media playing.
  • MediaMetadata.artwork sets or gets the array of images related with the media playing.

The value of the artwork property of the MediaMetadata object is an array of MediaImage objects. A MediaImage object contains details describing an image associated with the media. The objects have the three following properties:

  • src: the URL of the image
  • sizes: indicates the size of the image so one image does not have to be scaled
  • type: the MIME type of the image

Let’s create a MediaMetadata object for Kendrick Lamar’s “Alright” off his To Pimp a Butterfly album.

if ('mediaSession' in navigator) { navigator.mediaSession.metadata = new MediaMetadata({ title: 'Alright', artist: 'Kendrick Lamar', album: 'To Pimp A Butterfly', artwork: [ { src: 'https://mytechnicalarticle/kendrick-lamar/to-pimp-a-butterfly/alright/96x96', sizes: '96x96', type: 'image/png' }, { src: 'https://mytechnicalarticle/kendrick-lamar/to-pimp-a-butterfly/alright/128x128', sizes: '128x128', type: 'image/png' }, // More sizes, like 192x192, 256x256, 384x384, and 512x512 ] }); } The MediaSession interface

As stated earlier, this is what lets the user control the playback of the media. We can perform the following actions on the playing media through this interface:

  • play: play the media
  • pause: pause the media
  • previoustrack: switch to the previous track
  • nexttrack: switch to the next track
  • seekbackward: seek backward from the current position, by a few seconds
  • seekforward: seek forward from the current position, by a few seconds
  • seekto: seek to a specified time from the current position
  • stop: stop media playback
  • skipad: skip past the advertisement playing, if any

The MediaSessionAction enumerated type makes these actions available as string types. To support any of these actions, we have to use the MediaSession’s setActionHandler() method to define a handler for that action. The method takes the action, and a callback that is called when the user invokes the action. Let us take a not-too-deep dive to understand it better.

To set handlers for the play and pause actions, we include the following in our JavaScript file:

let alright = new HTMLAudioElement(); if ('mediaSession' in navigator) { navigator.mediaSession.setActionHandler('play', () => {; }); navigator.mediaSession.setActionHandler('pause', () => { alright.pause(); }); }

Here we set the track to play when the user plays it and pause when the user pauses it through the media interface.

For the previoustrack and nexttrack actions, we include the following:

let u = new HTMLAudioElement(); let forSaleInterlude = new HTMLAudioElement(); if ('mediaSession' in navigator) { navigator.mediaSession.setActionHandler('previoustrack', () => {; }); navigator.mediaSession.setActionHandler('nexttrack', () => {; }); }

This might not completely be self-explanatory if you are not much of a Kendrick Lamar fan but hopefully, you get the gist. When the user wants to play the previous track, we set the previous track to play. When it is the next track, it is the next track.

To implement the seekbackward and seekforward actions, we include the following:

if ('mediaSession' in navigator) { navigator.mediaSession.setActionHandler('seekbackward', (details) => { alright.currentTime = alright.currentTime - (details.seekOffset || 10); }); navigator.mediaSession.setActionHandler('seekforward', (details) => { alright.currentTime = alright.currentTime + (details.seekOffset || 10); }); }

Given that I don’t consider any of this self-explanatory, I would like to give a concise explanation about the seekbackward and seekforward actions. The handlers for both actions, seekbackward and seekforward, are fired, as their names imply, when the user wants to seek backward or forward by a few number of seconds. The MediaSessionActionDetails dictionary provides us the “few number of seconds” in a property, seekOffset. However, the seekOffset property is not always present because not all user agents act the same way. When it is not present, we should set the track to seek backward or forward by a “few number of seconds” that makes sense to us. Hence, we use 10 seconds because it is quite a few. In a nutshell, we set the track to seek by seekOffset seconds if it is provided. If it is not provided, we seek by 10 seconds.

To add the seekto functionality to our Media Session API, we include the following snippet:

if ('mediaSession' in navigator) { navigator.mediaSession.setActionHandler('seekto', (details) => { if (details.fastSeek && 'fastSeek' in alright) { alright.fastSeek(details.seekTime); return; } alright.currentTime = details.seekTime; }); }

Here, the MediaSessionActionDetails dictionary provides the fastSeek and seekTime properties. fastSeek is basically seek performed rapidly (like fast-forwarding or rewinding) while seekTime is the time the track should seek to. While fastSeek is an optional property, the MediaSessionActionDetails dictionary always provides the seekTime property for the seekto action handler. So fundamentally, we set the track to fastSeek to the seekTime when the property is available and the user fast seeks, while we just set it to the seekTime when the user just seeks to a specified time.

Although I wouldn’t know why one would want to stop a Kendrick song, it won’t hurt to describe the stop action handler of the MediaSession interface:

if ('mediaSession' in navigator) { navigator.mediaSession.setActionHandler('stop', () => { alright.pause(); alright.currentTime = 0; }); }

The user invokes the skipad (as in, “skip ad” rather than “ski pad”) action handler when an advertisement is playing and they want to skip it so they can continue listening to Kendrick Lamar’s “Alright track. If I’m being honest, the complete details of the skipad action handler is out of the scope of my “Media Session API” understanding. Hence, you should probably look that up on your own after reading this article, if you actually want to implement it.

Wrapping up

We should take note of something. Whenever the user plays the track, seeks, or changes the playback rate, we are supposed to update the position state on the interface provided by the Media Session API. What we use to implement this is the setPositionState() method of the mediaSession object, as in the following:

if ('mediaSession' in navigator) { navigator.mediaSession.setPositionState({ duration: alright.duration, playbackRate: alright.playbackRate, position: alright.currentTime }); }

In addition, I would like to remind you that not all browsers of the users would support all the actions. Therefore, it is recommended to set the action handlers in a try...catch block, as in the following:

const actionsAndHandlers = [ ['play', () => { /*...*/ }], ['pause', () => { /*...*/ }], ['previoustrack', () => { /*...*/ }], ['nexttrack', () => { /*...*/ }], ['seekbackward', (details) => { /*...*/ }], ['seekforward', (details) => { /*...*/ }], ['seekto', (details) => { /*...*/ }], ['stop', () => { /*...*/ }] ] for (const [action, handler] of actionsAndHandlers) { try { navigator.mediaSession.setActionHandler(action, handler); } catch (error) { console.log(`The media session action, ${action}, is not supported`); } }

Putting everything we have done, we would have the following:

let alright = new HTMLAudioElement(); let u = new HTMLAudioElement(); let forSaleInterlude = new HTMLAudioElement(); const updatePositionState = () => { navigator.mediaSession.setPositionState({ duration: alright.duration, playbackRate: alright.playbackRate, position: alright.currentTime }); } const actionsAndHandlers = [ ['play', () => {; updatePositionState(); }], ['pause', () => { alright.pause(); }], ['previoustrack', () => {; }], ['nexttrack', () => {; }], ['seekbackward', (details) => { alright.currentTime = alright.currentTime - (details.seekOffset || 10); updatePositionState(); }], ['seekforward', (details) => { alright.currentTime = alright.currentTime + (details.seekOffset || 10); updatePositionState(); }], ['seekto', (details) => { if (details.fastSeek && 'fastSeek' in alright) { alright.fastSeek(details.seekTime); updatePositionState(); return; } alright.currentTime = details.seekTime; updatePositionState(); }], ['stop', () => { alright.pause(); alright.currentTime = 0; }], ] if ( 'mediaSession' in navigator ) { navigator.mediaSession.metadata = new MediaMetadata({ title: 'Alright', artist: 'Kendrick Lamar', album: 'To Pimp A Butterfly', artwork: [ { src: 'https://mytechnicalarticle/kendrick-lamar/to-pimp-a-butterfly/alright/96x96', sizes: '96x96', type: 'image/png' }, { src: 'https://mytechnicalarticle/kendrick-lamar/to-pimp-a-butterfly/alright/128x128', sizes: '128x128', type: 'image/png' }, // More sizes, like 192x192, 256x256, 384x384, and 512x512 ] }); for (const [action, handler] of actionsAndHandlers) { try { navigator.mediaSession.setActionHandler(action, handler); } catch (error) { console.log(`The media session action, ${action}, is not supported`); } } }

Here’s a demo of the API:

CodePen Embed Fallback

I implemented six of the actions. Feel free to try the rest during your leisure.

If you view the Pen on your mobile device, notice how it appears on your notification area.

If your smart watch is paired to your device, take a sneak peek at it.

If you view the Pen on Chrome on desktop, navigate to the media hub and play with the media buttons there. The demo even has multiple tracks, so you experiment moving forward/back through tracks.

If you made it this far (or not), thanks for reading and please, on the next app you create with media functionality, implement this API.

The post Give Users Control: The Media Session API appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.

HTTP Archive’s Annual State of the Web Report

Css Tricks - Mon, 12/14/2020 - 5:38am

The HTTP Archive looked at more than 7 million websites and compiled their annual report detailing how the sites were built. And there’s an enormous wealth of information about how the web changed in 2020. In fact, this report is more like an enormous book and it’s entirely fabulous. The data comes from making queries to the HTTP Archive and is broken down into various sections, such as Performance, Security, and the languages themselves, including how folks wrote HTML or CSS.

Here’s what the report has to say about the CSS they scanned:

While JavaScript far surpasses CSS in its share of page weight, CSS has certainly grown in size over the years, with the median desktop page loading 62 KB of CSS code, and 1 in 10 pages loading more than 240 KB of CSS code. Mobile pages do use slightly less CSS code across all percentiles, but only by 4 to 7 KB. While this is definitely greater than previous years, it doesn’t come close to JavaScript’s whopping median of 444 KB and top 10% of 1.2 MB

Gasp! And here’s a shocking bit of info that shortly follows:

[…] only about 7% of pages concentrate all their CSS code in one remote stylesheet, as we are often taught to do. In fact, the median page contains 3 <style> elements and 6 (!) remote stylesheets, with 10% of them carrying over 14 <style> elements and over 20 remote CSS files! While this is suboptimal on desktop, it really kills performance on mobile, where round-trip latency is more important than raw download speed.

I sort of want to quote the whole section about CSS specifically because there’s a lot of interesting facts that show how we, as a community, have a lot of work to do to improve performance and spread the good word about CSS optimization.

Direct Link to ArticlePermalink

The post HTTP Archive’s Annual State of the Web Report appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.

Dragons & Unicorns

Typography - Sat, 12/12/2020 - 2:36am

Read the book, Typographic Firsts

For more than a thousand years the meaning of Egyptian hieroglyphs was completely lost. For centuries, many assumed that they were magical symbols that might never be understood by mere mortals. The breakthrough only came with the discovery of a 2,200-year-old black basalt slab. But what does that have to do with typography, dragons and unicorns? Read on to find out.

The post Dragons & Unicorns appeared first on I Love Typography.

Make it Personal

Css Tricks - Fri, 12/11/2020 - 1:35pm

One thing I noticed about building websites in 2020: despite all the social networks and publishing platforms craving our content, our stories, and our attention, people are somehow still building personal websites. Over the course of the year, many of you have launched or relaunched your website. It indeed feels like the personal website is experiencing a little revival.

To me, this comes as no surprise. The benefits of having your own personal site are enormous and appealing. As a creator, publishing on your own website might even be the single best thing you can do, both professionally and personally.

Your voice

On your personal website, you own your work. You decide what and when to publish. You decide when to delete things. You are in control. Your work, your rules, your freedom.

Your personal website is also a wonderful playground to tinker, prototype, experiment, explore, and learn about web standards and new technologies by experience. Want to improve your accessibility skills? Want to learn CSS Grid or try out variable fonts? Want to implement your first Service Worker or fine-tune the performance of your site? Your personal website is the perfect place for that!

But the prime reason to have a personal website is in the name: it is your personal home on the web. Since its early days, the web has been about sharing information and freedom of expression. Personal websites still deliver on that promise. Nowhere else do you have that much freedom to create and share your work and to tell your personal story. It is your chance to show what you stand for, to be different, and to be specific. Your site lets you be uniquely you and it can be whatever you imagine it to be.

So if you have a personal site, make sure to put in the work and attention to make it truly yours. Make it personal. Fine-tune the typography, add a theme switcher, or incorporate other quirky little details that add personality. As Sarah Drasner writes, you can feel it if a site is done with care and excitement. Those are the sites that are a joy to visit and will be remembered.



Your turn

Two things are crucial if you want to get started with your own personal site.

  1. You have to start. You can totally start with something small and basic. But start. Start a blog with WordPress, use a static site generator like Eleventy, a lovely flat-file CMS like Kirby, or code everything from scratch. That’s up to you. The tech stack doesn’t matter that much as long as you start.
  2. Be aware that your site is not “done” once you launch it. After the launch is when the journey begins. Many creators forget that and struggle with the fact that you have to maintain a site and publish work there. That’s why it is important to be really clear from the beginning in deciding what you want to publish and then build your site around that. Do you want to write? Great! Then focus on the reading experience and create a blog section. You want to post photos or illustrations? Amazing! Then make sure that the layout and structure of your site are built for this and it is easy to publish new images. If you know what you want to publish, it becomes much easier to make creating the work a habit and publishing it on your website part of your practice.

Remember, you don’t have to consider yourself a writer to write on your site. You don’t have to be a programmer to write code on your site. You don’t have to be a renowned expert to have an opinion. Document your process and share the things you learn. Try out different formats, different styles, and different topics. Write about one concept or one idea at a time. Or, if you love tricks, publish your best tricks. The journey is the destination and there might always be someone who benefits from even one of your smallest posts.

Publishing your work can be scary, especially in the beginning. It might feel like you are being judged. Everyone can inspect your source code, see your tiny mistakes, and notice when you get things awfully wrong. But that’s part of the process, too. And it’s this process that makes building, maintaining, and growing your personal site so worthwhile. Allow yourself to be vulnerable, push beyond your fear, and hit Publish. When you publish regularly on your personal site, it will inevitably grow. And so will you.

Your tribe

There is a community of creators out there, eagerly waiting for you to contribute your point of view. We are implementing Webmentions on our sites, adding blogrolls to our sites again, and we might even bring webrings back. We subscribe to each other’s feeds and we are sharing and quoting each other’s work and articles. In 2021, and in the years to come, we need you and many more to keep the Web open, independent, and diverse. Hidde de Vries recently shared this quote on his personal site which he had read on Jeremy Keith’s personal site:

If you have something wonderful, if you do not defend it, you will lose it.

Zeynep Tefepkçi (via


Personal websites are such a thing. If you don’t have a website yet, come join us on the other side where the web is (still) personal. It’s worth it.

The post Make it Personal appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.

There is No Normal

Css Tricks - Fri, 12/11/2020 - 1:30pm

This year I learned, or relearned maybe, that “normal” is subjective at best, and pretty misleading otherwise. If this forsaken year has taught us anything, it’s that there is no such thing as normal. Things change. People adapt. Everything is relative to everything else.

Besides being quite metaphysical, this somewhat connects to front-end development, our industry, and the sort of expectations we have for people down there. Too often, it feels like we tend to apply our own insecurities onto others.

I learned this way and so should you.

I know about this and you should too.

I don’t care about this therefore it’s not necessary.

This behavior needs to stop. We have to acknowledge that this industry is, for the most part, self-made and therefore does not have a clear learning path, let alone a defined success trajectory. As they say, Your Mileage May Vary™.

Let this message be for everyone who’s just starting in this field, everyone fresh out of bootcamp or university, and everyone with insecurities or impostor syndrome. This is for everyone who feels like they are not enough and don’t belong here.

You belong here.

Your experience, no matter how different than someone else’s, is valid.

There is no definitive list of skills to have. There is no mandatory technologies to look for. You don’t have to keep chasing the hype. At the end of the day, knowing HTML, CSS and some JavaScript should be enough to make do. You will learn the rest in due time. You have your entire career to learn new things. There is no rush to learn everything right now.

Breathe. Enjoy what you already know. You are doing well. Welcome to the craft.

The post There is No Normal appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.

Unconventional Stock Image Sources

Css Tricks - Thu, 12/10/2020 - 12:42pm

This year, I learned that there is a wide world of free stock imagery available beyond Unsplash and Pexels. You see, I’ve been working on designing WordPress themes this year, and all images need to be compatible with the GPL. Unsplash and Pexels both have free and open licenses, but unfortunately, aren’t compatible. Many other free stock photos sites don’t have the highest quality photos, so I’ve had to get creative about where I get the imagery I use in my mockups.

I discovered the solution to my stock imagery problem, ironically, on Unsplash. I started noticing photos from sources like the British Library, Birmingham Museums Trust, and Library of Congress. Who often has archives of public domain imagery? Libraries, museums, and governments. The sites are never a site like Unsplash, but they work well if you have the time and patience to dive through their archives. Plus? You can find some pretty cool photography, art, and illustrations that have a very different vibe than most stock photo resources.


There are tons of libraries with license-compatible digital archives, such as the New York Public Library, Library of Congress, The State Library of New South Wales, National Library of Ireland, and many more. Try seeing if a major city with your state has a digital archive. Libraries are great for old photos, advertisements, and illustrations that I’ll use in portfolio site designs.


Many museums have started digitizing their collections in the past few years, such as the Smithsonian’s National Museum of Asian Art, the Met, and the Art Institute of Chicago. As the museums are often digitizing the work themselves, they have the luxury of releasing the images into the public domain. Museums are a fantastic resource for art, and for photos of objects like ceramics and jewelry that work well in e-commerce mockups.


US Government agencies like NASA tend to release a ton of their own media for public use, and I’ve discovered that space images look great in blog post mockups. Need some COVID photos? The CDC’s got you covered. Need some black & white nature photos? Check out the National Park Service’s “Open Parks Network.” 

Finding high-quality, totally free stock imagery can be a huge hassle. But I’ve found, with some creativity and some patience, there are far more options than I knew!

The post Unconventional Stock Image Sources appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.

It’s all relative.

Css Tricks - Thu, 12/10/2020 - 12:36pm

I remember sitting in the back seat of our family’s Subaru station wagon. I was six and this was long before child carseats were a thing. My dad was at the wheel and my mom played 20 Questions with me while we drove to some vacation spot I can’t even remember.

It was my mom’s turn as she and I played 20 Questions. She had an object in mind and I was asking the questions.

“Is it big?” I asked.

“Relative to what?” my mom replied.

This was the pattern throughout the entire game. I asked a question and my mom answered with a question about relativity.

“You can ask questions when it’s my turn to think of an object,” I’d say.

“If you’re asking if the thing is as big as a mountain, the answer is no. But next to ant, yes, it is big.”

This year has been a long stretch of re-learning what it means to think relatively. How long of a stretch? It’s relative, I suppose. But as 2020 comes to a close, I can almost hear my mom asking me the same question, whether it’s at work or in my personal life.

“Relative to what?” she asks.

My mind jumps to relative units in CSS when I hear the word “relative.” I’m sure many of you are the same. I reach for things like em, rem and % all the time in my work. I only started using them to be cool when I first learned about them (whenever that was). I didn’t even know there was a real difference between em and rem. I thought they had something to do with retina screens.

Of course, that was a long time ago. (How long? It’s relative.) I now know that relative units are relative to the thing they reference. 2rem is going to evaluate one way on a site with a root font size of 16px and another for a site at 24px. Same deal with percentages. 50% inside a 400px container means something different than 50% inside a 1200px container.

So, now, when I find myself assigning size values to elements, I first having a little dialogue with my mom.

“This element is 5.25em.”

“Relative to what?” she asks.

I’ve also learned that thinking relatively requires a little perspective. And, no, this has nothing to do with CSS perspective (although I could probably try to make that connection). Thinking in relative terms means momentarily stepping out of your own shoes and seeing things from something else’s vantage point.

I say “something” because I think about this most when writing code. Whenever I’m working on the pieces of a component, I have to be mindful of the context—or perspective—of where they sit. Why? Because the pieces mean different things in different contexts and those contexts are relative to the component that contains them.

When is an <h2> just an <h2>? Hardly ever. It might be the post name. Or perhaps the heading for a widget. Maybe it’s the heading for a card component. As front-enders, we name those things according to the perspective of the component. From the perspective of a post, an <h2> means (and probably looks) something different from the perspective of, say, a card.

.post {} .post__title {} .widget {} .widget__title {} .card {} .card__title {}

Naming things is hard. I often find myself thinking, “Ack! What the heck should I call this thing?”

“Relative to what?” my mom interjects.

I could go on and on. The truth is that thinking in terms of relativity is just as important to the code we write as it is to a game of 20 Questions, or even our personal lives. And in a year where we’ve been upended by so many competing forces, thinking along these lines can offer solace and wisdom in the midst of what has been stressful and frustrating for many of us—relatively, of course.

  • “This is stressing me out.” Relative to what?
  • “I have so much to do.” Relative to when?
  • “I suck as JavaScript.” Relative to whom?
  • “I hate the place I work.” Relative to where?

It’s easy to get caught up in absolutes. Relativity forces us to see things differently.

The post It’s all relative. appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.

Optimize Images According to Network and Device Constraints in React

Css Tricks - Thu, 12/10/2020 - 12:16pm

Connectivity has evolved beyond recognition since the beginning of the internet. We are lightyears past dial up, these days, and can watch a video in high resolution on our smartphone while being connected to a mobile network. But not all mobile connections are created equal – older generation networks (3G, 2G, etc.) are still quite dominant, accounting for almost half of all connections worldwide in 2020.

Unfortunately, the phasing out process is very slow, and many people around the globe are experiencing really dragged out page loads, comparable to the very early days of home internet adoption.

Modern websites became resource-hungry, featuring lots of images and animations. For a visitor on an underpowered device and a fragile network connection, an average webpage might take a good minute to load completely. This is largely due to the fact that developers often make binary decisions when it comes to user’s hardware and network conditions: devices fall either in the desktop or smartphone category, while connectivity is a question of being on- or offline. In reality, user’s circumstances tend to be much more nuanced.

We Can Do Better?

What can be done to bridge the gap for users on modest devices and spotty connections? First, we need to do a quick evaluation of what exactly their conditions are by looking at the following two properties:

Based on that, we can decide, for instance, to adjust the quality of the images we intend to serve. There is a catch, however, with Jamstack websites and apps rendered on the server – `navigator`object, as any other browser API, isn’t available during the rendering stage. A common workaround for this issue is to add a bunch of responsive image markup, but it comes with a significant pain point – inefficient scaling. An image CDN like ImageEngine helps to avoid this and other pitfalls associated with responsive images as it handles all the heavy-lifting behind the scenes by applying automated, smart tweaks to requested resources on-the-fly.

When it comes to adapting to a user’s network constraints, one could detect connection type and instruct an image CDN to vary compression according to connection quality. Here’s how one might go about it in React:

import React, { useState, useEffect } from 'react' const useConnectionType = (defaultConnectionType) => { const isSupported = navigator?.connection?.effectiveType ? true : false const [connectionType, setNetworkStatus] = useState( isSupported ? navigator.connection.effectiveType : defaultConnectionType ) useEffect(() => { if (isSupported) { const { connection } = navigator const updateConnectionType = () => { setNetworkStatus(connection.effectiveType) } connection.addEventListener('change', updateConnectionType) return () => { connection.removeEventListener('change', updateConnectionType) } } }, []) return [ connectionType, setNetworkStatus ] } const imageCDNHost = ' function ConnectionAwareComponent () { const [ connectionType ] = useConnectionType() let compressionLevel = 0 switch (connectionType) { case 'slow-2g': compressionLevel = 65 break case '2g': compressionLevel = 50 break case '3g': compressionLevel = 30 break case '4g': compressionLevel = 0 break } return ( <div> {/* Apply variable compression via dedicated directive */} <img src={`${imageCDNHost}/?imgeng?=cmpr_${compressionLevel}`} /> </div> ) }

One can take this idea even further to accommodate those on really sluggish and wonky networks by rendering blurred images and offering an option to download a higher resolution version on demand. Or devise a performance score system and adjust what is sent based on that. 

On the other hand, the fact that the user is on a “speedy” 4G connection doesn’t necessarily mean they aren’t interested in saving data as they might be accessing a website in roaming. Enabling Client Hints on one’s website will let site owners detect the presence of a data saver flag and take necessary steps to adjust to the user’s preferences.

Reasons for Faster Images

Mediocre CPU, modest amounts of memory and a low-grade connection aren’t imaginary constraints. They pose real user experience challenges potentially affecting hundreds of millions of users worldwide. Some companies began to bake inclusive experiences into their products: streaming services like Netflix and Spotify adjust the streaming quality based on your network conditions, while many others are doing automatic image optimizations behind the scenes for users.

Developing regions, where fast networks aren’t yet accessible to everyone and everywhere, might not be one’s target market. Meanwhile, someone browsing from a rural area in a developed country will likely have a jarring experience if they are served a fully-fledged version of a website. We can be more considerate and intentional by adjusting what we send / display to our users with only a couple of small tweaks.

Using an image CDN like ImageEngine simplifies the image optimization process and automatically responds to the Client Hints for network constraints. The result is a better experience for a network-constrained visitor and an elegant workflow for developers.

The post Optimize Images According to Network and Device Constraints in React appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.

Using CSS Custom Properties to Adjust Variable Font Weights in Dark Mode

Css Tricks - Thu, 12/10/2020 - 9:34am

Black isn’t always slimming.

When recently testing a dark mode option for one of my sites, I experienced first-hand the issue that Robin Rendle addresses in this article. All of my page text — headings and body copy — appeared to bulk up when I switched to dark mode. And it didn’t matter what fonts I used or which browsers I tried. The same thing happened with all of them.

For example, here’s what happens with Adobe’s Source Sans Pro in Chrome for Windows:

See those blurry edges when we switch to dark mode?

It’s not an illusion. The light characters really are heavier against dark backgrounds. We can zoom in to see better:

The characters really are thicker in dark mode!

And it becomes really obvious when we invert the dark mode portions of those images:

We can really see the difference when putting the characters side-by-side on the same white background. One solution

Since variable fonts enjoy wide browser support, we can use them to help us address this issue. The three panels below demonstrate a solution we’ll be working toward:

The top shows us some light text on a dark background. The middle panel shows what happens in dark mode without changing any font weight settings. And the bottom panel demonstrates dark mode text that we’ve thinned out a bit. That third panel is adjusted to match the weight of its light counterpart, which is what we’re trying to accomplish here.

Here’s how we can get this improved effect:

  1. Reduce font-weight properties in dark mode via one of the following methods:
    1. Manually changing each font-weight assignment directly in a dark mode media query.
    2. Creating a single --font-weight-multiplier custom property that changes its value in dark mode, and by which we can then multiply by each element’s default font-weight value.
    3. Same thing, but instead of calculating each element’s font-weight property individually, we take advantage of CSS variable scoping and the universal selector (*) to apply our multiplier calculation everywhere at once.
  2. Adjust a variable font’s grade (“GRAD”) axis. Not all variable fonts support this specific feature, but Roboto Flex, does. Altering this axis value changes the font’s apparent weight, without affecting the width of the letters.
  3. Adjust a variable font’s darkmode ("DRKM") axis. Dalton Maag’s aptly-named Darkmode, with its eponymous darkmode axis, is uniquely suited for this. As with the Roboto Flex’s grade axis, adjusting Darkmode’s darkmode axis changes the font’s apparent weight. But while the grade axis requires some fine-tuning of values, the darkmode axis is simply switched on (thinner) or off (regular).

The techniques in the first group work for most variable fonts. The solution Robin uses in his article is actually the very first item in the group. I’ll expand on the second and third items in the group by introducing custom properties that help us automatically adjust font weights in dark mode.

The second and third groups involve less common font-variation-settings axes. Though these strategies apply to fewer typefaces, they may be preferable when available. The trick is knowing what a variable font supports before choosing it.

I’ve made a demonstration page including all the strategies covered in this article. You can see what some different variable fonts look like in light mode, in dark mode with no adjustment, and in dark mode with our solutions for thinning out characters.

View Demo

In addition to the strategies listed above, there’s always one more option: don’t do anything! If you think your fonts look good enough in light and dark mode, or you don’t have the bandwidth right now to wrestle with reflow, element resizing, browser/display inconsistencies, and extra CSS to maintain, then you may not have to change a thing. Focus on the rest of your site and leave yourself open to the possibility of revisiting this topic later.

Strategy 1: Reducing the font-weight value

Most variable text fonts have a weight axis, which lets us assign any specific font-weight value within the weight range available to that font (e.g. 0-1000, 300-800, etc.). Each technique in this strategy takes advantage of this fine control over the weight axis to reduce font-weight values in dark mode. (The need for such font-weight precision is also what renders most non-variable fonts unsuitable for this solution.)

If you’re using variable fonts you have locally, you can check their axes and value ranges at Wakamai Fondue:

At Wakamai Fondue, you can view any local font’s variable axes and ranges.

Keep in mind that, if you’re using the @font-face rule to load fonts, you should set a font-weight range for each of them at the same time:

@font-face { src: url('Highgate.woff2') format('woff2-variations'); font-family: 'Highgate'; font-weight: 100 900; }

If you neglect this step, some variable fonts may not properly reflect specific font-weight values in current Chromium browsers.

Dalton Maag Highgate’s font-weight set to 800 in Chrome without (left) and with (right) a font-weight range specified in the @font-face rule. The basic solution: Manually entering each weight

Here’s the technique most of us may reach for. We create a dark mode media query in which we enter some font-weight values that are a bit lower than their defaults.

/* Default (light mode) CSS */ body { font-weight: 400; } strong, b, th, h1, h2, h3, h4, h5, h6 { font-weight: 700; } /* Dark mode CSS */ @media (prefers-color-scheme: dark) { body { font-weight: 350; } strong, b, th, h1, h2, h3, h4, h5, h6 { font-weight: 600; } } CodePen Embed Fallback

It works, and it’s no problem to maintain — so long as we’re not planning on adding or editing any other weights at our site! But if we do start incorporating more weights, it can get unwieldy fast. Remember to enter each selector/property combo both outside and inside the prefers-color-scheme media query. We’ll have to do some manual calculations (or guesswork) to determine the dark mode property values for each element.

Creating a weight multiplier custom property and using it in a calculation when setting an element’s weight

I generally try to adhere to Mike Riethmuller’s credo that “media queries are only used to change the value of custom properties.” And that’s the improvement we make in this solution. Instead of having to enter font weights for all our elements in and out of dark mode, the only thing we’re putting in our media query is a --font-weight-multiplier custom property:

@media (prefers-color-scheme: dark) { :root { --font-weight-multiplier: .85; } }

Then, for all our font-weight properties throughout the stylesheet, we’ll multiply the variable’s value by our preferred default weight value for each element — thus lowering the font weight by 15% in dark mode. If we’re not in dark mode, we’ll multiply the default weight by 1, meaning it doesn’t change at all.

Here’s what I mean. Normally, we’d use this to set a body font weight of 400:

body { font-weight: 400; }

For this solution, we use this:

body { font-weight: calc(400 * var(--font-weight-multiplier, 1)); }

In the var() function, notice that our variable has a fallback value of 1. Because --font-weight-multiplier is only set in dark mode, this fallback value will be used the rest of the time. So, by default, our font weight for body text stays at 400 (400*1). But in dark mode, the weight decreases to 340 (400*.85).

We’ll also do this with bold elements:

strong, b, th, h1, h2, h3, h4, h5, h6 { font-weight: calc(700 * var(--font-weight-multiplier, 1)); }

These weights will decrease from 700 to 595 (700*.85) in dark mode.

And we can use the same technique for any other elements where we want to set the font-weight to something other than 400 by default.

I’m using a value of .85 for --font-weight-multiplier, because I’ve found that to be a good general value for most fonts (like Adobe Source Sans Pro, the free typeface I use in most of this article’s demos). But feel free to play around with that number.

Here’s how this looks put together:

/* DARK-MODE-SPECIFIC CUSTOM PROPERTIES */ @media (prefers-color-scheme: dark) { :root { --font-weight-multiplier: .85; } } /* DEFAULT CSS STYLES... */ body { font-weight: calc(400 * var(--font-weight-multiplier, 1)); } strong, b, th, h1, h2, h3, h4, h5, h6 { font-weight: calc(700 * var(--font-weight-multiplier, 1)); } CodePen Embed Fallback Creating a weight multiplier variable and automatically calculating and applying it to all elements at once.

When using many CSS custom properties, I think many of us stick to a “set as needed and manually apply everywhere” approach. That’s what the previous solution does. We set our custom property value in the :root (and/or use a fallback value), set it again in a media query, then apply it with calc() and var() functions throughout our stylesheet each time we assign a font-weight value.

The code might look something like this:

h1 { font-weight: calc(800 * var(--font-weight-multiplier, 1); } summary { font-weight: calc(600 * var(--font-weight-multiplier, 1); }

But when we use this technique for various elements, you can see we have to do these three things every time we assign font-weight values:

  • Include the calc() function
  • Include the var() function
  • Remember the --font-weight-multiplier custom property’s name and default value

Instead, I’ve recently started inverting this approach for certain tasks, taking advantage of CSS variable scope with a “set everywhere and apply once” method. For this technique, I replace every font-weight property in the stylesheet with a --font-weight variable, keeping the name the same except for the dashes, for simplicity’s sake. I then set this value to the default weight for that particular selector (e.g. 400 for body text). Neither calc() nor var() is needed — yet. This is how we set everywhere.

Then we apply once, with a lone font-weight property in our stylesheet that sets every text element’s weight via the universal selector. Modifying our snippet above, we’d now have this:

h1 { --font-weight: 800; } summary { --font-weight: 600; } * { font-weight: calc(var(--font-weight, 400) * var(--font-weight-multiplier, 1); }

The calc() function multiplies each of our --font-weight custom properties by our multiplier variable, and the font-weight property then applies the value to its appropriate element.

It’s unnecessary to use only a single var() for each custom property in the stylesheet. But I often like doing so when performing calculations and/or using a helper variable, as we do here. That said, while this is certainly the cleverest technique for adjusting font weights, that doesn’t mean it’s the best technique for all projects. There is at least one serious caveat.

The primary advantage of using the universal selector technique — that it applies to everything — also introduces its chief risk. There may be some elements that we don’t want thinned out! For example, if our form elements retain dark text on light backgrounds in dark mode, they may still get steamrolled by the universal selector.

There are ways to mitigate this risk. We could replace * with a long selector string containing a list of only elements to thin out (having them opt-in to the calculation). Or we could hard-code font weights for the elements that we don’t want affected (opt-out):

* { font-weight: calc(var(--font-weight, 400) * var(--font-weight-multiplier, 1)); } button, input, select, textarea { font-weight: 400; }

Such fixes may ultimately make code just as complicated as the previous technique. So, you’ll have to gauge which is appropriate for your project. If you still have concerns over performance, code complexity, or think this technique might introduce undesired (even unpredictable) results, the previous technique might be safest.

The final code:

/* DEFAULT CUSTOM PROPERTIES */ :root { --font-weight: 400; --font-weight-multiplier: 1; } strong, b, th, h1, h2, h3, h4, h5, h6 { --font-weight: 700; } /* DARK-MODE-SPECIFIC CUSTOM PROPERTIES */ @media (prefers-color-scheme: dark) { :root { --font-weight-multiplier: .85; } } /* APPLYING THE CUSTOM PROPERTIES... */ * { font-weight: calc(var(--font-weight, 400) * var(--font-weight-multiplier, 1)); } CodePen Embed Fallback

We’re not required to set the default --font-weight: 400 and --font-weight-multiplier: 1 custom properties in the above code, because we’ve included the fallback values in the var() functions. But as code gets more complicated, I often like assigning them in a logical place, just in case I want to find and alter them later.

A final note on this strategy: we can also apply weights with the font-variation-settings property and a "wght" axis value, instead of font-weight. If you’re using a typeface with several axes, maybe you find it more manageable to do all your font tweaking that way. I know of at least one font (Type Network’s Roboto Flex, which we’ll be using later in this article) that has 13 axes!

Here’s how to apply our solution via a font-variation-settings property:

* { --wght: calc(var(--font-weight, 400) * var(--font-weight-multiplier, 1)); font-variation-settings: "wght" var(--wght); } Strategy 1 Addendum: Handling letter-spacing

One side effect of lowering our type weight is that, for most non-monspaced fonts, it also narrows the characters.

Here again is what happens when we lighten Source Sans Pro with our multiplier. The top two panels below show Source Sans Pro in light and dark mode by default. And the lower panel shows the lighter version.

Adobe’s Source Sans Pro in light mode, dark mode by default, and dark mode thinned out.

With no adjustments, the characters in light mode and dark mode are the same width. But when we lower the font weight, those characters now take up less space. You may not like how this change affects your flow or element sizes (e.g. narrower buttons). And some designers think it’s a good idea to add letter spacing in dark mode, anyway. So, if you want, you can create another custom property to add some space.

Implementing a custom property for letter spacing

Just like we did with our font-weight multiplier variable, we’re going to create a letter spacing variable with a default value that gets overridden in dark mode. In our default (light mode) :root, we set our new --letter-spacing custom property to 0 for now:

:root { /* ...other custom variables... */ --letter-spacing: 0; }

Then, in our dark mode query, we raise the value to something greater than 0. I’ve entered it as .02ch here (which combines pretty well with a --font-weight-multiplier value of .85). You could even get clever and fine-tune it with some calculations based on your font weights and/or sizes, if you like. But I’ll use this hard-coded value for now:

@media (prefers-color-scheme: dark) { :root { /* ...other custom variables... */ --letter-spacing: .02ch; } }

Finally, we apply it via our universal selector (with a fallback value of 0):

* { /* ...other property settings... */ letter-spacing: var(--letter-spacing, 0); }

Note: Though I use the ch unit in this example, using em also works, if you prefer. For Source Sans Pro, a value of .009em is about equal to .02ch.

Here’s the full code for a font weight multiplier with letter spacing:

/* DEFAULT CSS CUSTOM PROPERTIES */ :root { --font-weight: 400; --font-weight-multiplier: 1; --letter-spacing: 0; } strong, b, th, h1, h2, h3, h4, h5, h6 { --font-weight: 700; } /* DARK MODE CSS CUSTOM PROPERTIES */ @media (prefers-color-scheme: dark) { :root { /* Variables to set the dark mode bg and text colors for our demo. */ --background: #222; --color: #fff; /* Variables that affect font appearance in dark mode. */ --font-weight-multiplier: .85; --letter-spacing: .02ch; } } /* APPLYING CSS STYLES... */ * { font-weight: calc(var(--font-weight, 400) * var(--font-weight-multiplier, 1)); letter-spacing: var(--letter-spacing, 0); } body { background: var(--background, #fff); color: var(--color, #222); } CodePen Embed Fallback Fonts with constant-width characters (aka multi-plexed fonts)

In addition to monospaced fonts, there are some other typefaces specifically designed so that their individual characters take up the same amount of horizontal space, regardless of weight. For example, if an “i” occupies five horizontal pixels of space at a weight of 400, and a “w” occupies thirteen pixels at the same weight, they will still occupy five and thirteen pixels, respectively, when their weights are increased to 700.

Arrow Type’s Recursive Sans is one such typeface. The following image shows how Recursive’s characters maintain the same widths in light mode, default dark mode, and dark mode with our font weight multiplier, respectively:

The characters in Arrow Type’s Recursive maintain their widths regardless of font weight.

Multi-plexed typefaces, like Recursive, are designed so you won’t need to adjust letter spacing when changing their font weights in dark mode. Your element sizes and page flow will remain intact.

Strategy 2: Adjust a variable font’s grade axis

The grade axis ("GRAD") changes a font’s apparent weight without changing its actual font-weight value or the widths of its characters. When using fonts with this axis, you may not need our font weight multiplier variable at all.

For Type Network’s free Roboto Flex font, a grade of -1 is thinnest, 0 (default) is normal, and 1 is thickest. With this font, I start by assigning its grade axis a value of around -.75 for dark mode.

Roboto Flex in light mode, dark mode default, and dark mode with “GRAD” set to -.75 :root { --GRAD: 0; } @media (prefers-color-scheme: dark) { :root { --GRAD: -.75; } } body { font-variation-settings: "GRAD" var(--GRAD, 0); } CodePen Embed Fallback

So, adjusting the grade axis seems like the perfect solution if it’s available to you, right? Well, maybe. There are a few things to keep in mind when considering it.

First, the scale for all fonts doesn’t always go from -1 to 1. Some range from 0 to 1. At least one typeface uses percents, making 100 the default. And other fonts align the grade scale with font weights, so the range may be something like 100-900. If you want to use the grade axis in the latter case, you may have to set all your font weights everywhere to a default of 400, and then use the grade axis for all weight changes. For dark mode, you’ll then want to treat grade essentially like we do in our font weight multiplier solution — applying the multiplier to the "GRAD" axis in font-variation settings.

The second caveat is that some typefaces don’t let you grade a font to a value below its default weight. So, grade can’t lighten it at all. Apple’s San Francisco typeface (which can be tested via font-family: system-ui; on Apple devices) has both of these issues. As of macOS Catalina, San Francisco has a grade axis. It’s scaled to line up with font weights, and its minimum value is 400.

San Francisco’s grade and weight axes use the same scale, but have different ranges.

Because we can’t set the grade to a value lower than 400, we can’t lighten fonts from a default of 400 in dark mode. If we want to go lower, we’ll need to lower the weight axis value, instead.

Strategy 3: Adjusting a variable font’s darkmode axis

There’s currently only one typeface with a darkmode ("DRKM") axis at the time of this writing: Dalton Maag’s Darkmode.

The darkmode axis is essentially a grade axis without any fine-tuning. Just turn it on (1) for a thinner appearance in dark mode, and leave it off (0, the default) for normal display.

Darkmode in light mode, in dark mode with “DRKM” unset, and in dark mode with “DRKM” set to 1. :root { --DRKM: 0; } @media (prefers-color-scheme: dark) { :root { --DRKM: 1; } } body { font-variation-settings: "DRKM" var(--DRKM, 0); } CodePen Embed Fallback

I like the Darkmode font a lot. But beware that it is a commercial license that’s required for professional use. Dalton Maag offers a trial version that can be used for “academic, speculative, or pitching purposes only.” I’m hoping this typeface is a pilot for more Dalton Maag families to get a darkmode axis, and that other font foundries will then follow suit!

Other factors to consider

We’ve covered a few big strategies for working with variable fonts in a dark mode context. But, as with most things, there are other things to consider that might sway you toward one solution or another.

Dark mode on high-resolution (“retina”) screens

On screens with higher pixel densities (e.g. most modern phones, MacBooks, iMacs, etc.), the thickening effect of dark mode is often less pronounced. Therefore, you may not want to thin the fonts on these screens out as much — if at all!

If you still want to lighten fonts a bit, you can add another media query to make the effect less severe. Depending which solution you’re using, you can raise the --font-weight-multiplier value closer to 1, raise the --GRAD value closer to 0, or disable --DRKM altogether (since it’s either on or off, with no in-between).

If you add this query, remember to place it below the original prefers-color-scheme query, or it may have no effect. Media queries don’t add CSS specificity, so their order matters!

@media (prefers-color-scheme: dark) and (-webkit-min-device-pixel-ratio: 2), (prefers-color-scheme: dark) and (min-resolution: 192dpi) { :root { --font-weight-multiplier: .92; /* Or, if you're using grade or darkmode axis instead: */ /* --GRAD: -.3; */ /* --DRKM: 0; */ } }

If you don’t want to lighten fonts at all on high density screens in dark mode, you can update your original dark mode prefers-color-scheme query to the following, to omit these screens:

@media (prefers-color-scheme: dark) and (-webkit-max-device-pixel-ratio: 1.9), (prefers-color-scheme: dark) and (max-resolution: 191dpi) { /* Custom properties for dark mode go here. */ } Mixing fonts with different axes (and mixing variable fonts with non-variable fonts)

If you’re using more than one typeface on your site, you’ll need to consider what effects these adjustments may have on all of them. For example, if you’re using multiple fonts with intersecting axes, you could wind up accidentally combining the effects of multiple strategies (e.g. reducing both grade and weight simultaneously):

If your stylesheet includes solutions for several typefaces/axes, then the effect on fonts that have multiple axes (like this example’s Roboto Flex, which has both grade and weight axes) may be cumulative. CodePen Embed Fallback

If all the fonts on your site are variable and have a grade axis with a matching scale and range (e.g. if they all range from -1 to 1), that’s the solution I’d recommend. However, you’ll have to revisit this if you plan to add other fonts later that don’t meet those criteria. Same goes for the darkmode axis, too, if it becomes more widespread.

If all your fonts are variable, but they don’t all share the same axes (e.g. grade and darkmode), then using only the --font-weight-multiplier custom property may be your safest bet.

Finally, if you’re mixing variable and non-variable fonts, know that the non-variable fonts will not change appearance with any of these solutions — with some exceptions. For example, if you’re using the font weight multiplier with the font-weight property, it is possible that some — but maybe not all — of your font weights will change enough to move to the next lower weight name.

Say your site includes a font with three weights: regular (400), semi-bold (600), and bold (700). In dark mode, your bold text may lighten up enough to display as semi-bold. But your regular font will still stay regular (as that’s the lowest weight included on the site). If you want to avoid that inconsistency, you could apply your variable font weights via font-variation-settings, and not font-weight, so your non-variable fonts aren’t affected at all. They’ll just always maintain their default weight in dark mode.

In closing

It’s always a happy coincidence when complementary technologies attain common usage near the same time. With the rise in popularity of both dark mode and variable fonts, we have the luxury of using the latter to mitigate one of the challenges of the former. Using CSS custom properties in conjunction with weight, grade, and darkmode axes, we can bring some consistency to the look of our text in both light and dark modes.

You can visit my interactive demo with the fonts and axes from this article.

The post Using CSS Custom Properties to Adjust Variable Font Weights in Dark Mode appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.

2020 was not a good year for learning

Css Tricks - Wed, 12/09/2020 - 1:30pm

There, I said it.

What did I learn about building websites in 2020? A lot. But what I learned is not nearly as important as how I learned it. So instead, I want to share a couple of strategies I used to unblock learning in less-than-ideal times.

I spent almost a decade teaching design and, let me tell you, the conditions for curiosity were all wrong this year. You are not alone if you’ve found yourself battling brain fog, deep existential crisis, and long spans of nothingness instead of basking in a creative renaissance. I spent most of this year in a tiny apartment under a terrifying lockdown in epicenter-of-the-pandemic New York with my husband, two cats, and a very energetic toddler. I’ll save the details for a therapist, but let’s just say this year did not go as planned.

But then again, whose year did? The entire world plunged into crisis. I only feel deep gratitude for having weathered this storm and for having cultivated the skills I needed for my little family to thrive despite the chaos. 

I spent years speaking at conferences about mental models and growing creativity. This year, I’ve had to focus on the other side of that coin: helping people recover their creativity when it feels out of reach. The first thing I usually recommend to anyone feeling creatively blocked is that they start actively wasting their time.

Waste your time

I had ambitious plans for 2020, having plotted out a rigorous study plan for myself. That plan is now in the garbage next to my travel plans and willingness to wear anything but yoga pants.

What I am capable of after a good night’s sleep, coffee in hand, is one thing. What I can manage after a sleepless night followed by a full day of Zoom meetings while simultaneously trying to wrangle a toddler is another thing entirely.

The loss of childcare meant that the only time to indulge in learning was after a long day of work after my daughter was tucked into bed, and I was completely exhausted. The last thing I wanted was disciplined study in pursuit of a goal, so I changed my approach: focus on breadth, widen the scope of topics, let my mind wander, and feed my curiosity. As a bonus, this shift often kept me from losing the evening to bingeing Netflix or doom-scrolling the news late into the night.

When you’re low on motivation or approaching burnout, create the opportunity to “waste time” and play. Follow what interests you, not just the things you “should” be interested in. Do not let your velocity or productivity enter the equation – respect that a life of learning is much more complex than that. Practice curiosity and protect your ability to play, it is critical to learning and creating. If you’re curious about why that is, look up inquiry-based or constructivist learning. What you’ll learn will have nothing to do with building websites, and that’s the point.


Many talented people felt their ability to create disappear this year, for a variety of reasons. I want to be clear — that’s a normal human reaction to all of this. There is no “one weird trick” for unblocking productivity when the world is on fire. But, if building websites brings you the joy and/or money you need, and you want some advice from someone trained in jump-starting learning, I’ll say this: revisit your foundations. Not the foundations, but your foundations. 

When things are this uncertain for this long, we reach for coping mechanisms. Why not use one to unblock learning? In a recent New York Times article, psychologists said that ‘conjuring nostalgia during stressful times is a healthy coping mechanism,” and I totally agree. Between re-watching 90’s movies and cooking comfort foods, I dove back into what brought me to tech: design, CSS, color functions, typography, design patterns. I played around in Illustrator, dusted off old repos, looked at happy projects, and remembered old trends. I avoided burning energy on the hot new thing. Instead, I revisited the foundations of my understanding through the lens of experience.

I’m not suggesting you stay in your comfort zone, but I am saying it’s okay to start there. The important thing is starting.

Routines and Structures

There is disruption on every level: global, national, and personal. I don’t know anyone who hasn’t experienced a disruption to their lives. When our routines are disrupted and our basic needs (including health and psychological) are threatened, we are unable to learn well. One of the most effective things a teacher can do in the classroom is to make sure their students eat breakfast, so focus on your needs. Getting a good night’s sleep contributes to learning. Caring for your mental health contributes to learning. Punishing yourself for struggling does not.

This year, as all of my support structures disappeared, there was not enough Natalya to do everything and be there for everyone. Accepting that was difficult. I spent a lot of energy shifting my focus from “the year I wished for” to the one I was experiencing. I used every trick in the teacher’s handbook on myself to keep my love of learning intact, feel some progress, and keep up. No matter how much I did, I still felt like I didn’t do enough. It was just the kind of year when everything felt like it took more energy (because it did). Setting up new routines and structures is work. Factor that into your equation when evaluating your progress.

This is where I would usually rant about how teaching and childcare are real skills and difficult jobs, dedicating several paragraphs on how important and valuable the work of teachers and caregivers is. But anyone who has been homeschooling or caring for someone this year is already well aware.

Creating the right conditions for effective learning is a lot of work, so give yourself credit if you’ve been taking it on.

This has been a deeply unsustainable year for so many. Showing up and making it through each day and making any progress is enough. Not giving up and trying again, day after day, is success.

What I learned…

Ah, the reason you’re reading this. I would love to talk about the cool design and tech stuff I learned this year, but I won’t. What I learned as an individual in 2020 is not nearly as important as what I hope we learned as an industry.

I hope we learned that the most important thing to know about building websites is that there are real people building them, and we need to make sure they can thrive.

Lunch perks and swag are cool, but what about childcare, accessibility, and support structures? What about flexible hours and remote-first practices? We should notice that the companies which supported people before the pandemic are doing better than the ones struggling to pivot to do the work they should have been doing all along.

I wish it didn’t take a global pandemic, but we need change. Let’s use this knowledge to build a better society with stronger principles and more thoughtful structures—not just better websites.

The post 2020 was not a good year for learning appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.

npm ruin dev

Css Tricks - Wed, 12/09/2020 - 5:52am

In 2020, I rediscovered the enjoyment of building a website with plain ol’ HTML, CSS, and JavaScript — no transpilin’, no compilin’, no build tools other than my hands on the keyboard.

Seeing as my personal brand could be summed up “so late to the game that the stadium has been demolished,” I decided to start a podcast in 2020. It’s the podcast of my agency, Clearleft, and it has been given the soaringly imaginative title of The Clearleft Podcast. I’m really pleased with how the first season turned out. I’m also really pleased with the website I put together for it.

The website isn’t very big, though it will grow with time. I had a think about what the build process for the site should be and after literally seconds of debate, I settled on a build process of none. Zero. Nada.

This turned out to be enormously liberating. It felt very hands-on to write the actual HTML and CSS that will be delivered to end users, without any mediation. I felt like I was getting my hands into the soil of the site.

CSS has evolved so much in recent years—with features like calc()  and custom properties—that you don’t have to use preprocessors like Sass. And vanilla JavaScript is powerful, fully-featured, and works across browsers without any compiling.

Don’t get me wrong—I totally understand why complicated pipelines are necessary for complicated websites. If you’re part of a large team, you probably need to have processes in place so that everyone can contribute to the codebase in a consistent way. The more complex that codebase is, the more technology you need to help you automate your work and catch errors before they go live.

But that setup isn’t appropriate for every website. And all those tools and processes that are supposed to save time sometimes end up wasting time further down the road. Ever had to revisit a project after, say, six or twelve months? Maybe you just want to make one little change to the CSS. But you can’t because a dependency is broken. So you try to update it. But it relies on a different version of Node. Before you know it, you’re Bryan Cranston changing a light bulb. You should be tweaking one line of CSS but instead, you’re battling entropy.

Whenever I’m tackling a problem in front-end development, I like to apply the principle of least power: choose the least powerful language suitable for a given purpose. A classic example would be using a simple HTML button element instead of trying to recreate all the native functionality of a button using a div with lashings of ARIA and JavaScript. This year, I realized that this same principle applies to build tools too.

Instead of reaching for all-singing all-dancing toolchain by default, I’m going to start with a boring baseline. If and when that becomes too painful or unwieldy, then I’ll throw in a task manager. But every time I add a dependency, I’ll be limiting the lifespan of the project.

My new year’s resolution for 2021 will be to go on a diet. No more weighty node_modules folders; just crispy and delicious HTML, CSS, and JavaScript.

The post npm ruin dev appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.

Syndicate content
©2003 - Present Akamai Design & Development.