Posts in the CSS Category

Color Easing Isn’t Always Easy

Published 5 years past

A fairly new addition to CSS is the ability to define midpoints between two color stops in a gradient.  You can do this for both linear and radial gradients, but I’m going to stick with linear gradients in this piece, since they’re easier to show and visualize, at least for me.

The way they work is that you can define a spot on the gradient where the color that’s a halfway blend between the two color stops is located.  Take the mix of #00F (blue) with #FFF (white), for example.  The color midway through that blend is #8080FF, a pale-ish blue.  By default, that will land halfway between the two color stops.  So given linear-gradient(90deg, blue 0px, white 200px), you get #8080FF at 100 pixels.  If you use a more generic 90deg, blue, white 100%, then you get #8080FF at the 50% mark.

linear-gradient(90deg, blue, white 100%)

If you set a midpoint, though, the placement of #8080FF is set, and the rest of the gradient is altered to create a smooth progression.  linear-gradient(blue 0px, 150px, white 200px) places the midway color #8080FF at 150 pixels.  From 0 to 150 pixels is a gradient from #F00 to #8080FF, and from 150 pixels to 200 pixels is a gradient from #8080FF to #FFF.  In the following case, #8080FF is placed at the 80% mark; if the gradient is 200 pixels wide, that’s at 160 pixels.  For a 40-em gradient, that midpoint color is placed at 32em.

linear-gradient(90deg, blue, 80%, white 100%)

You might think that’s essentially two linear gradients next to each other, and that’s an understandable assumption.  For one, that’s what used to be the case.  For another, without setting midpoints, you do get linear transitions.  Take a look at the following example.  If you hover over the second gradient, it’ll switch direction from 270deg to 90deg.  Visually, there’s no difference, other than the label change.

linear-gradient(<angle>, blue, white, blue)

That works out because the easing from color stop to color stop is, in this case, linear.  That’s the case here because the easing midpoints are halfway between the color stops — if you leave them out, then they default to 50%.  In other words, linear-gradient(0deg, blue, white, blue) and linear-gradient(0deg, blue, 50%, white, 50%, blue) have the same effect.  This is because the midpoint easing algorithm is based on logarithms, and is designed to yield linear easing for a 50% midpoint.

Still, in the general case, it’s a logarithm algorithm (which I love to say out loud).  If the midpoint is anywhere other than exactly halfway between color stops, there will be non-linear easing.  More to the point, there will be non-linear, asymmetrical easing.  Hover over the second gradient in the following example, where there are midpoints set at 10% and 90%, to switch it from  270deg to 90deg, and you’ll see that it’s only a match when the direction is the same.

linear-gradient(<angle>, blue, 10%, white, 90%, blue)

This logarithmic easing is used because that’s what Photoshop does.  (Not Mosaic, for once!)  Adobe proposed adding non-linear midpoint easing to gradients, and they had an equation on hand that gave linear results in the default case.  It was also what developers would likely need to match if they got handed a Photoshop file with eased gradients in it.  So the Working Group, rather sensibly, went with it.

The downside is that under this easing regime, it’s really hard to create symmetric non-linear line gradients.  It might even be mathematically impossible, though I’m no mathematician.  Regardless, its very nature means you can’t get perfect symmetry.  This stands in contrast to cubic Bézier easing, where it’s easy to make symmetric easings as long as you know which values to swap.  And there are already defined keywords that are symmetric to each other, like ease-in and ease-out.

If you’re up for the work it takes, it’s possible to get some close visual matches to cubic Bézier easing using the logarithmic easing we have now.  With a massive assist from Tab Atkins, who wrote the JavaScript I put to use, I created a couple of CodePens to demonstrate this.  In the first, you can see that linear-gradient(90deg, blue, 66.6%, white) is pretty close to linear-gradient(90deg, blue, ease-in, white).  There’s a divergence around the 20-30% area, but it’s fairly minor.  Setting an interim color stop would probably bring it more in line.  That’s partly how I got a close match to linear-gradient(90deg, blue, ease-out, white), which came out to be linear-gradient(90deg, blue, 23%, #AFAFFF 50%, 68%, white 93%).

Those examples are all one-way, however — not symmetrical.  So I set up a second CodePen where I explored recreations of a few symmetrical non-linear gradients.  The simplest example matches linear-gradient(90deg, blue, ease-in, white, ease-out, blue) with linear-gradient(90deg, blue, 33.3%, white 50%, 61.5%, #5050FF 75%, 84%, blue 93%), and they only get more complex from there.

I should note that I make no claim I’ve found the best possible matches in my experiments.  There are probably more accurate reproductions possible, and there are probably algorithms to work out what they would be.  Instead,  I did what most authors would do, were they motivated to do this at all: I set some stops and manually tweaked midpoints until I got a close match.  My basic goal was to minimize the number of stops and midpoints, because doing so meant less work for me.

So, okay, we can recreate cubic Bézier easing with logarithmic midpoints.  Still, wouldn’t it be cool to just allow color easing using cubic Béziers?  That’s what Issue #1332 in the CSS Working Group’s Editor Drafts repository requests.  From the initial request, the idea has been debated and refined so that most of the participants seem happy with a syntax like linear-gradient(red, ease-in-out, blue).

The thing is, it’s generally not enough to have an accepted syntax — the Working Group, and more specifically browser implementors, want to see use cases.  When resources are finite, requests get prioritized.  Solving actual problems that authors face always wins over doing an arguably cool thing nobody really needs.  Which is this?  I don’t know, and neither does the Working Group.

So: if you have use cases where cubic Bézier easing for gradient color stops would make your life easier, whether it’s for drop shadows or image overlays or something I could never think of because I haven’t faced it, please add them to the GitHub issue!


Color Me FACE1E55

Published 5 years, 3 weeks past

There’s a long history in computer programming of using hexadecimal strings that look like English words to flag errors.  These are referred to, amusingly, as “magic debug values”, and yes, Wikipedia has the lowdown.  One of the most (in)famous is DEADBEEF, which was used “on IBM systems such as the RS/6000, also used in the classic Mac OS operating systems, OPENSTEP Enterprise, and the Commodore Amiga”, among others.  It’s also become the name of a Gnu/Linux music player, and apparently does not have anything to do with Cult of the Dead Cow, at least not so far as I could determine.  Maybe someone with more knowledge can drop a comment.

Anyway, one of the things about these magic debug values is they’re usually eight characters long.  Not always, as in the case of BADC0FFEE0DDF00D (from RS/6000, again), but usually.  Nintendo used 0D15EA5E in the GameCube and Wii to indicate a normal boot (!), iOS logs DEAD10CC when an application terminates in a specific yet incorrect manner, and FEEDFACE shows up in PowerPC Mach-O binaries , as well as the VLC Player application.  Just to pick a few examples.

The eight-character nature of these magic codes has meant that, for a long time, you couldn’t also use them on the sly to define colors in CSS, because it was limited to the #RRGGBB format.  Well, those days are over.  Long over.  Eight-digit hex color values are here, have been here a while, and are widely supported.  Here are a few swatches laid over a (fully opaque) white-to-black gradient.

#abadcafe
#baaaaaaa
#deadbeef
#deadfeed
#defec8ed
#feedbacc

If you’re using Internet Explorer or Edge, those aren’t going to work for you.  At least, not until Edge switches over to Blink; then, they should work just fine.

Thanks to the way they were constructed, by only using the letters A-F, most of the colors above are mostly opaque.  The last two digits in #RRGGBBAA set the alpha channel level of the color, just like the last part of the rgba() syntax.  Thus, the EF at the end of DEADBEEF sets the alpha value to 0.937; EF is equivalent to decimal 239, and 239 ÷ 255 = 0.937 (approximately).  In other words, #DEADBEEF is essentially equivalent to rgba(222,173,190,0.937).

That’s why, of the six swatches, only the sheepish #baaaaaaa and the homophonic #feedbacc let the background gradient show through more than very slightly; their alpha channels are 0.666 and 0.8, respectively.  The rest are 0.929 and up.

Being stuck in the A-F range is fairly constraining, but that’s where hexadecimal and English overlap, so that’s how it goes.  However, if you’re willing to turn to leetspeak syntax — that is, allowing yourself to use 0 as a substitute for O, 1 for L and occasionally I, 5 for S, 7 for T, and so on — then a lot more possibilities open up.  In addition to some of the classic error codes like fee1dead (Linux), I had fun devising other eight-character color words like acc0lade and face1e55, not to mention the very nautical ccccccc5.  (Think about it.)  Behold!

#0ff1c1a1
#1337c0de
#5e1f1e55
#a114c0de
#acc01ade
#ba5e1e55
#bada55e5
#bebada55
#beefc0de
#b0bafe77
#b0a710ad
#c010ca7e
#c0de1e55
#ccccccc5
#d0d0c0de
#dabbad00
#dead10ad
#deadd0d0
#decea5ed
#face1e55
#fee1dead

There are still more l33t-compliant number substitutions available, like 6 for G, but I felt like I was already pushing it with the examples I have.  One could also use calculator spelling, where 9 is a stand-in for g, and even mix together l33t and calculator syntaxes in the same value.  So many possibilities!

You may have noticed one value which creates no color: #DABBAD00, which has 00 for its alpha, so it’s fully, completely transparent.  It’s fully transparent #DABBAD, I suppose, but there’s really no difference between one transparent color and another, as far as I’m concerned.  I mean, if a color falls transparent, then there’s nobody to see it, so is it really a color at all?  I say thee nay.

If you’re familiar with the way #RRGGBB hex values can be represented with the shortened #RGB syntax, then it will probably come as little surprise that #RRGGBBAA has a shortened #RGBA syntax, where each digit is duplicated.  This opens the world of four-letter words to us!  Here are a few:

#10ad
#1337
#b007
#ba5e
#bead
#beef
#c0de
#cafe
#cede
#dada
#dead
#deed
#f00d
#fade
#f8ed
#feed
#0b0e

Here, we finally have a fully opaque word-color: #BEEF expands out to #BBEEEEFF, making the alpha value FF, which decimal-translates to 255, which is fully opaque.  So we get a nice opaque powdery blue out of BEEF, which is counterintuitive in the best possible way.  Also, every time I see BBEEEEFF, either in print or in my head, I hear Mrs. Which ordering dinner.

And okay, yes, #F8ED isn’t a four-letter word, it’s a four-symbol license-plate word.  So it’s even cooler.

If you’re thinking about using these in your CSS, you might be concerned about backwards compatibility, since any browser that doesn’t understand four- or eight-digit hexadecimal color values will just drop them on the floor.  That might be okay for text coloring, since the text will likely have some color, even if it’s browser-default, which is usually black.  For backgrounds, having colors ignored probably less okay, particularly if you set foreground colors that depend on the background colors.

There are a couple of possibilities here.  One is to use the cascade and CSS error handling to your advantage, in the time-honored pattern of doing the simpler version first and the more sophisticated version second.

#example {
   color: #DEA;
   color: #DEAD;
}

That works in simple scenarios, but for more complicated situations — say, ones where you have foreground and background depending on each other — feature queries are an option to consider, if for no other reason than cleaner organization and legibility.

#example {
   color: red;
   background: #EEE;
}

@supports (color: #ABCD) {
   #example {
      color: #f00d;
      background: #feed;
   }
}

Naturally and as usual, you’ll have to figure out what makes the most sense for your situation.  Maybe the right answer will be to avoid using these sorts of values at all, although I don’t know where the fun is in that.

At any rate, I hope you’ve enjoyed this little tour of magic debug values, l33tspeak, and color words.  As always, #feedbacc is more than welcome in the comments!


CSS4 Color Keyword Distribution Visualization

Published 5 years, 3 weeks past

Long, long ago — not quite seven years ago, in fact — I built a canvas-based visualization of the distribution of CSS3/SVG color keywords and released it.  And there it’s sat, static and inert (despite being drawn with a whooooole lotta JS) ever since.

I’ve always meant to get back to it and make it more interactive.  So over the past several evenings, I’ve rebuilt it as an SVG-based visualization.  The main point of doing this was so that when you hover the mouse pointer over one of the little color boxes, it will fill the center of the color wheel with the hovered color and tell you its name and HSL values.  Which it does, now.  It even tries to guess whether the text should be white or black, in order to contrast with the underlying color.  Current success rate on that is about 90%, I think.  Calculating perceived visual brightness turns out to be pretty hard!

Other things I either discovered, or want to do better in the future:

  • Very nearly half the CSS4 (and also CSS3/SVG) color keywords are in the first 90 degrees of hue.  More than half are in the first 120 degrees.
  • There are a lot of light/medium/dark variant names in the green and blue areas of the color space.
  • I wish I could make the color swatches bigger, but when I do that the adjacent swatches overlap each other and one of them gets obscured.
  • Therefore, being able to zoom in on parts of the visualization is high on my priority list.  All I need is a bit of event monitoring and some viewbox manipulation.  Well, that and a bit more time. Done, at least for mouse scroll wheels.
  • I’d like to add a feature at some point where you type text, and a list is dynamically filtered to show keywords containing what you typed.  And each such keyword has a line connecting it to the actual color swatch in the visualization.  I have some ideas for how to make that work.
  • I’d love to create a visualization that placed the color swatches in a 3D cylindrical space summarizing hue, lightness. and saturation.  Not this week, though.
  • I’m almost certain it needs accessibility work, which is also high on my priority list.
  • SVG needs conic gradients.  Or the ability to wrap a linear gradient along/inside/around a shape like a circle, that would work too.  Having to build a conic gradient out of 360 individual <path>s is faintly ridiculous, even if you can automate it with JS.
  • And also z-index awareness.  C’mon, SVG, get it together.

Anyway, here it is: CSS4 Color Keyword Distribution.  I hope you  like it!


“Stacked ‘Borders’” Published at CSS-Tricks

Published 5 years, 1 month past

I toyed with the idea of nesting elements with borders and some negative margins to pull one border on top of another, or nesting a border inside an outline and then using negative margins to keep from throwing off the layout. But none of that felt satisfying.

It turns out there are a number of tricks to create the effect of stacking one border atop another by combining a border with some other CSS effects, or even without actually requiring the use of any borders at all. Let’s explore, shall we?

That’s from the introduction to my article “Stacked ‘Borders’”, which marks the first time I’ve ever been published at the venerable upstart CSS-Tricks.  (I’m old, so I can call things both venerable and an upstart.  You kids today!)  In it, I explore ways to simulate the effect of stacking multiple element borders atop on another, including combining box shadows and outlines, borders and backgrounds, and even using border images, which have a much wider support base than you might have realized.

And yes, as per my usual, the images in the piece are all double-dpi :screenshot captures directly from Firefox.

Many thanks to Chris Coyier for accepting the piece, and Geoff Graham for his editorial assistance.  I hope you’ll find at least some part of it useful, or better still, interesting.  Share and enjoy!


Variable Font Support

Published 5 years, 7 months past

Firefox 62 ships today, bringing with it some real CSS goodness.  For one: float shapes!  Which means now, mainline Firefox users will see the text flow past the blender in “Handiwork” the same way Chrome users have for a long time now.

But an even bigger addition is support for variable fonts.  The ability to have one font file that mathematically describes variants on the base face means that all kinds of fine-grained typography is possible with far less bandwidth overhead and a major reduction in page weight.

However: bear in mind that like Safari, but unlike Chrome, Firefox’s variable-font support is dependent on the operating system on which is runs.  If you have Windows 10, or Max OS X 10.13, then you have variable font support in Firefox and Safari.  Earlier versions of those operating systems don’t support variable fonts, and so Safari and Firefox don’t either.  Chrome rolls its own variable-font support, so it can extend support backwards in the OS timeline.

(I don’t know how things stand in the Linux world.  Hopefully someone can clear things up in the comments!)

I say this not to chastise Firefox (nor Safari), because I tend to think leaning on the OS for this sort of thing is reasonable.  I feel the same way about form elements like <select> dropdowns, to be clear, which I know likely places me in the minority.  The point here is to give you a heads-up: if you get reports that a font isn’t doing the variable thing you styled, but it’s working fine for you, keep “check their operating system version” on your list of diagnostic tests.


What is the CSS ‘ch’ Unit?

Published 5 years, 9 months past

I keep seeing authors and speakers refer to the ch unit as meaning “character width”.  This leads to claims that you can “make your content column 60 characters wide for maximum readability” or “size images to be a certain number of characters!”

Well… yes and no.  Specifically, yes if you’re using fixed-width fonts.  Otherwise, mostly no.

This is because, despite what the letters ch might imply, ch units are not “character” units.  They are defined as:

Equal to the used advance measure of the “0” (ZERO, U+0030) glyph found in the font used to render it. (The advance measure of a glyph is its advance width or height, whichever is in the inline axis of the element.)

So however wide the “0” character is in a given typeface, that’s the measure of one ch.  In monospace (fixed-width) fonts, where all characters are the same width, 1ch equals one character.  In proportional (variable-width) fonts, any given character could be wider or narrower than the “0” character.

To illustrate this, here are a few example elements which are set to be exactly 20ch wide, and also contain exactly 20 characters.

Courier

Look, 20 characters. abcdefghijklmnopqrst 12345678901234567890 iiiiiiiiiiiiiiiiiiii mmmmmmmmmmmmmmmmmmmm

Helvetica

Look, 20 characters. abcdefghijklmnopqrst 12345678901234567890 iiiiiiiiiiiiiiiiiiii mmmmmmmmmmmmmmmmmmmm

Georgia

Look, 20 characters. abcdefghijklmnopqrst 12345678901234567890 iiiiiiiiiiiiiiiiiiii mmmmmmmmmmmmmmmmmmmm

It’s probably no surprise that in Courier, all the elements are the exact same width as their text contents.  In Helvetica, by contrast, this is mostly not the case except for numbers, which appear to be fixed-width.  In Georgia, by contrast, none of the text contents fit the boxes, not even the numbers.

What I’ve found through random experimentation is that in proportional typefaces, 1ch is usually wider than the average character width, usually by around 20-30%.  But there are at least a few typefaces where the zero symbol is skinny with respect to the other letterforms; in such a case, 1ch is narrower than the average character width.  Trajan Pro is one example I found where the zero was a bit narrower than the average, but I’m sure there are others. Conversely, I’m sure there are typefaces with Big Fat Zeroes, in which case the difference between ch and the average character width could be around 50%.

So in general, if you want an 80-character column width and you’re going to use ch to size it, aim for about 60ch, unless you’re specifically working with a typeface that has a skinny zero.  And if you’re working with multiple typefaces, say one for headlines and another for body copy, be careful about setting ch measures and thinking they’ll be equivalent between the two fonts.  The odds are very, very high they won’t be.

It would be interesting to see the Working Group take up the idea of average character width as a unit of measure — say, 1acw or possibly just 1cw — which actually uses all the letterforms in a typeface to calculate an average value.  That would get a lot closer to “make your columns 60 characters wide!” in a lot more cases than ch does now.


Specificity in :not(), :has(), and :matches()

Published 5 years, 10 months past

A few years back, I wrote a short post on specificity, element proximity, and the negation pseudo-class.  Everything in it is still accurate and relevant, but I have some updates to share.

First off, I’d like to clarify something that some people may have found confusing.  In that post, I said:

But it turns out that the negation pseudo-class isn’t counted as a pseudo-class.

That might leave some people with the idea that the entire negation portion of the selector is ignored for the purposes of specificity, especially if you don’t speak spec.

So consider the following:

div:not(.one) p

In order from left to right, that’s an element selector (div), a negation pesudo-class (:not) a class selector (.one), and another element selection (p).  Two element selectors and one class selector are counted towards the specificity, yielding a total of 0,0,1,2.  That’s the same specificity as div.one p, though the two selectors select very different things.

In Ye Olden Days, that was easy enough to work out, because :not() could only ever contain a simple selector.  Things are looking to get more complicated, however — :not() is set to accept grouped selectors.  So we will at some point be able to say:

div:not(.one, .two, #navbar) p

So any p element that is not descended from a div that has a class containing either one or two (or both), or that has an id of navbar, will be selected.

But how do we calculate the specificity of that whole selector?  Just add up all the pieces?  No.  The Working Group recently decided that the specificity contributed from inside a :not() will be equal to the single selector with the highest specificity.  So given div:not(.one, .two, #navbar) p, the #navbar will contribute 0,1,0,0 to the overall specificity of the selector, yielding a total of 0,1,0,2.  The specificities of .one and .two are ignored.

This same approach will be taken with the :has() and :matches() pseudo-classes.  Thus we get the following:


:matches(nav, header, footer#pageend) a[href] {color: silver;}  /* 0,1,1,2 */
article:has(a.external, a img)  /* 0,0,1,2 */
input:not([type="radio"], [type="checkbox"])  /* 0,0,1,1 */

In the first instance, the bits that are added together are footer#pageend and a[href], so that’s one ID, one attribute, and two elements.  In the second, it’s article and a.external for one class and two elements.  And last, we add up input and either of the [type=""] attribute selectors, since their specificities are equal, which means we add up one attribute and one element.

There is still, so far as I’m aware, no concept of DOM-tree proximity in CSS.  I would still continue to wager that will remain true, though I’d put a fair bit less money down now than I would have six years ago.


Displaying CSS Breakpoint Information with Generated Content

Published 6 years, 2 months past

In the course of experimenting with an example design for my talks at An Event Apart this year, I came up with a way to keep track of which breakpoint was in force as I tested the design’s responsiveness.  I searched the web to see if anyone else had written about this and didn’t come up with any results, so I’ll document it here.  And probably also in the talks.

What I found was that, since I was setting breakpoints in ems instead of pixels, the responsive testing view in browsers didn’t really help, because I can’t maintain realtime mapping in my head from the current pixel value to however many rems it equals.  Since I don’t think the browser has a simple display of that information, I decided I’d do it myself.

It starts with some generated content:

body::before {content: "default";
   position: fixed; top: 1px; right: 1px; z-index: 100; padding: 1ch;
   background: rgba(0,0,0,0.67); color: rgba(255,255,255,0.75);
   font: bold 0.85rem Lucida Grande, sans-serif;}

You can of course change these to some other placement and appearance.  You can also attach these styles to the html element, or your page wrapper if you have one, or honestly even the footer of your document — since the position is fixed, it’ll be viewport-relative no matter where it originates.  The real point here is that we’re generating a bit of text we can change at each breakpoint, like so:

@media (max-width: 38em) {
   body::before {content: "<38em";}
   /* the rest of the breakpoint styles here */
}
@media (max-width: 50em) {
   body::before {content: "<50em";}
   /* the rest of the breakpoint styles here */
}
@media (min-width: 80em) {
   body::before {content: ">80em";}
   /* the rest of the breakpoint styles here */
}

The labels can be any string you want, so you can use “Narrow”, “Wide”, and so on just as easily as showing the measure in play, as I did.

The downside for me is that we automatically can’t make the labels cumulative in native CSS.  That means the order the @media blocks appear will determine which label is shown, even if multiple blocks are being applied.  As an example, given the styles above, at a width of 25em, the label shown will be <50em even though both the 38em and 50em blocks apply.

There are ways around this, like switching the order of the max-width blocks so the 38em block comes after the 50em block.  Or we could play specificity games:

@media (max-width: 38em) {
   html body::before {content: "<38em";}
   /* the rest of the breakpoint styles here */
}
@media (max-width: 50em) {
   body::before {content: "<50em";}
   /* the rest of the breakpoint styles here */
}

That’s not a solution that scales, sadly.  Probably better to sort the max-width media blocks in descending order, if you think you might end up with several.

The upside is that it’s easy to find and remove these lines once the development phase moves to QA.  Even better, before that point, you get a fully customizable in-viewport indication of where you are in the breakpoint stack as you look at the work in progress.  It’s pretty trivial to take this further by also changing the background color of the little box.  Maybe use a green for all the block above the “standard” set, and a red for all those below it.  Or toss in little background image icons of a phone or a desktop, if you have some handy.

So that’s the quick-and-dirty little responsive development hack I came up with this morning.  I hope it’s useful to some of you out there — and, if so, by all means share and enjoy!


Addendum: Emil Björklund proposes a variant approach that uses CSS Custom Properties (aka CSS variables) to implement this technique.


Browse the Archive

Earlier Entries

Later Entries