Tag: Features

A Whistle-Stop Tour of 4 New CSS Color Features

I was just writing in my “What’s new in since CSS3?” article about recent and possible future changes to CSS colors. It’s weirdly a lot. There are just as many or more new and upcoming ways to define colors than what we have now. I thought we’d take a really quick look.

First, a major heads up. This stuff is so complicated. I barely understand it. But here are some aspects:

  • Before all this upcoming change, we only had RGB as a color model, and everything dealt with that.
  • We had different “color spaces” that handled it differently (e.g. the rgb() function mapped that RGB color model as a cube with linear coordinates, the hsl() function mapped that RGB color model as a cylinder) but it was all sRGB gamut.
  • With the upcoming changes, we’re getting new color models and (!) we’re getting new functions that map that color model differently. So I think it’s kind of a double-triple whammy.

I can’t personally educate you on all the nitty-gritty details — I’m writing this because I bet there are a lot of you like me, wondering why you should care at all about this, and this is my attempt to understand why I should care about all of it.

Display-P3 is one that opens up a ton of more vibrant color that was able to be expressed before.

body {   background: color(display-p3 1 0.08 0); /* super red! */ }

It turns out that modern monitors can display way more colors, particularly extra vibrant ones, but we just have no way of defining those colors with classic CSS color syntaxes, like HEX, RGB, and HSL. Super weird, right?! But if you use Display-P3, you get a wider range of access to these vibrant colors.

Screenshot of a super bright pink in a CodePen preview using the display-p3 CSS color syntax.
That white line in Safari DevTools is showing us the “extra” range of Display-P3

The dev shop Panic latched onto this early on and started using these colors as a “secret weapon”:

Jen Simmons also covers how to use them, including a fallback for non-supporting browsers:

Resources

HWB is the one that is more “for humans” except that’s a bit debatable and it’s still based on sRGB.

I had no idea hwb() was a thing — shout out to Stefan Judis for blogging about it.

I normally think of HSL as the CSS color format that is “for humans” (and good for programmatic control) because, well, manipulating 360° of Hue and 0-100% of both Saturation and Lightness make some kind of obvious sense.

But in hwb(), we’ve got Hue (the same as HSL, I think), then Whiteness and Blackness. Stefan:

Adding White and Black to a color affects its saturation. Suppose you add the same amount of White and Black to a color, the color tone stays the same, but color loses saturation. This works up to 50% White and 50% Black (hwb(0deg 50% 50%)), which results in an achromatic color.

Showing six gradients going from red to black and the impact that change CSS color values in hwb has on the transition between colors.

Stefan expressed some doubt that this is any easier to understand than HSL, and I tend to agree. I probably just need to get more used to it, but it seems to be more abstract than simply changing the lightness or saturation.

HWB is limited to the same color gamut (sRGB) as all the old color formats all. No new colors are unlocked here.

Resources

LAB is like rgb() of a much wider gamut

div {   background: lab(150% -400 400); }

I liked Eric Portis’ explanation of LAB when I went around asking about it:

LAB is like RGB in that there are three linear components. Lower numbers mean less of the thing, bigger numbers mean more of the thing. So you could use LAB to specify the brightest, greenest green that ever bright-greened, and it’ll be super bright and green for everybody, but brighter and greener on monitors with wider gamuts.

So, we get all the extra color, which is awesome, but sRGB had this other problem (aside from being limited in color expression), that it isn’t perceptually uniform. Brian Kardell:

The sRGB space is not perceptually uniform. The same mathematical movement has different degrees of perceived effect depending on where you are at in the color space. If you want to read a designer’s experience with this, here’s an interesting example which does a good job struggling to do well.

The classic example here is how, in HSL, colors with the exact same “Lightness” really don’t feel the same at all.

But in LAB, apparently, it is perceptually uniform, meaning that programmatically manipulating colors is a much more sane task. And another bonus is that LAB colors are specced as being device-independent. Here’s Michelle Barker:

LAB and LCH are defined in the specification as device-independent colors. LAB is a color space that can be accessed in software like Photoshop and is recommended if you want a color to look the same on-screen as, say, printed on a t-shirt.

Resources

LCH is like HSL of a much wider gamut

Remember how I said HSL is “for humans” in that it is easier understand than RGB? Changing the Hue, Saturation, and Lightness makes a lot of logical sense. Similar here with lch() where we’ve got Lightness, Chroma, and Hue. Back to my conversation with Eric Portis:

LCH is more like HSL: a polar space. H = hue = a circle. So doing math to pick complementary colors (or whatever transforms you’re after) becomes trivial (just add 180 — or whatever!)

I suppose you’d pick LCH just because you like the syntax of it or because it makes some complicated programmatic thing you’re trying to do easier — and you get the fact that it can express 50% more colors for free.

We get the perceptual uniformity here, too. Here’s Lea Verou who seems excited that lightness will actually mean something:

In HSL, lightness is meaningless. Colors can have the same lightness value, with wildly different perceptual lightness. […] With LCH, any colors with the same lightness are equally perceptually light, and any colors with the same chroma are equally perceptually saturated.

Another benefit of the new model is that we can wipe our hands clean of the “gray dead zone” in CSS color gradients. I think because of this perceptual uniformity stuff, two rich colors won’t get cheeky and gradient themselves through non-rich territory.

Two gradients going from blue to pink, one on top of the other. The first uses the LCH CSS color syntax and the second use HSL. HSL has noticeable gray areas.
There will always be tradeoffs in color models, especially with gradients. (Demo)

Here’s a small personal prediction: I’d say that lch() is probably going to be a designer favorite. Soon there are going to be a ton of new color choices and it’s too difficult and weird to always be picking different ones. LCH seems to have the most bang for the mental buck.

Resources

“OK”

LAB ‘n’ friends seems so new because it is new… to CSS. But LAB was invented in the 1940s. In a conversation with Adam Argyle, he used a memorable phrase: All the color spaces have an Achilles’ heel. That is, something they kinda suck at. For sRGB, it’s the grey dead zone thing, as well as the limited color gamut. LAB is great and all, but it certainly has its own weaknesses. For example, a blue-to-white gradient in LAB travels pretty awkwardly through purpletown.

In December 2020, Björn Ottosson is all like “Hey, a new color space just dropped,” and now OKLAB exists. Apparently the CSS powers-that-be see enough value in that color space that both oklab() and oklch() are already specced. I guess we should care because they are just generally better, but don’t quote me on that.

Why is it Display P3 uses the color() function but the other’s don’t?

I don’t really know. I think the CSS color() function is a bit newer and that’s just how Safari dunked it in there to start. I have no idea if Display P3 will get its own dedicated function, or if we all should just start using CSS color(), or what.

/* This is how you use Display P3 */ color(display-p3 1 0.08 0);   /* But this doesn't work */ color(oklch 42.1% 0.192 328.6);  /* You gotta do this instead 🤷‍♀️ */ oklch(42.1% 0.192 328.6);  /* But you can use the color space within a gradient... */ background-image: linear-gradient(     to right      in oklch,     lch(50% 100 100),      lch(50% 100 250)   );

The relative color syntax is super useful.

There is this really cool ability called “relative color syntax” where you can basically deconstruct a CSS color while moving it into another format. Say you have the (obviously) most famous CSS HEX color ever, fog dog, and you wanna kick it into HSL instead:

body {   background: hsl(from #f06d06 h s l); }

Maybe that’s not all that useful immediately, but hey, now we’re able to add alpha to it! There is literally no other way to apply alpha to an existing HEX color, so that’s kinda huge:

body {   background: hsl(from #f06d06 h s l / 0.5); }

But I can also mess with it. Say I wanna saturate fog dog a bit before I add opacity because the lower opacity will naturally dull it out and I wanna combat that. I can use calc() on the implied variables there:

body {   background: hsl(from #f06d06 h calc(s + 20%) l / 0.5); }

That’s so cool. I’m sure we’ll see some amazing things come from this. And it certainly isn’t limited to HSL. I was just using HSL because it’s what is comfortable to me right now. I could start with the named color red and mess with it in LCH if I want:

body {   background: lch(from red l calc(c + 15) h / 0.25); }

This stuff is going to be most useful when liberally combined with custom properties.

There are no special functions just for alpha anymore.

Just to be clear: no commas preceding the alpha value in a CSS color function — just a forward slash instead:

/* Old! */ rgb(255, 0, 0); rgba(255, 0, 0, 0.5);  /* New! */ rgb(255 0 0); rgb(255 0 0 / 0.5); hsl(0deg 40% 40%) hsl(0deg 40% 40% / 90%) /* can be percentage rather than 0.9 or whatever */  /* The New color stuff ONLY has the single base function, no alpha secondardy function */ lab(49% 39 80) lab(49% 39 80 / 0.25)  /* Display P3, with the color function, essentially works the same way with the slash */ color(display-p3 1 0.08 0 / 0.25); 

You can even define your own CSS color space.

But I literally can’t even think about that. It blows my mind, sorry.


A Whistle-Stop Tour of 4 New CSS Color Features originally published on CSS-Tricks. You should get the newsletter and become a supporter.

CSS-Tricks

, , ,

Jetpack Features We Love and Use at CSS-Tricks

(This is a sponsored post.)

We use and love Jetpack around here. It’s a WordPress plugin that brings a whole suite of functionality to your site ranging from security to marketing with lots of ridiculously useful stuff in between! Here’s our favorite features around here.

Jetpack’s Search feature gives your site an incredibly powerful search engine with the flip of a switch. You get a very fast, truly intelligent search for your entire site that is easily sortable and filterable with literally zero work on your part. You can’t rely on default WordPress search — this is a must-have. Bonus: it’s all handled offsite, so there is minimal burden on your server.

Showing the full-page JetPack search feature.

Backups & Activity

We sleep easy knowing CSS-Tricks is entirely backed up in real-time. Everything is backed up from the site’s content, comments, settings, theme files, media, even WordPress itself.

An activity log shows off everything that happens on the site, and I use that same log to restore the site to any point in time.

Showing a timeline log of site changes in Jetpack Backup.

Performant Media

There are at least four things you have to do with images on websites to make sure you’re serving them in a performance responsible way: (1) use the responsive images syntax to serve an appropriately sized version, (2) optimize the image, (3) lazy load the image, and (4) serve the image from a CDN. Fortunately, WordPress itself helps with #1, which can be tricky. Jetpack helps with the others with the flip of a switch.

And don’t forget about video! VideoPress does even more for your hosted videos. No ads, beautiful feature-rich player, CDN-hosted optimized video, poster graphics for mobile, and you do absolutely nothing different with your workflow: just drag and drop videos into posts.

JetPack Markdown

Writing content in Markdown can be awful handy. Especially on a developer-focused site, it makes sense to offer it to users in the comment section.

With Jetpack Markdown, you also get a Markdown block to use in the block editor so you can write in chunks of Markdown wherever needed.

CSS-Tricks has thousands of pages of content! It’s a challenge for us to surface all the best stuff, particularly on a per-topic basis and without having to hand-pick everything. Showing related posts is tricky to pull off and we love that Jetpack does a great job with it, all without burdening our servers the way other related content solutions can.

Social Connections

We like to tell the world as best as we can when we publish new content. Rather than having to do that manually, we can share the post to Twitter and Facebook the second we hit that “Publish” button. You can always head back to older content and re-publish to social media as well.

Showing the Jetpack publishing settings prior to publishing the post. It shows options to publish to Twitter and Facebook and an area to customize the message.


This isn’t a complete list. The official features page will show you even more. Every site’s needs will be different. There are all sorts of security, design, and promotion features that might be your favorites. If you manage a lot of WordPress sites, as agencies often too, take note there is a new Licensing Portal to manage billing across multiple sites much more easily.


Jetpack Features We Love and Use at CSS-Tricks originally published on CSS-Tricks

CSS-Tricks

, , ,
[Top]

A Themeable React Data Grid With Great UX-Focused Features

(This is a sponsored post.)

KendoReact can save you boatloads of time because it offers pre-built componentry you can use in your app right away. They look nice, but more importantly, they are easily themeable, so they look however you need them to look. And I’d say the looks aren’t even the important part. There are lots of component libraries out there that focus on the visuals. These components tackle the hardest interactivity problems in UI/UX, and do it with grace, speed, and accessibility in mind.

Let’s take a look at their React Data Grid component.

The ol’ <table> element is the right tool for the job for data grids, but a table doesn’t offer most of the features that make for a good data browsing experience. If we use the KendoReact <Grid /> component (and friends), we get an absolute ton of extra features, any one of which is non-trivial to pull off nicely, and all together make for an extremely compelling solution. Let’s go through a list of what you get.

Sortable Columns

You’ll surely pick a default ordering for your data, but if any given row of data has things like ID’s, dates, or names, it’s perfectly likely that a user would want to sort the column by that data. Perhaps they want to view the oldest orders, or the orders of the highest total value. HTML does not help with ordering in tables, so this is table stakes (get it?!) for a JavaScript library for data grids, and it’s perfectly handled here.

Pagination and Limits

When you have any more than, say, a few dozen rows of data, it’s common that you want to paginate it. That way users don’t have to scroll as much, and equally importantly, it keeps the page fast by not making the DOM too enormous. One of the problems with pagination though is it makes things like sorting harder! You can’t just sort the 20 rows you can see, it is expected that the entire data set gets sorted. Of course that’s handled in KendoReact’s Data Grid component.

Or, if pagination isn’t your thing, the data grid offers virtualized scrolling — in both the column and row directions. That’s a nice touch as the data loads quickly for smooth, natural scrolling.

Expandable Rows

A data grid might have a bunch of data visible across the row itself, but there might be even more data that a user might want to dig out of an entry once they find it. Perhaps it is data that doesn’t need to be cross-referenced in the same way column data is. This can be tricky to pull off, because of the way table cells are laid out. The data is still associated with a single row, but you often need more room than the width of one cell offers. With the KendoReact Data Grid component, you can pass in a detail prop with an arbitrary React component to show when a row is expanded. Super flexible!

Notice how the expanded details can have their own <Grid /> inside!

Responsive Design

Perhaps the most notoriously difficult thing to pull off with <table> designs is how to display them on small screens. Zooming out isn’t very good UX, nor is collapsing the table into something non-table-like. The thing about data grids is that they are all different, and you’ll know data is most important to your users best. The KendoReact Data Grid component helps with this by making your data grid scrollable/swipeable, and also being able to lock columns to make sure they continue to be easy to find and cross-reference.

Filtering Data

This is perhaps my favorite feature just because of how UX-focused it is. Imagine you’re looking at a big data grid of orders, and you’re like “Let me see all orders from White Clover Markets.” With a filtering feature, perhaps you quickly type “clover” into the filter input, and viola, all those orders are right there. That’s extra tricky stuff when you’re also supporting ordering and pagination — so it’s great all these features work together.

Grouping Data

Now this feature actually blows my mind 🤯 a little bit. Filtering and sorting are both very useful, but in some cases, they leave a little bit to be desired. For example, it’s easy to filter too far too quickly, leaving the data you are looking at very limited. And with sorting, you might be trying to look at a subset of data as well, but it’s up to your brain to figure out where that data begins and ends. With grouping, you can tell the data grid to strongly group together things that are the most important to you, but then still leverage filtering and sorting on top of that. It instantly makes your data exploration easier and more useful.

Localization

This is where you can really tell KendoReact went full monty. It would be highly unfortunate to pick some kind of component library and then realize that you need localization and realize it wasn’t made to be a first-class citizen. You avoid all that with KendoReact, which you can see in this Data Grid component. In the demo, you can flip out English for Spanish with a simple dropdown and see all the dates localized. You pull off any sort of translation and localization with the <LocalizationProvider> and <IntlProvider>, both comfortable React concepts.

Exporting to PDF or Excel

Here’s a live demo of this:

C’mon now! That’s very cool.

That’s not all…

Go check out the docs for the React Data Grid. There are a bunch more features we didn’t even get to here (row pinning! cell editing!). And here’s something to ease your mind: this component, and all the KendoReact components, are keyboard friendly and meet Section 508 accessibility standards. That is no small feat. When components are this complex and involve this much interactivity, getting the accessibility right is tough. So not only are you getting good-looking components that work everywhere, you’re getting richly interactive components that deliver UX beyond what you might even think of, and it’s all done fast and accessiblty. That’s pretty unreal, really.


The post A Themeable React Data Grid With Great UX-Focused Features appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

CSS-Tricks

, , , , , ,
[Top]

Comparing HTML Preprocessor Features

(This is a sponsored post.)

Of the languages that browsers speak, I’d wager that the very first one that developers decided needed some additional processing was HTML. Every single CMS in the world (aside from intentionally headless-only CMSs) is essentially an elaborate HTML processor: they take content and squoosh it together with HTML templates. There are dozens of other dedicated HTML processing languages that exist today.

The main needs of HTML processing being:

  • Compose complete HTML documents from parts
  • Template the HTML by injecting variable data

There are plenty of other features they can have, and we’ll get to that, but I think those are the biggies.

This research is brought to you by support from Frontend Masters, CSS-Tricks’ official learning partner.

Need front-end development training?

Frontend Masters is the best place to get it. They have courses on all the most important front-end technologies, from React to CSS, from Vue to D3, and beyond with Node.js and Full Stack.

Diagram showing partials and {{ data }} turning into a complete HTML document.

Consider PHP. It’s literally a “Hypertext Preprocessor.” On this very website, I make use of PHP in order to piece together bits of templated HTML to build the pages and complete content you’re looking at now.

<h2>   <?php the_title(); // Templating! ?> </h2>  <?php include("metadata.php"); // Partials! ?>

In the above code, I’ve squooshed some content into an HTML template, which calls another PHP file that likely contains more templated HTML. PHP covers the two biggies for HTML processing and is available with cost-friendly hosting — I’d guess that’s a big reason why PHP-powered websites power a huge chunk of the entire internet.

But PHP certainly isn’t the only HTML preprocessor around, and it requires a server to work. There are many others, some designed specifically to run during a build process before the website is ever requested by users.

Let’s go language-by-language and look at whether or not it supports certain features and how. When possible the link of the preprocessor name links to relevant docs.

Does it allow for templating?

Can you mix in data into the final HTML output?

Processor Example
Pug
- var title = "On Dogs: Man's Best Friend";
- var author = "enlore";
h1= title
p Written with love by #Chris Coyier
ERB
<%= title %>
<%= description %>

<%= @logged_in_user.name %>

Markdown
PHP
<?php echo $ post.title; ?>
<?php echo get_post_description($ post.id) ?>
Also has HEREDOC syntax.
Slim
tr
td.name = item.name
Haml
<h1><%= post.title %></h1>
<div class="subhead"><%= post.subtitle %></div>
Liquid
Hello {{ user.name }}!
Go html/template
{{ .Title }}
{{ $ address }}
Handlebars
{{firstname}} {{lastname}}
Mustache
Hello {{ firstname }}!
Twig
{{ foo.bar }}
Nunjucks
<h1>{{ title }}</h1>
Kit
<!-- $ myVar = We finish each other's sandwiches. -->
<p> <!-- $ myVar --> </p>
Sergey

Does it do partials/includes?

Can you compose HTML from smaller parts?

Processor Example
Pug
include includes/head.pug
ERB
<%= render 'includes/head' %>
Markdown
PHP
<?php include 'head.php'; ?>
<?php include_once 'meta.php'; ?>
Slim ⚠️
If you have access to the Ruby code, it looks like it can do it, but you have to write custom helpers.
Haml
.content
=render 'meeting_info'
Liquid {% render head.html %}
{% render meta.liquid %}
Go html/template {{ partial "includes/head.html" . }}
Handlebars ⚠️
Only through registering a partial ahead of time.
Mustache {{> next_more}}
Twig {{ include('page.html', sandboxed = true) }}
Nunjucks {% include "missing.html" ignore missing %}
{% import "forms.html" as forms %}
{{ forms.label('Username') }}
Kit <!-- @import "someFile.kit" -->
<!-- @import "file.html" -->
Sergey <sergey-import src="header" />

Does it do local variables with includes?

As in, can you pass data to the include/partial for it to use specifically? For example, in Liquid, you can pass a second parameter of variables for the partial to use. But in PHP or Twig, there is no such ability—they can only access global variables.

Processor Example
PHP
ERB <%= render(
partial: "card",
locals: {
title: "Title"
}
) %>
Markdown
Pug
Slim
Haml .content
= render :partial => 'meeting_info', :locals => { :info => @info }
Liquid {% render "name", my_variable: my_variable, my_other_variable: "oranges" %}
Go html/template {{ partial "header/site-header.html" . }}
(The period at the end is “variable scoping.”)
Handlebars {{> myPartial parameter=favoriteNumber }}
Mustache
Twig
Nunjucks {% macro field(name, value='', type='text') %}
<div class="field">
<input type="{{ type }}" name="{{ name }}" value="{{ value | escape }}" />
</div>
{% endmacro %}
Kit
Sergey

Does it do loops?

Sometimes you just need 100 <div>s, ya know? Or more likely, you need to loop over an array of data and output HTML for each entry. There are lots of different types of loops, but having at least one is nice and you can generally make it work for whatever you need to loop.

Processor Example
PHP for ($ i = 1; $ i <= 10; $ i++) {
echo $ i;
}
ERB <% for i in 0..9 do %>
<%= @users[i].name %>
<% end %>
Markdown
Pug for (var x = 1; x < 16; x++)
div= x
Slim - for i in (1..15)
div #{i}
Haml (1..16).each do |i|
%div #{i}
Liquid {% for i in (1..5) %}
{% endfor %}
Go html/template {{ range $ i, $ sequence := (seq 5) }}
{{ $ i }}: {{ $ sequence }
{{ end }}
Handlebars {{#each myArray}}
<div class="row"></div>
{{/each}}
Mustache {{#myArray}}
{{name}}
{{/myArray}}
Twig {% for i in 0..10 %}
{{ i }}
{% endfor %}
Nunjucks {% set points = [0, 1, 2, 3, 4] %}
{% for x in points %}
Point: {{ x }}
{% endfor %}
Kit
Sergey

Does it have logic?

Mustache is famous for philosophically being “logic-less”. So sometimes it’s desirable to have a templating language that doesn’t mix in any other functionality, forcing you to deal with your business logic in another layer. Sometimes, a little logic is just what you need in a template. And actually, even Mustache has some basic logic.

Processor Example
Pug #user
if user.description
h2.green Description
else if authorised
h2.blue Description
ERB <% if show %>
<% endif %>
Markdown
PHP <?php if (value > 10) { ?>
<?php } ?>
Slim - unless items.empty?If you turn on logic less mode:
- article
h1 = title
-! article
p Sorry, article not found
Haml if data == true
%p true
else
%p false
Liquid {% if user %}
Hello {{ user.name }}!
{% endif %}
Go html/template {{ if isset .Params "title" }}
<h4>{{ index .Params "title" }}</h4>
{{ end }}
Handlebars {{#if author}}
{{firstName}} {{lastName}}
{{/if}}
Mustache
It’s kind of ironic that Mustache calls itself “Logic-less templates”, but they do kinda have logic in the form of “inverted sections.”
{{#repo}}
{{name}}
{{/repo}}
{{^repo}}
No repos :(
{{/repo}}
Twig {% if online == false %}
Our website is in maintenance mode.
{% endif %}
Nunjucks {% if hungry %}
I am hungry
{% elif tired %}
I am tired
{% else %}
I am good!
{% endif %}
Kit
It can output a variable if it exists, which it calls “optionals”:
<dd class='<!-- $ myVar? -->'> Page 1 </dd>
Sergey

Does it have filters?

What I mean by filter here is a way to output content, but change it on the way out. For example, escape special characters or capitalize text.

Processor Example
Pug ⚠️
Pug thinks of filters as ways to use other languages within Pug, and doesn’t ship with any out of the box.
ERB
Whatever Ruby has, like:
"hello James!".upcase #=> "HELLO JAMES!"
Markdown
PHP $ str = "Mary Had A Little Lamb";
$ str = strtoupper($ str);
echo $ str; // Prints MARY HAD A LITTLE LAMB
Slim ⚠️
Private only?
Haml ⚠️
Very specific one for whitespace removal. Mostly for embedding other languages?
Liquid
Lots of them, and you can use multiple.
{{ "adam!" | capitalize | prepend: "Hello " }}
Go html/template ⚠️
Has a bunch of functions, many of which are filter-like.
Handlebars ⚠️
Triple-brackets do HTML escaping, but otherwise, you’d have to register your own block helpers.
Mustache
Twig {% autoescape "html" %}
{{ var }}
{{ var|raw }} {# var won't be escaped #}
{{ var|escape }} {# var won't be doubled-escaped #}
{% endautoescape %}
Nunjucks {% filter replace("force", "forth") %}
may the force be with you
{% endfilter %}
Kit
Sergey

Does it have math?

Sometimes math is baked right into the language. Some of these languages are built on top of other languages, and thus use that other language to do the math. Like Pug is written in JavaScript, so you can write JavaScript in Pug, which can do math.

Processor Support
PHP <?php echo 1 + 1; ?>
ERB <%= 1 + 1 %>
Markdown
Pug - const x = 1 + 1
p= x
Slim - x = 1 + 1
p= x
Haml %p= 1 + 1
Liquid {{ 1 | plus: 1 }}
Go html/template
{{add 1 2}}
Handlebars
Mustache
Twig {{ 1 + 1 }}
Nunjucks {{ 1 + 1 }}
Kit
Sergey

Does it have slots / blocks?

The concept of a slot is a template that has special areas within it that are filled with content should it be available. It’s conceptually similar to partials, but almost in reverse. Like you could think of a template with partials as the template calling those partials to compose a page, and you almost think of slots like a bit of data calling a template to turn itself into a complete page. Vue is famous for having slots, a concept that made its way to web components.

Processor Example
PHP
ERB
Markdown
Pug
You can pull it off with “mixins”
Slim
Haml
Liquid
Go html/template
Handlebars
Mustache
Twig {% block footer %}
© Copyright 2011 by you.
{% endblock %}
Nunjucks {% block item %}
The name of the item is: {{ item.name }}
{% endblock %}
Kit
Sergey <sergey-slot />

Does it have a special HTML syntax?

HTML has <angle> <brackets> and while whitespace matters a little (a space is a space, but 80 spaces is also… a space), it’s not really a whitespace dependant language like Pug or Python. Changing these things up is a language choice. If all the language does is add in extra syntax, but otherwise, you write HTML as normal HTML, I’m considering that not a special syntax. If the language changes how you write normal HTML, that’s special syntax.

Processor Example
PHP
ERB In Ruby, if you want that you generally do Haml.
Markdown
This is pretty much the whole point of Markdown.
# Title
Paragraph with [link](#link).

- List
- List

> Quote

Pug
Slim
Haml
Liquid
Go html/template
Handlebars
Mustache
Twig
Nunjucks
Kit ⚠️
HTML comment directives.
Sergey ⚠️
Some invented HTML tags.

Wait wait — what about stuff like React and Vue?

I’d agree that those technologies are component-based and used to do templating and often craft complete pages. They also can do many/most of the features listed here. Them, and the many other JavaScript-based-frameworks like them, are also generally capable of running on a server or during a build step and producing HTML, even if it sometimes feels like an afterthought (but not always). They also have other features that can be extremely compelling, like scoped/encapsulated styles, which requires cooperation between the HTML and CSS, which is an enticing feature.

I didn’t include them because they are generally intentionally used to essentially craft the DOM. They are focused on things like data retrieval and manipulation, state management, interactivity, and such. They aren’t really focused on just being an HTML processor. If you’re using a JavaScript framework, you probably don’t need a dedicated HTML processor, although it absolutely can be done. For example, mixing Markdown and JSX or mixing Vue templates and Pug.

I didn’t even put native web components on the list here because they are very JavaScript-focused.

Other considerations

  • SpeedHow fast does it process? Do you care?
  • Language — What was in what is it written in? Is it compatible with the machines you need to support?
  • Server or Build – Does it require a web server running to work? Or can it be run once during a build process? Or both?

Superchart

Templating Includes Local Variables Loops Logic Filters Math Slots Special Syntax
PHP
ERB ⚠️
Markdown
Pug ⚠️
Slim ⚠️ ⚠️
Haml ⚠️
Liquid
Go html/template ⚠️
Handlebars ⚠️
Mustache
Twig
Nunjucks
Kit ⚠️
Sergey ⚠️

The post Comparing HTML Preprocessor Features appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

CSS-Tricks

, , ,
[Top]

New Nuxt Features past v2.10

Nuxt offers an incredible developer experience, with a lot of performance and application setup best practices baked in. In recent releases, they’ve been working on taking this developer experience to the next level, with some newer features that speed up and simplify developer processes. Let’s explore some today.

I set up a repo and site for you to explore some of these features! You can check them out here:

A recipe page for crab cakes with red pepper. The recipe directions and ingredients are noted on the left and a featured image of the finished dish on the right.

Nuxt Content

You no longer have to pair Nuxt with an external headless CMS and do all of the setup, particularly if you’re not looking for something at a huge scale, but something smaller like a blog. Nuxt content offers a git-based headless CMS where you can write configuration in markdown, CSV, YAML, or XML, based on your preference. There are some out of the box configuration settings available to you, and writing custom configurations is as simple as creating a property.

What this means for development: you can write static Markdown files in a directory, and that can be your blog! We’re using the same dynamic pages API that you would typically use in Nuxt to generate this content.

It also offers full-text search out of the box, which is a lovely feature to add so quickly to a blog without having to integrate a third-party service.

This tutorial by Debbie O’Brien is an incredible guide, it walks you through every piece of setting it up, highly recommended.

Nuxt Components

One thing I noticed I was doing again and again and again was typing import code in all my components. I do have some snippets to make this a bit faster, but adding them each in every file was still interrupting the flow of my work just a bit.

Nuxt component module scans, imports, and registers components, so that we no longer need to do this. The components must be in the components directory, but we can use them in layouts, pages, and components themselves. 

The addition of this module is a small change to our nuxt.config.js:

export default {   components: true } 

Seriously, that’s it!

If you’d like a deep dive, this incredibly comprehensive guide by Kruite Patel has you covered.

If you use the component repeatedly, Nuxt will do some nice optimizations such as automatically creating a shared chunk for the component. Be mindful when using this on huge projects, though, as it may impact build times. 

Nuxt Image

Nuxt Image is a newer module that offers seamless and quick resizes and transforms for optimized responsive images. You can use their built-in optimizer, or work with 10+ ready-to-use popular providers such as Cloudinary or Fastly.

The code output from using their API are standard <img> and <picture> tags, so there’s no obfuscation when integrating them into your workflow.

After adding the module, you’ll be able to add configuration to the images via an images property in the nuxt.config.js, and designate breakpoints, providers, and other configurations:

export default {   image: {     // The screen sizes predefined by `@nuxt/image`:     screens: {       xs: 320,       sm: 640,       md: 768,       lg: 1024,       xl: 1280,       xxl: 1536,       '2xl': 1536     },     // Generate images to `/_nuxt/image/file.png`      staticFilename: '[publicPath]/images/[name]-[hash][ext]',      domains: [         'images.unsplash.com'     ],     alias: {       unsplash: 'https://images.unsplash.com'     }   } } 

This is just a sampling of some of the options available to you, provided as an example. The full documentation is here.

And then the usage is similar to any Vue component:

<nuxt-img src="/nuxt-icon.png" />

or

<nuxt-picture src="/nuxt-icon.png" />

Further information and all options are documented here. Hat tip to Ben Hong for letting me know this was available. He has a few Nuxt resources out there that are worth exploring, too!

Sample Repo

I’ve created a sample repo for you to explore that uses all of this functionality. It’s a small recipe blog with nuxt-content for the recipe entries, Nuxt components so that I didn’t need to define imports, and nuxt-image for the image transformations.

You can visit it here to see it all in action, fork it, play around with it, and make it your own.

You can see in it how I used the $ img API in Nuxt image for background images here, too, which is not yet fully documented.


Nuxt offers incredible developer experience. Nuxt is even coming out with a new version soon with more updates, always expertly implemented. It’s why using Nuxt is continually such a joy, and proves to be a great framework for teams and single developers alike.


The post New Nuxt Features past v2.10 appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

CSS-Tricks

, , ,
[Top]

ES2021 Features

Hemanth HM very succinctly shows off ES2021 features. Gosh it doesn’t feel like that long ago that all we could talk about is ES2015, and now that’s over a half-decade behind us.

There are new things like “arbitrarily chuck underscores in numbers.” I kinda dig that. Like 1_000_000_000 is the same as 1000000000 but more readable. To be honest, I barely even understand the other features.

It’s interesting to observe the JavaScript truck moving forward with new features, while also being around people that write Go a lot and how starkly different, philosophically, it seems to me. Like there is only one kind of loop in Go, a for loop, and that’s it, while JavaScript has a bunch of them—four just for Arrays! Go doesn’t add syntactic sugar on purpose, while JavaScript feels addicted to it. Sugar is a helluva drug.

Direct Link to ArticlePermalink


The post ES2021 Features appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

CSS-Tricks

,
[Top]

Web Features That May Not Work As You’d Expect

As the web gets more and more capable, developers are able to make richer online experiences. There are times, however, where some new web capabilities may not work as you would expect in the interest of usability, security and privacy.

I have run into situations like this. Like lazy loading in HTML. It’s easy to drop that attribute onto an image element only to realize… it actually needs more than that to do its thing. We’ll get into that specific one in a moment as we look at a few other features that might not work exactly as you‘d expect.

This limitation has been around for a while, but it does show how browser features can be exploited. One possible exploit is an anchor gets some :visited link style in CSS and is positioned off screen. With the off-screen anchor, one could use JavaScript to change the anchor’s href value and see if a particular href causes the link to appear visited—reconstructing a user’s history in the process.

Known as the CSS History Leak, this was so pervasive at one time that the Federal Trade Commission, the United States’ consumer protection agency, had imposed harsh fines for exploiting it.

These days, attempting to use getComputedStyle on a :visited link returns the style of the :unvisited link instead. That’s just one of those things you have to know because that’s different from how it intuitively ought to work.

But we can get around this in two ways:

  1. make the visited link’s style trigger a side effect (e.g. a layout shift), or
  2. leverage the sibling (~ or +) or child (>) CSS selectors to render another style.

Regarding side effects, while there are some clever yet fragile ways to do this, the options we have for styling :visited links are limited and some styles (like background-color) will only work if they’re applied to unvisited links. As for using a sibling or child, executing getComputedStyle on these returns the style as if the link wasn’t visited to begin with.

Browsers don’t cache assets across sites anymore

One advantage of a CDN was that they allowed for a particular resource (like Google Fonts) to be cached in the browser for use across different websites. While this does provide a big performance win, it has grave privacy implications.

Given that an asset that’s already cached will take longer to load than one that’s not, a site could perform a timing attack to not only see your site history but also expose both who you are and your online activity. Jeff Kaufman gives an example:

Unfortunately, a shared cache enables a privacy leak. Summary of the simplest version:

  • I want to know if you’re a moderator on www.forum.example.
  • I know that only pages under load www.forum.example/moderators/header.css.
  • When you visit my page I load www.forum.example/moderators/header.css and see if it came from cache.

In light of this, browsers don’t offer this anymore.

performance.now() may be inaccurate

A scary group of vulnerabilities came out as couple of years ago, one of which was called Spectre. For an in depth explanation, see Google’s leaky.page (works best in Chromium) as a proof of concept. But for the purposes of this article, just know that the exploit relies on getting highly accurate timing, which is something that performance.now() provides, to try and map sensitive CPU data.

Text about the demo on the left side of the page and two black Germaine-looking code blocks on the right side with a black background and green text.
The demo at leaky.page

To mitigate Spectre, browsers have reduced its accuracy and may add noise as well. These range from 20μs to 1ms and can be changed based on various conditions like HTTP headers and browser settings.

Lazy loading with the loading attribute doesn’t work without JavaScript

Lazy loading is a technique where assets are only loaded in the browser when it scrolls into the viewport. Until recently, we could only implement this in JavaScript using IntersectionObserver or onscroll. Except for Safari, we can apply the loading attribute to images and iframes (in Chromium) and the browser will handle lazy loading.

Note that lazy loading can’t be polyfilled since an image is probably loading by the time you check for the loading attribute’s support.

Being able to do this in HTML makes it sound like the attribute doesn’t require JavaScript at all, but it does. From the WHATWG spec:

  1. If scripting is disabled for an element, return false.

    Note
    This is an anti-tracking measure, because if a user agent supported lazy loading when scripting is disabled, it would still be possible for a site to track a user’s approximate scroll position throughout a session, by strategically placing images in a page’s markup such that a server can track how many images are requested and when.

I’ve seen articles mention that this attribute is how you support lazy loading “without JavaScript” which isn’t true, though it is true you don’t have to write any.

Browsers can limit features based on user preferences

Some users might opt to heavily restrict browser functionality in the interest of further security and privacy. Firefox and Tor are two browsers that do this through the resist fingerprint setting which does things like reducing the precision of certain variables (dimensions and time), omitting certain variables entirely, limiting or disabling some Web APIs and never matching media queries. WebKit has a document outlining how browsers can approach fingerprint resistance.

Note that this goes beyond the standard anti-tracking features that browsers implement. It’s unlikely that a user will enable this as they would need a very specific threat model to do so. Part of this can be countered with progressive enhancement, graceful degradation, and understanding your users. This limitation is a big issue when you actually need fingerprinting, like fraud detection. So, if it’s absolutely necessary, look for an alternative means.

Screen readers might not relay the semantics of certain elements

Semantic HTML is great for many reasons, most notably that it conveys meaning in markup that software, like screen readers, interpret and announce to users who rely on them to navigate the web. It’s essential for crafting accessible websites. But, at times, those semantics aren’t conveyed—at least how you might expect. Something might be accessible, but still have usability issues.

An example is the way removing a list’s markers removes its semantic meaning in WebKit with VoiceOver enabled. It’s a very common pattern, most notably for site navigation. Apple Accessibility Standards Manager James Craig explains why it’s a usability issue, though, citing the W3C’s Design Principle of Priority of Constituents:

In case of conflict, consider users over authors over implementors over specifiers over theoretical purity. In other words costs or difficulties to the user should be given more weight than costs to authors;

Another case where semantics might not be relayed is with emphasis. Take inline elements like strong, em, mark, ins, del, and data—all elements that have semantic meanings, but are unlikely to be read out because they can get noisy. This can be changed in a user’s screenreader’s settings, but if you really want it to be read you can declare it in visually hidden in the content property of either a :before or :after pseudo-element.

To illustrate this I made a brief example to see how NVDA with Firefox 89 and VoiceOver with Safari 14.6 read out semantic elements.

Unlike VoiceOver, NVDA reads out some of the semantic elements (del, ins and mark) and tries to emphasize text by gradually increasing the volume of emphasized text. Both of them have no trouble reading out the :before/:after psudo-elements however. Also, VoiceOver read out the tag’s brackets (greater than, less than), though both screenreaders have the ability to change how much punctuation is read.

To see whether or not you need to emphasize the emphasis, make sure you test with your users and see what they need. I didn’t focus on the visual aspect but the default styling of emphasis elements may be inconsistent across browsers, so make sure you provide suitable styling to go along with it.

Web storage might not be persistent

The WHATWG Web storage specification includes a section on privacy that outlines possible ways to prevent storage from being a tracking vector. One such way is to make the data expire. This is why Safari controversially limits script writable storage for seven days. Note that this doesn’t apply to “installed” websites added to the home screen.

Conclusion

Interesting, isn’t it? Some web features that we might expect to work a certain way just don’t. That isn’t to say that the features are wrong and need to be fixed, but more of a heads up as we write code.

It’s worth examining your own assumptions during development. Critically examine what your users need and factor it in as you make your site. You’re certainly welcome to work around these these as you encounter them, but in cases where you’re unable to, make sure to find and provide reasonable progressive enhancement and graceful degradation. It’s OK if users don’t experience a website the exact same way in every browser as long as they’re able to do what they need to.

That’s my list of things that don’t work the way I expect them to. What’s on your list? I’m sure you’ve got some and I’d love to see them in the comments!


The post Web Features That May Not Work As You’d Expect appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

CSS-Tricks

, , ,
[Top]

Chromium spelling and grammar features

Delan Azabani digs into the (hopefully) coming soon ::spelling-error and ::grammar-error pseudo selectors in CSS. Design control is always nice. Hey, if we can style scrollbars and style selected text, why not this?

The squiggly lines that indicate possible spelling or grammar errors have been a staple of word processing on computers for decades. But on the web, these indicators are powered by the browser, which doesn’t always have the information needed to place and render them most appropriately. For example, authors might want to provide their own grammar checker (placement), or tweak colors to improve contrast (rendering).

To address this, the CSS pseudo and text decoration specs have defined new pseudo-elements ::spelling-error and ::grammar-error, allowing authors to style those indicators, and new text-decoration-line values spelling-error and grammar-error, allowing authors to mark up their text with the same kind of decorations as native indicators.

This is a unique post too, as Delan is literally the person implementing the feature in the browser. So there is all sorts of deep-in-the-weeds stuff about how complex all this is and what all the considerations are. Kinda like, ya know, web development. Love to see this. I’ve long felt that it’s weird there is seemingly such little communication between browser engineers and website authors, despite the latter being a literal consumer of the former’s work.

Direct Link to ArticlePermalink


The post Chromium spelling and grammar features appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

CSS-Tricks

, , ,
[Top]

SVGator 3.0 Reshapes the Way You Create and Animate SVG With Extensive New Features

Building animations can get complicated, particularly compelling animations where you aren’t just rolling a ball across the screen, but building a scene where many things are moving and changing in concert. When compelling animation is the goal, a timeline GUI interface is a long-proven way to get there. That was true in the days of Flash (RIP), and still true today in every serious video editing tool.

But what do we have on the web for animating vector graphics? We have SVGator, an innovative and unique SVG animation creator that doesn’t require any coding skills! With the new SVGator 3.0 release it becomes a ground-breaking tool for building web animations in a visually intuitive way.

It’s worth just looking at the homepage to see all the amazing animations built using the tool itself.

A powerful tool right at your fingertips

I love a good browser-based design tool. All my projects in the cloud, waiting for me no matter where I roam. Here’s my SVGator project as I was working on a basic animation myself:

Users of any design tool should feel at home here. It comes with an intuitive interface that rises to the demands of a professional workflow with options that allow more control over your workspace. You will find a list of your elements and groups on the left side, along with the main editing tools right at the top. All the properties can be found on the right side and you can bring any elements to life by setting up keyframes on the timeline.

I’ll be honest: I’ve never really used SVGator before, I read zero documentation, and I was able to fiddle together these animated CSS-Tricks stars in just a few minutes:

See that animation above?

  • It was output as a single animation-stars.svg document I was able to upload to this blog post without any problem.
  • Uses only CSS inside the SVG for animation. There is a JavaScript-animation output option too for greater cross-browser compatibility and extra features (e.g. start animation only when it enters the view or start the animation on click!)
  • The final file is just 5 KB. Tiny!
  • The SVG, including the CSS animation bits, are pre-optimized and compressed.
  • And again, it only took me a couple minutes to figure out.

Imagine what you could do if you actually had design and animation talent.

Intuitive and useful export options

Optimized SVG output

SVGator is also an outstanding vector editor

With the 3.0 release, SVGator is no longer just an animation tool, but a full-featured SVG creator! This means that there’s no need for a third-party vector editing tool, you can start from scratch in SVGator or edit any pre-existing SVGs that you’ve ever created. A Pen tool with fast editing options, easily editable shapes, compound options, and lots of other amazing features will assist you in your work.

That’s particularly useful with morphing tweens, which SVGator totally supports!

Here’s my one word review:

Cool (animating in and out with scale and blur effects)

The all-new interface in SVGator has a sidebar of collapsible panels for controlling all aspects of the design. See here how easy this animation was to make by controlling a line of text, the transforms, and the filters, and then choosing keyframes for those things below.

Suitable plans for everyone

If you want to use SVGator as a vector editing tool, that’s absolutely free, which means that you can create and export an endless number of static vector graphics, regardless of your subscription plan In fact, even on the Free plan, you export three animations per month. It’s just when you want more advanced animation tooling or unlimited animated exports per month than that you have to upgrade to a paid plan.


The post SVGator 3.0 Reshapes the Way You Create and Animate SVG With Extensive New Features appeared first on CSS-Tricks.

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

CSS-Tricks

, , , , ,
[Top]

Interaction Media Features and Their Potential (for Incorrect Assumptions)

This is an updated and greatly expanded version of the article originally published on dev.opera back in 2015. That article referenced the Editor’s Draft, 24 March 2015 of the specification Media Queries Level 4, and contained a fairly big misunderstanding about how any-hover:none would end up being evaluated by browsers in practice.

The spec has since been updated (including clarifications and examples that I submitted following the publication of the original article), so this updated version removes the incorrect information of the original and brings the explanations in line with the most recent working draft. It also covers additional aspects relating to JavaScript touch/input detection.

The Media Queries Level 4 Interaction Media Featurespointer, hover, any-pointer and any-hover — are meant to allow sites to implement different styles and functionality (either CSS-specific interactivity like :hover, or JavaScript behaviors, when queried using window.matchMedia), depending on the particular characteristics of a user’s input devices.

Although the specification is still in working draft, interaction media features are generally well supported, though, to date, there are still some issues and inconsistencies in the various browser implementations — see the recent pointer/hover/any-pointer/any-hover test results, with references to relevant browser bugs.

Common use cases cited for interaction media features are often “make controls bigger/smaller depending on whether the users has a touchscreen device or is using a mouse/stylus” and “only use a CSS dropdown menu if the user has an input that allows hover-based interactions.”

@media (pointer: fine) {   /* using a mouse or stylus - ok to use small buttons/controls */ } @media (pointer: coarse) {   /* using touch - make buttons and other "touch targets" bigger */ } @media (hover: hover) {   /* ok to use :hover-based menus */ } @media (hover: none) {   /* don't use :hover-based menus */ }

There are also examples of developers using these new interaction media features as a way of achieving standards-based “touch detection,” often just for listening to touch events when the device is identified as having a coarse pointer.

if (window.matchMedia && window.matchMedia("(pointer:coarse)").matches) {   /* if the pointer is coarse, listen to touch events */   target.addEventListener("touchstart", ...);   // ... } else {   /* otherwise, listen to mouse and keyboard events */   // ... }

However, these approaches are slightly naive, and stem from a misunderstanding of what these interaction media queries are designed to tell us.

What’s the primary input?

One of the limitations of pointer and hover is that, by design, they only expose the characteristics of what a browser deems to be the primary pointer input. What the browser thinks, and what a user is actually using as their primary input, may differ — particularly now that the lines between devices, and the types of inputs they support, is becoming more and more blurry.

Microsoft Surface with a keyboard, trackpad, external bluetooth mouse, touchscreen.
Which one’s the “primary” input? the answer may depend on the activity.

Right out of the gate, it’s worth noting that interaction media features only cover pointer inputs (mouse, stylus, touchscreen). They don’t provide any way of detecting if a user’s primary input is a keyboard or keyboard-like interface, such as a switch control. In theory, for a keyboard user, a browser could report pointer: none, signaling that the user’s primary input is not a pointer at all. However, in practice, no browser offers a way for users to specify that they are in fact keyboard users. So keep in mind that, regardless of what the interaction media feature queries may return, it’s worth making sure that your site or app also works for keyboard users.

Traditionally, we could say that a phone or tablet’s primary input is the touchscreen. However, even on these devices, a user may have an additional input, like a paired bluetooth mouse (a feature that has been available for years on Android, is now supported in iPadOS, and is sure land in iOS), that they are using as their primary input.

An Android phone with a paired bluetooth keyboard and mouse, with the screen showing an actual mouse pointer and right-click context menu in Chrome

An iPad with a paired bluetooth keyboard, mouse, and Apple Pencil, with the screen showing the mouse “dot” and right-click context menu in Safari

In this case, while the device nominally has pointer: coarse and hover: none, users may actually be using a fine pointer device that is capable of hovers. Similarly, if a user has a stylus (like the Apple Pencil), their primary input may still be reported as the touchscreen, but rather than pointer: coarse, they now have an input that can provide fine pointer accuracy.

In these particular scenarios, if all the site is doing is making buttons and controls bigger and avoiding hover-based interactions, that would not be a major problem for the user: despite using a fine and hover-capable mouse, or a fine but still not hover-capable stylus, they will get styling and functionality aimed at the coarse, non-hover-capable touchscreen.

If the site is using the cues from pointer: coarse for more drastic changes, such as then only listening to touch events, then that will be problematic for users — see the section about incorrect assumptions that can completely break the experience.

However, consider the opposite: a “regular” desktop or laptop with a touchscreen, like Microsoft’s Surface. In most cases, the primary input will be the trackpad/mouse — with pointer:fine and hover:hover — but the user may well be using the touchscreen, which has coarse pointer accuracy and does not have hover capability. If styling and functionality are then tailored specifically to rely on the characteristics of the trackpad/mouse, the user may find it problematic or impossible to use the coarse, non-hover-capable touchscreen.

Feature Touchscreen Touchscreen + Mouse Desktop/Laptop Desktop/Laptop + Touchscreen
pointer:coarse true true false false
pointer:fine false false true true
hover:none true true false false
hover:hover false false true true

For a similar take on this problem, see ”The Good & Bad of Level 4 Media Queries” by Stu Cox. While it refers to an even earlier iteration of the spec that only contained pointer and hover and a requirement for these features to report the least capable, rather than the primary, input device.

The problem with the original pointer and hover on their own is that they don’t account for multi-input scenarios, and they rely on the browser to be able to correctly pick a single primary input. That’s where any-pointer and any-hover come into play.

Testing the capabilities of all inputs

Instead of focusing purely on the primary pointer input, any-pointer and any-hover report the combined capabilities of all available pointer inputs.

In order to support multi-input scenarios, where different (pointer-based) inputs may have different characteristics, more than one of the values for any-pointer (and, theoretically, any-hover, but this aspect is useless as we’ll see later) can match, if different input devices have different characteristics< (compared to pointer and hover, which only ever refer to the capabilities of the primary pointer input). In current implementations, these media features generally evaluate as follows:

Feature Touchscreen Touchscreen + Mouse Desktop/Laptop Desktop/Laptop + Touchscreen
any-pointer:coarse true true false true
any-pointer:fine false true true true
any-hover:none false false false false
any-hover:hover false true true true
Comparison of Firefox on Android’s media query results with just the touchscreen, and when adding a bluetooth mouse. Note how pointer and hover remain the same, but any-pointer and any-hover change to cover the new hover-capable fine input.

Going back to the original use cases for the interaction media features, instead of basing our decision to provide larger or smaller inputs or to enable hover-based functionality only on the characteristics of the primary pointer input, we can make that decision based on the characteristics of any available pointer inputs. Roughly translated, instead of saying “make all controls bigger if the primary input has pointer: coarse” or “only offer a CSS menu if the primary input has hover: hover,” we can build media queries that equate to saying, “if any of the pointer inputs is coarse, make the controls bigger” and “only offer a hover-based menu if at least one of the pointer inputs available to the user is hover-capable.”

@media (any-pointer: coarse) {   /* at least one of the pointer inputs     is coarse, best to make buttons and      other "touch targets" bigger (using      the query "defensively" to target      the least capable input) */ } @media (any-hover: hover) {   /* at least one of the inputs is       hover-capable, so it's at least       possible for users to trigger      hover-based menus */ }

Due to the way that any-pointer and any-hover are currently defined (as “the union of capabilities of all pointing devices available to the user”), any-pointer: none will only ever evaluate to true if there are no pointer inputs available, and, more crucially, any-hover: none will only ever be true if none of the pointer inputs present are hover-capable. Particularly for the latter, it’s therefore not possible to use the any-hover: none query to determine if only one or more of the pointer inputs present is not hover-capable — we can only use this media feature query to determine whether or not all inputs are not hover-capable, which is something that can just as well be achieved by checking if any-hover: hover evaluates to false. This makes the any-hover: none query essentially redundant.

We could work around this by inferring that if any-pointer: coarse is true, it’s likely a touchscreen, and generally those inputs are not hover-capable, but conceptually, we’re making assumptions here, and the moment there’s a coarse pointer that is also hover-capable, that logic falls apart. (And for those doubting that we may ever see a touchscreen with hover, remember that some devices, like the Samsung Galaxy Note and Microsoft’s Surface, have a hover-capable stylus that is detected even when it’s not touching the digitizer/screen, so some form of “hovering touch” detection may not be out of the question in the future.)

Combining queries for more educated guesses

The information provided by any-pointer and any-hover can of course be combined with pointer and hover, as well as the browser’s determination of what the primary input is capable of, for some slightly more nuanced assessments.

@media (pointer: coarse) and (any-pointer: fine) {   /* the primary input is a touchscreen, but      there is also a fine input (a mouse or       perhaps stylus) present. Make the design      touch-first, mouse/stylus users can      still use this just fine (though it may       feel a big clunky for them?) */ } @media (pointer: fine) and (any-pointer: coarse) {   /* the primary input is a mouse/stylus,      but there is also a touchscreen       present. May be safest to make       controls big, just in case users do       actually use the touchscreen? */ } @media (hover: none) and (any-hover: hover) {   /* the primary input can't hover, but      the user has at least one other      input available that would let them      hover. Do you trust that the primary      input is in fact what the user is       more likely to use, and omit hover-      based interactions? Or treat hover       as as something optional — can be       used (e.g. to provide shortcuts) to       users that do use the mouse, but       don't rely on it? */ }

Dynamic changes

Per the specification, browsers should re-evaluate media queries in response to changes in the user environment. This means that pointer, hover, any-pointer, and any-hover interaction media features can change dynamically at any point. For instance, adding/removing a bluetooth mouse on a mobile/tablet device will trigger a change in any-pointer / any-hover. A more drastic example would be a Surface tablet, where adding/removing the device’s “type cover” (which includes a keyboard and trackpad) will result in changes to the primary input itself (going from pointer: fine / hover: hover when the cover is present, to pointer: coarse / hover: none when the Surface is in “tablet mode”).

Screenshots of Firefox on a Surface tablet. With the cover attached, pointer:finehover:hoverany-pointer:coarseany-pointer:fine, and any-hover:hover are true; once the cover is removed (and Windows asks if the user wants to switch to “tablet mode”), touch becomes the primary input with pointer:coarse and hover:none, and only any-pointer:coarse and any-hover:none are true.

If you’re modifying your site’s layout/functionality based on these media features, be aware that the site may suddenly change “under the user’s feet” whenever the inputs change — not just when the page/site is first loaded.

Media queries may not be enough — roll on scripting

The fundamental shortcoming of the interaction media features is that they won’t necessarily tell us anything about the input devices that are in use right now. For that, we may need to dig deeper into solutions, like What Input?, that keep track of the specific JavaScript events fired. But of course, those solutions can only give us information about the user’s input after they have already started interacting with the site — at which point it may be too late to make drastic changes to your layout or functionality.

Keep in mind that even these JavaScript-based approaches can just as easily lead to incorrect results. That’s especially true on mobile/tablet platforms, or in situations where assistive technologies are involved, where it is common to see “faked” events being generated. For instance, if we look over the series of events fired when activating a control on desktop using a keyboard and screen reader, we can see that fake mouse events are triggered. Assistive technologies do this because, historically, a lot of web content has been coded to work for mouse users, but not necessarily for keyboard users, making a simulation of those interactions necessary for some functionalities.

Similarly, when activating “Full Keyboard Support” in iOS’s Settings → Accessibility → Keyboard, it’s possible for users to navigate web content using an external bluetooth keyboard, just as they would on desktop. But if we look at the event sequence for mobile/tablet devices and paired keyboard/mouse, that situation produces pointer events, touch events, and fallback mouse events — the same sequence we’d get for a touchscreen interaction.

Showing iOS settings with Full Keyboard Access enabled on the left and an iPhone browser window open to the right with the What Input tool.
When enabled, iOS’s “Full Keyboard Access” setting results in pointer, touch, and mouse events. What Input? identifies this as a touch input

In all these situations, scripts like What Input? will — understandably, through no fault of its own — misidentify the current input type.

Incorrect assumptions that can completely break the experience

Having outlined the complexity of multi-input devices, it should be clear by now that approaches that only listen to specific types of events, like the form of “touch detection” we see commonly in use, quickly fall apart.

if (window.matchMedia && window.matchMedia("(pointer: coarse)").matches) {   /* if the pointer is coarse, listen to touch events */   target.addEventListener("touchstart", ...);   // ... } else {   /* otherwise, listen to mouse and keyboard events */   target.addEventListener("click", ...);   // ... }

In the case of a “touch” device with additional inputs — such as a mobile or tablet with an external mouse — this code will essentially prevent the user from being able to use anything other than their touchscreen. And on devices that are primarily mouse-driven but do have a secondary touchscreen interface — like a Microsoft Surface — the user will be unable to use their touchscreen.

Instead of thinking about this as “touch or mouse/keyboard,” realize that it’s often a case of “touch and mouse/keyboard.” If we only want to register touch events when there’s an actual touchscreen device for performance reasons, we can try detecting any-pointer: coarse. But we should also keep other regular event listeners for mouse and keyboard.

/* always, as a matter of course, listen to mouse and keyboard events */ target.addEventListener("click", ...);  // ...  if (window.matchMedia && window.matchMedia("(any-pointer: coarse)").matches) {   /* if there's a coarse pointer, *also* listen to touch events */   target.addEventListener("touchstart", ...);   // ... }

Alternatively, we could avoid this entire conundrum about different types of events by using pointer events, which cover all types of pointer inputs in a single, unified event model, and are fairly well supported.

Give users an explicit choice

One potential solution for neatly circumventing our inability to make absolute determinations about which type of input the users are using may be to use the information provided by media queries and tools like What Input?, not to immediately switch between different layouts/functionalities — or worse, to only listen to particular types of events, and potentially locking out any additional input types — but to use them only as signals for when to provide users with an explicit way to switch modes.

For instance, see the way Microsoft Office lets you change between “Touch” and “Mouse” mode. On touch devices, this option is shown by default in the application’s toolbar, while on non-touch devices, it’s initially hidden (though it can be enabled, regardless of whether or not a touchscreen is present).

Screenshot of Microsoft Office's 'Touch/Mouse mode' dropdown, and a comparison of (part of) the toolbar as it's presented in each mode

A site or web application could take the same approach, and even set the default based on what the primary input is — but still allow users to explicitly change modes. And, using an approach similar to What Input?, the site could detect the first appearance of a touch-based input, and alert/prompt the user if they want to switch to a touch-friendly mode.

Potential for incorrect assumptions — query responsibly

Using Media Queries Level 4 Interaction Media Features and adapting our sites based on the characteristics of the available primary or additional pointer input is a great idea — but beware false assumptions about what these media features actually say. As with similar feature detection methods, developers need to be aware of what exactly they’re trying to detect, the limitations of that particular detection, and most importantly, consider why they are doing it — in a similar way to the problem I outlined in my article on detecting touch.

pointer and hover tell us about the capabilities of whatever the browser determines to be the primary device input. any-pointer and any-hover tell you about the capabilities of all connected inputs, and combined with information about the primary pointer input, they allow us to make educated guesses about a user’s particular device/scenario. We can use these features to inform our layout, or the type of interaction/functionality we want to offer; but don’t discount the possibility that those assumptions may be incorrect. The media queries themselves are not necessarily flawed (though the fact that most browsers seem to still have quirks and bugs adds to the potential problems). It just depends on how they’re used.

With that, I want to conclude by offering suggestions to “defend” yourself from the pitfalls of input detections.

Don’t

Assume a single input type. It’s not “touch or mouse/keyboard” these days, but “touch and mouse/keyboard” — and the available input types may change at any moment, even after the initial page load.

Just go by pointer and hover. the “primary” pointer input is not necessarily the one that your users are using.

Rely on hover in general. Regardless of what hover or any-hover suggest, your users may have a pointer input that they’re currently using that is not hover-capable, and you can’t currently detect this unless it’s the primary input (since hover: none  is true if that particular input lacks hover, but any-hover: none will only ever be true if none of the inputs are hover-capable). And remember that hover-based interfaces generally don’t work for keyboard users.

Do

Make your interfaces “touch-friendly.” If you detect that there’s an any-pointer:coarse input (most likely a touchscreen), consider providing large touch targets and sufficient spacing between them. Even if the user is using another input, like a mouse, at that moment, no harm done.

Give users a choice. If all else fails, consider giving the user an option/toggle to switch between touch or mouse layouts. Feel free to use any information you can glean from the media queries (such as any-pointer: coarse being true) to make an educated guess about the toggle’s initial setting.

Remember about keyboard users. Regardless of any pointer inputs that the user may or may not be using, don’t forget about keyboard accessibility — it can’t be conclusively detected, so just make sure your stuff works for keyboard users as a matter of course.


The post Interaction Media Features and Their Potential (for Incorrect Assumptions) appeared first on CSS-Tricks.

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

CSS-Tricks

, , , , , ,
[Top]