Month: June 2020

Tips on Designing a Cross-Platform Mobile App

Five 5-minute Videos from Ethan on Design & Accessibility


I’ve been working with Aquent Gymnasium to produce a series of five short tutorial videos, which have been launching over the course of this past week. Since the last video just went live, I’m thrilled to share the whole list with you:

Introduction to using VoiceOver on macOS
Designing beautiful focus states
Flexible and accessible typesetting
Responsively designing with viewport units
Designing beautiful and accessible drop caps

Five minutes is a real sweet spot for a how-to video. Ain’t no time to screw around. I loved every minute of these.

Direct Link to ArticlePermalink

The post Five 5-minute Videos from Ethan on Design & Accessibility appeared first on CSS-Tricks.


, , , , , ,

Responsive Styling Using Attribute Selectors

One of the challenges we face when implementing class-based atomic styling is that it often depends on a specific breakpoint for context.

<div class="span-12"></div> <!-- we want this for small screens  --> <div class="span-6"></div>  <!-- we want this for medium screens --> <div class="span-4"></div>  <!-- we want this for large screens  -->

It’s common to use a prefix to target each breakpoint:

<div class="sm-span-12 md-span-6 lg-span-4"></div>

This works well until we start adding multiple classes. That’s when it becomes difficult to keep a track what relates to what and where to add, remove. or change stuff.

<div class="   sm-span-12    md-span-6    lg-span-4    sm-font-size-xl    md-font-size-xl    lg-font-size-xl    md-font-weight-500    lg-font-weight-700"> </div>

We can try to make it more readable by re-grouping:

<div class="   sm-span-12    sm-font-size-xl  
   md-span-6    md-font-size-xl    md-font-weight-500  
   lg-span-4    lg-font-size-xl    lg-font-weight-700"> </div>

We can add funky separators (invalid class names will be ignored):

<div class="   [    sm-span-12     sm-font-size-xl    ],[    md-span-6     md-font-size-xl     md-font-weight-500    ],[    lg-span-4     lg-font-size-xl     lg-font-weight-700   ]"> </div>

But this still feels messy and hard to grasp, at least to me.

We can get a better overview and avoid implementation prefixes by grouping attribute selectors instead of actual classes:

<div    sm="span-12 font-size-lg"   md="span-6 font-size-xl font-weight-500"   lg="span-4 font-size-xl font-weight-700" > </div>

These aren’t lost of classes but a whitespace-separated list of attributes we can select using [attribute~="value"], where ~= requires the exact word to be found in the attribute value in order to match.

@media (min-width: 0) {  [sm~="span-1"] { /*...*/ }                [sm~="span-2"] { /*...*/ }     /* etc. */  } @media (min-width: 30rem) {  [md~="span-1"] { /*...*/ }     [md~="span-2"] { /*...*/ }     /* etc. */    } @media (min-width: 60rem) {  [lg~="span-1"] { /*...*/ }     [lg~="span-2"] { /*...*/ }     /* etc. */    }

It may be a bit odd-looking but I think translating atomic classes to  attributes is fairly straightforward (e.g. .sm-span-1 becomes [sm~="span-1"]). Plus, attribute selectors have the same specificity as classes, so we lose nothing there. And, unlike classes, attributes can be written without escaping special characters, like /+.:?.

That’s all! Again, this is merely an idea that aims to make switching declarations in media queries easier to write, read and manage. It’s definitely not a proposal to do away with classes or anything like that.

The post Responsive Styling Using Attribute Selectors appeared first on CSS-Tricks.


, , , ,

A Designer’s Guide to Protecting Your Eyes at Work


Styling Layout Wrappers In CSS

Two things that strike me often about the web are how many ways there are to go about the same thing and how many considerations go into even the most seemingly simple things.

Working with wrapper elements is definitely on both those lists. Wrappers (or containers or whatever) are so common — especially when establishing grid layouts and boundaries for the elements inside them — that it’s easy to take them for granted and reach for them without stepping back to consider how they work, why we use them, and how to use them effectively.

Ahmed Shadeed wrote up the most exhaustive article on wrappers I’ve ever read. He provides a brief overview of them before diving into a bunch of considerations and techniques for working with them, including:

  • When to use them
  • How to size them
  • Positioning them
  • Adding margin and padding
  • Working with CSS grid and other display values
  • Breaking out of the wrapper
  • Using CSS custom properties

If you take the images from the article, it tells a pretty cool story.

Direct Link to ArticlePermalink

The post Styling Layout Wrappers In CSS appeared first on CSS-Tricks.


, ,

When Sass and New CSS Features Collide

Recently, CSS has added a lot of new cool features such as custom properties and new functions. While these things can make our lives a lot easier, they can also end up interacting with preprocessors, like Sass, in funny ways.

So this is going to be a post about the issues I’ve encountered, how I go around them, and why I still find Sass necessary these days.

The errors

If you’ve played with the new min() and max() functions, you may have ran into an error message like this when working with different units: “Incompatible units: vh and em.”

Screenshot. Shows the `Incompatible units: 'em' and 'vh'` error when trying to set `width: min(20em, 50vh)`.
An error when working with different types of units in the min()/ max() function

This is because Sass has its ownmin() function, and ignores the CSS min() function. Plus, Sass cannot perform any sort of computation using two values with units that don’t have a fixed relation between them.

For example, cm and in units have a fixed relation between them, so Sass can figure out what’s the result of min(20in, 50cm) and doesn’t throw an error when we try to use it in our code.

The same things goes for other units. Angular units, for example, all have a fixed relation between them: 1turn, 1rad or 1grad always compute to the same deg values. Same goes for 1s which is always 1000ms, 1kHz which is always 1000Hz, 1dppx which is always 96dpi, and 1in which is always 96px. This is why Sass can convert between them and mix them in computations and inside functions such as its own min() function.

But things break when these units don’t have a fixed relation between them (like the earlier case with em and vh units).

And it’s not just different units. Trying to use calc() inside min() also results in an error. If I try something like calc(20em + 7px), the error I get is, “calc(20em + 7px) is not a number for min.”

Screenshot. Shows the `'calc(20em + 7px)' is not a number for 'min'` error when trying to set `width: min(calc(20em + 7px), 50vh)`.
An error when using different unit values with calc() nested in the min()function

Another problem arises when we want to use a CSS variable or the result of a mathematical CSS function (such as calc(), min() or max()) in a CSS filter like invert().

In this case, we get told that “$ color: 'var(--p, 0.85) is not a color for invert.”

Screenshot. Shows the `$ color: 'var(--p, 0.85)' is not a color for 'invert'` error when trying to set `filter: invert(var(--p, .85))`.
var() in filter: invert() error

The same thing happens for grayscale(): “$ color: ‘calc(.2 + var(--d, .3))‘ is not a color for grayscale.”

Screenshot. Shows the `$ color: 'calc(.2 + var(--d, .3))' is not a color for 'grayscale'` error when trying to set `filter: grayscale(calc(.2 + var(--d, .3)))`.
calc() in filter: grayscale() error

opacity() causes the same issue: “$ color: ‘var(--p, 0.8)‘ is not a color for opacity.”

Screenshot. Shows the `$ color: 'var(--p, 0.8)' is not a color for 'opacity'` error when trying to set `filter: opacity(var(--p, 0.8))`.
var() in filter: opacity() error

However, other filter functions — including sepia(), blur(), drop-shadow(), brightness(), contrast() and hue-rotate()— all work just fine with CSS variables!

Turns out that what’s happening is similar to the min() and max() problem. Sass doesn’t have built-in sepia(), blur(), drop-shadow(), brightness(), contrast(), hue-rotate() functions, but it does have its own grayscale(), invert() and opacity() functions, and their first argument is a $ color value. Since it doesn’t find that argument, it throws an error.

For the same reason, we also run into trouble when trying to use a CSS variable that lists at least two hsl()or hsla() values.

Screenshot. Shows the `wrong number of arguments (2 for 3) for 'hsl'` error when trying to set `color: hsl(9, var(--sl, 95%, 65%))`.
var() in color: hsl() error.

On the flip side, color: hsl(9, var(--sl, 95%, 65%)) is perfectly valid CSS and works just fine without Sass.

The exact same thing happens with the rgb()and rgba() functions.

Screenshot. Shows the `$ color: 'var(--rgb, 128, 64, 64)' is not a color for 'rgba'` error when trying to set `color: rgba(var(--rgb, 128, 64, 64), .7)`.
var() in color: rgba() error.

Furthermore, if we import Compass and try to use a CSS variable inside a linear-gradient() or inside a radial-gradient(), we get another error, even though using variables inside conic-gradient() works just fine (that is, if the browser supports it).

Screenshot. Shows the At least two color stops are required for a linear-gradient error when trying to set background: linear-gradient(var(--c, pink), gold).
var() in background: linear-gradient() error.

This is because Compass comes with linear-gradient() and radial-gradient() functions, but has never added a conic-gradient() one.

The problems in all of these cases arise from Sass or Compass having identically-named functions and assuming those are what we intended to use in our code.


The solution

The trick here is to remember that Sass is case-sensitive, but CSS isn’t.

That means we can write Min(20em, 50vh)and Sass won’t recognize it as its own min() function. No errors will be thrown and it’s still valid CSS that works as intended. Similarly, writing HSL()/ HSLA()/ RGB()/ RGBA() or Invert() allows us to avoid issues we looked at earlier.

As for gradients, I usually prefer linear-Gradient() and radial-Gradient() just because it’s closer to the SVG version, but using at least one capital letter in there works just fine.

But why?

Almost every time I tweet anything Sass-related, I get lectured on how it shouldn’t be used now that we have CSS variables. I thought I’d address that and explain why I disagree.

First, while I find CSS variables immensely useful and have used them for almost everything for the past three years, it’s good to keep in mind that they come with a performance cost and that tracing where something went wrong in a maze of calc() computations can be a pain with our current DevTools. I try not to overuse them to avoid getting into a territory where the downsides of using them outweigh the benefits.

Screenshot. Shows how `calc()` expressions are presented in DevTools.
Not exactly easy to figure out what’s the result of those calc() expressions.

In general, if it acts like a constant, doesn’t change element-to-element or state-to-state (in which case custom properties are definitely the way to go) or reduce the amount of compiled CSS (solving the repetition problem created by prefixes), then I’m going to use a Sass variable.

Secondly, variables have always been a pretty small portion of why I use Sass. When I started using Sass in late 2012, it was primarily for looping, a feature we still don’t have in CSS. While I’ve moved some of that looping to an HTML preprocessor (because it reduces the generated code and avoids having to modify both the HTML and the CSS later), I still use Sass loops in plenty of cases, like generating lists of values, stop lists inside gradient functions, lists of points inside a polygon function, lists of transforms, and so on.

Here’s an example. I used to generate n HTML items with a preprocessor. The choice of preprocessor matters less, but I’ll be using Pug here.

- let n = 12;  while n--   .item

Then I would set the $ n variable into the Sass (and it would have to be equal to that in the HTML) and loop up to it to generate the transforms that would position each item:

$ n: 12; $ ba: 360deg/$ n; $ d: 2em;  .item {   position: absolute;   top: 50%; left: 50%;   margin: -.5*$ d;   width: $ d; height: $ d;   /* prettifying styles */    @for $ i from 0 to $ n {     &:nth-child(#{$ i + 1}) {       transform: rotate($ i*$ ba) translate(2*$ d) rotate(-$ i*$ ba); 			       &::before { content: '#{$ i}' }     }   } }

However, this meant that I would have to change both the Pug and the Sass when changing the number of items, making the generated code very repetitive.

Screenshot. Shows the generated CSS, really verbose, almost completely identical transform declaration repeated for each item.
CSS generated by the above code

I have since moved to making Pug generate the indices as custom properties and then use those in the transform declaration.

- let n = 12;  body(style=`--n: $ {n}`)   - for(let i = 0; i < n; i++)     .item(style=`--i: $ {i}`)
$ d: 2em;  .item {   position: absolute;   top: 50%;   left: 50%;   margin: -.5*$ d;   width: $ d;   height: $ d;   /* prettifying styles */   --az: calc(var(--i)*1turn/var(--n));   transform: rotate(var(--az)) translate(2*$ d) rotate(calc(-1*var(--az)));   counter-reset: i var(--i); 	   &::before { content: counter(i) } }

This significantly reduces the generated code.

Screenshot. Shows the generated CSS, much more compact, no having almost the exact same declaration set on every element separately.
CSS generated by the above code

However, looping in Sass is still necessary if I want to generate something like a rainbow.

@function get-rainbow($ n: 12, $ sat: 90%, $ lum: 65%) {   $ unit: 360/$ n;   $ s-list: (); 	   @for $ i from 0 through $ n {     $ s-list: $ s-list, hsl($ i*$ unit, $ sat, $ lum)   } 	   @return $ s-list }  html { background: linear-gradient(90deg, get-rainbow()) }

Sure, I could generate it as a list variable from Pug, but doing so doesn’t take advantage of the dynamic nature of CSS variables and it doesn’t reduce the amount of code that gets served to the browser, so there’s no benefit coming out of it.

Another big part of my Sass (and Compass) use is tied to built-in mathematical functions (such as trigonometric functions), which are part of the CSS spec now, but not yet implemented in any browser. Sass doesn’t come with these functions either, but Compass does and this is why I often need to use Compass.

And, sure, I could write my own such functions in Sass. I did resort to this in the beginning, before Compass supported inverse trigonometric functions. I really needed them, so I wrote my own based on the Taylor series. But Compass provides these sorts of functions nowadays and they are better and more performant than mine.

Mathematical functions are extremely important for me as I’m a technician, not an artist. The values in my CSS usually result from mathematical computations. They’re not magic numbers or something used purely for aesthetics. A example is generating lists of clip paths points that create regular or quasi-regular polygons. Think about the case where we want to create things like non-rectangular avatars or stickers.

Let’s consider a regular polygon with vertices on a circle with a radius 50% of the square element we start from. Dragging the slider in the following demo allows us to see where the points are placed for different numbers of vertices:

Putting it into Sass code, we have:

@mixin reg-poly($ n: 3) {   $ ba: 360deg/$ n; // base angle   $ p: (); // point coords list, initially empty 	   @for $ i from 0 to $ n {     $ ca: $ i*$ ba; // current angle     $ x: 50%*(1 + cos($ ca)); // x coord of current point     $ y: 50%*(1 + sin($ ca)); // y coord of current point     $ p: $ p, $ x $ y // add current point coords to point coords list   } 	   clip-path: polygon($ p) // set clip-path to list of points }

Note that here we’re also making use of looping and of things such as conditionals and modulo that are a real pain when using CSS without Sass.

A slightly more evolved version of this might involve rotating the polygon by adding the same offset angle ($ oa) to the angle of each vertex. This can be seen in the following demo. This example tosses in a star mixin that works in a similar manner, except we always have an even number of vertices and every odd-indexed vertex is situated on a circle of a smaller radius ($ f*50%, where $ f is sub-unitary):

We can also have chubby stars like this:

Or stickers with interesting border patterns. In this particular demo, each sticker is created with a single HTML element and the border pattern is created with clip-path, looping and mathematics in Sass. Quite a bit of it, in fact.

Another example are these card backgrounds where looping, the modulo operation and exponential functions work together to generate the dithering pixel background layers:

This demo just happens to rely heavily on CSS variables as well.

Then there’s using mixins to avoid writing the exact same declarations over and over when styling things like range inputs. Different browsers use different pseudo-elements to style the components of such a control, so for every component, we have to set the styles that control its look on multiple pseudos.

Sadly, as tempting as it may be to put this in our CSS:

input::-webkit-slider-runnable-track,  input::-moz-range-track,  input::-ms-track { /* common styles */ }

…we cannot do it because it doesn’t work! The entire rule set is dropped if even one of the selectors isn’t recognized. And since no browser recognises all three of the above, the styles don’t get applied in any browser.

We need to have something like this if we want our styles to be applied:

input::-webkit-slider-runnable-track { /* common styles */ } input::-moz-range-track { /* common styles */ } input::-ms-track { /* common styles */ }

But that can mean a lot of identical styles repeated three times. And if we want to change, say, the background of the track, we need to change it in the ::-webkit-slider-runnable-track styles, in the ::-moz-range-track styles and in the ::-ms-track styles.

The only sane solution we have is to use a mixin. The styles get repeated in the compiled code because they have to be repeated there, but we don’t have to write the same thing three times anymore.

@mixin track() { /* common styles */ }  input {   &::-webkit-slider-runnable-track { @include track }   &::-moz-range-track { @include track }   &::-ms-track { @include track } }

The bottom line is: yes, Sass is still very much necessary in 2020.

The post When Sass and New CSS Features Collide appeared first on CSS-Tricks.


, ,

Book: The Greatest CSS Tricks Vol. I

Ya know, for a site called “CSS-Tricks” that I’ve run for well over a decade, it’s a little funny we’ve never done a book under that name. I’ve written a book about WordPress and SVG, but never CSS!

Well, allow me to change that. I’ve been working on a “book” called The Greatest CSS Tricks Vol. I, as my attempt to stay true to this site’s name! The big idea to make it like a coffee-table book for CSS, where each chapter is totally independent and talks about one literal CSS trick that I’ve found to be exceptionally clever and useful. A book about quite literally the best CSS tricks I’ve come across over the years.

I quoted the word “book” above because this is the loosest possible definition of a book. I have not yet made it into an eBook format. I have not even considered printing it yet (although there is a “full book” URL available with the whole book together for printing and print-to-PDFing). This book exists as URLs which are essentially fancy blog posts grouped together. I’m also calling it Volume I as there are already ideas for another one!

Some chapters are fairly broadly known concepts that I’m writing up to put a point on. But many of the chapters are based on ideas that can be traced back to individual people and I always try to credit them directly.

Here’s the chapter list so far:

  1. Pin Scrolling to Bottom
  2. Scroll Animation
  3. Yellow Flash
  4. Shape Morphing
  5. Flexible Grids
  6. Border Triangles
  7. Scroll Indicator
  8. Boxy Buttons
  9. Self-Drawing Shapes
  10. Perfect Font Fallbacks
  11. Scroll Shadows
  12. Editable Style Blocks
  13. Draggable Elements
  14. Hard Stop Gradients
  15. Squigglevision

I say so far because I might add a few and rearrange them and such, not to mention it could still use a healthy bit of editing. But I think the bulk of the value of the book is already there.

Value? I think so. While it’s fun to learn some CSS trickery, I think there is value beyond the tricks themselves. Tricks help you see how CSS works at a deeper level. When you understand the trick, you’re seeing how that part of CSS works through a new lens and it helps you be more in tune with the nature of that CSS. It will help you reach for those CSS properties more intuitively when you know what they are capable of.

In another sense, it’s like taking a walk with weights in your backpack. You do it on purpose so that when you walk normally, it feels easier. The tricks are like mental weights. They make writing non-tricky CSS feel easier.

So about buying the book. You don’t buy the book directly. What you buy is an MVP Supporter membership to this site. When you’re an MVP Supporter, you have access to the book, and more. This is the whole package:

  • No Ads. You see no ads on this site, except for sponsored posts which are just blog posts and I try to make useful anyway.
  • Extra Content. You can read the digital books I’m making (you can already read some chapters, but they are under progress.)
  • Easier Commenting. You’ll be logged in, so leaving comments is easier and won’t require the delay for approval.
  • Good feels. An extreme sense of satisfaction of supporting this site and our commitment to bringing you useful tech knowledge.

It’s just just $ 20/year.

Have I, or this site, helped you out over the years? This is the best way to say thanks.

Also, if you would really like to have access to read the book, and can’t afford it right now, I totally get it. Email me at and we can work that out.

The post Book: The Greatest CSS Tricks Vol. I appeared first on CSS-Tricks.


, , ,

Quick Tips for High Contrast Mode

Sarah Higley has some CSS tricks up her sleeve for dealing with High Contrast Mode on Windows, which I learned is referred to as WHCM.

Here’s the first trick:

[…] if the default CSS outline property doesn’t give you the visual effect you want [in WHCM] for focus states, there’s a very simple fix. Instead of overriding default browser focus styles with outline: none, make it transparent instead: outline 3px solid transparent.

That will essentially do nothing outside of WHCM, but in WHCM, it will be a thick white border, which is a strong, good visual focus style.

Direct Link to ArticlePermalink

The post Quick Tips for High Contrast Mode appeared first on CSS-Tricks.


, , , ,

In Defense of a Fussy Website

The other day I was doom-scrolling twitter, and I saw a delightful article titled “The Case for Fussy Breakfasts.” I love food and especially breakfast, and since the pandemic hit I’ve been using my breaks in between meetings (or sometimes on meetings, shh) to make a full bacon, poached egg, vegetable plate, so I really got into the article. This small joy of creating a bit of space for myself for the most important meal of the day has been meaningful to me — while everything else feels out of control, indulging in some ceremony has done a tiny part to offset the intensity of our collective situation.

It caused me to think of this “fussiness” as applied to other inconsequential joys. A walk. A bath. What about programming?

While we’re all laser-focused on shipping the newest feature with the hottest software and the best Lighthouse scores, I’ve been missing a bit of the joy on the web. Apps are currently conveying little care for UX, guidance, richness, and — well, for humans trying to communicate through a computer, we’re certainly bending a lot to… the computer.

I’m getting a little tired of the web being seen as a mere document reader, and though I do love me a healthy lighthouse score, some of these point matrixes seem to live and die more by our developer ego in this gamification than actually considering what we can do without incurring much weight. SVGs can be very small while still being impactful. Some effects are tiny bits of CSS. JS animations can be lazy-loaded. You can even dazzle with words, color, and layout if you’re willing to be a bit adventurous, no weight at all!

A few of my favorite developer sites lately have been Josh Comeau, Johnson Ogwuru and Cassie Evans. The small delights and touches, the little a-ha moments, make me STAY. I wander around the site, exploring, learning, feeling actually more connected to each of these humans rather than as if I’m glancing at a PDF of their resume. They flex their muscles, show me the pride they have in building things, and it intrigues me! These small bits are more than the fluff that many portray any “excess” as: they do the job that the web is intending. We are communicating using this tool- the computer- as an extension of ourselves.

Nuance can be challenging. It’s easy as programmers to get stuck in absolutes, and one of these of late has been that if you’re having any bit of fun, any bit of style, that must mean it’s “not useful.” Honestly, I’d make the case that the opposite is true. Emotions attach to the limbic system, making memories easier to recall. If your site is a flat bit of text, how will anyone remember it?

Don’t you want to build the site that teams in companies the world over remember and cite as an inspiration? I’ve been at four different companies where people have mentioned Stripe as a site they would aspire to be like. Stripe took chances. Stripe told stories. Stripe engaged the imagination of developers, spoke directly to us.

I’m sad acknowledging the irony that after thinking about how spot on Stripe was, most of those companies ignored much of what they learned while exploring it. Any creativity, risk, and intention was slowly, piece by piece, chipped away by the drumbeat of “usefulness,” missing the forest for the trees.

When a site is done with care and excitement you can tell. You feel it as you visit, the hum of intention. The craft, the cohesiveness, the attention to detail is obvious. And in turn, you meet them halfway. These are the sites with the low bounce rates, the best engagement metrics, the ones where they get questions like “can I contribute?” No gimmicks needed.

What if you don’t have the time? Of course, we all have to get things over the line. Perhaps a challenge: what small thing can you incorporate that someone might notice? Can you start with a single detail? I didn’t start with a poached egg in my breakfast, one day I made a goofy scrambled one. It went on from there. Can you challenge yourself to learn one small new technique? Can you outsource one graphic? Can you introduce a tiny easter egg? Say something just a little differently from the typical corporate lingo?

If something is meaningful to you, the audience you’ll gather will likely be the folks that find it meaningful, too.

The post In Defense of a Fussy Website appeared first on CSS-Tricks.


, ,

The Return of the 90s Web

One of my forever-lessons here on CSS-Tricks is that having your own website and blogging on it is a good idea. It’s probably one of the best decisions I’ve ever made, as it’s been a direct source of fun, career development and, eventually, income.

I always chuckle at little blogging is cool again declarations from the community. It’s always cool, my friends. But it is always nice to see more people pick it back up.

I enjoyed this post from Max Böck that gets into how what is old is new again. Server side rendering! Personal websites! Blogging! Heck yes.

Direct Link to ArticlePermalink

The post The Return of the 90s Web appeared first on CSS-Tricks.