“Yes or No?” β€” One Checkbox vs Two Radio Buttons

Great research by Sara Soueidan:

If you have a question with a binary Yes/No answer, is it better to use one checkbox or two radio buttons?

As per usual: It Depends™. The consensus however seems to be that requiring an explicit β€œNo” is the determining factor to avoid the simple checkbox.

Over at CSS-Tricks Chris Coyier has collected several options how to implement this, in one handy pen:

See the Pen Ways to ask Yes or No by Chris Coyier (@chriscoyier) on CodePen.

Do note that not all of those listed options are as usable as the others. As Sara says:

[A]s with all user interfaces, nothing beats the input you can get from user testing and research.

“Yes or No?” β€” One Checkbox vs Two Radio Buttons →

Avoiding tab styles for navigation

Adam Silver, who works at/with the fine folks at GOV.UK:

Tabs should only look like tabs if they behave like tabs otherwise it can be in disorienting and confusing for users.

Shown above is the old layout that featured the tabs (which are actually links, here). The new version still has the links in place at the same position, but they’ve styled them differently so that the look less like tabs.

Avoiding tab styles for navigation →

8 Tips to Make Your Website Feel Like an iOS App

Very nice video by Sam Selikoff in which he sets up a web manifest to make his site feel more app-like. Great production quality.

There are some tweaks I’d suggest though:

  1. Fixate the header using position: sticky; instead of position: fixed;. No need for that extra margin on the content then. Update: See note below

  2. Don’t set the header height to a fixed number, as not all devices have (equally sized) notches. Use the User Agent Variable safe-area-inset-top instead, and apply it as the top padding:

    header {
      padding-top: env(safe-area-inset-top);
    }
  3. Don’t disable scaling in case your app does not follow the system font sizing β€” which it does not by default. To make it do follow the system font sizing, use this snippet:

    @supports (font: -apple-system-body) {
      html {
        font: -apple-system-body;
      }
    }

    πŸ’‘ You can still override the font-family after that. That way you can have your custom font and also follow the preferred system Text Size

As an extra: to prevent that long-press behavior when the app is not added to the home screen, set -webkit-touch-callout: none; on the links.

On Twitter Sam pointed me out that when using position: sticky; there’s an issue with overscroll/bounce: The header will shift down along with the body as you do so.

In theory one could easily fix this by applying some overscroll-behavior on the body. However, as Safari does not support overscroll-behavior you’ll have to resort to a hack to prevent overscrolling in Safari. But, as that hack relies on 100vh β€” which Safari also doesn’t understand properly β€” you’ll have to use another hack to make Safari use the correct value for 100vh.

(* mumbles something about Safari being the new IE6 *)

Did this help you out? Like what you see?
Thank me with a coffee.

I don't do this for profit but a small one-time donation would surely put a smile on my face. Thanks!

β˜•οΈ Buy me a Coffee (€3)

To stay in the loop you can follow @bramus or follow @bramusblog on Twitter.

HTML Forms: How (and Why) to Prevent Double Form Submissions

When double clicking a submit button your form will be sent twice. Using JavaScript you can prevent this from happening, but wouldn’t it be nice if this behavior could be tweaked by use of an attribute on the <form>? If you think so, feel free to give this issue a thumbs up.

Today Sebastian wondered:

I quickly chimed in saying that I do tend to lock up forms when submitting them. Let me explain why …

~

I started locking up submitted forms as users of the apps I’m building reported that sometimes the actions they had taken β€” such as adding an entry β€” were performed twice. I took me some time to figure out what exactly was going on, but eventually I found out this was because they were double clicking the submit button of the forms. As they double clicked the button, the form got sent over twice. By locking up forms after their first submission, all successive submissions β€” such as those triggered by that second click of a double click β€” are ignored.

~

To prevent these extra form submissions from being made I don’t hijack the form with Ajax nor do I perform any other complicated things. I let the inner workings of the web and forms in the browser be: when pressing the submit button the browser will still collect all form data, build a new HTTP request, and execute that request.

What I simply do is extend the form’s capabilities by adding a flag β€” by means of a CSS class β€” onto the form to indicate whether it’s being submitted or not. I can then use this flag’s presence to deny any successive submissions, and also hook some CSS styles on. β€” Progressive Enhancement, Yay! πŸŽ‰

The code looks as follows:

 // Prevent Double Submits
document.querySelectorAll('form').forEach(form => {
	form.addEventListener('submit', (e) => {
		// Prevent if already submitting
		if (form.classList.contains('is-submitting')) {
			e.preventDefault();
		}
		
		// Add class to hook our visual indicator on
		form.classList.add('is-submitting');
	});
});

πŸ’‘ Although the problem initially was a double click problem, we don’t listen for any clicks on the submit button but listen for the form’s submit event. This way our code not only works when clicking any of the submit buttons, but also when pressing enter to submit.

With that .is-submitting class in place, we can then attach some extra CSS onto the form to give the user visual feedback. Here’s a few examples:

See the Pen
Prevent Form Double Submits
by Bramus (@bramus)
on CodePen.

See the Pen
Prevent Form Double Submits (Alternative version)
by Bramus (@bramus)
on CodePen.

Note that this solution might not cover 100% of all possible scenarios, as it doesn’t take failing networks and other things that might go wrong into account. However, as I’m relying on the basic mechanisms of the web I think I can also rely on the browser to show that typical “Confirm Form Resubmission” interstitial should a timeout occur. Additionally, if need be, one could always unlock the form after a fixed amount of time. That way the user will be able to re-submit it again.

~

Dealing with double form submissions isn’t a new issue at all. You’ll find quite some results when running a few queries through Google β€” something I only found out after stumbling over the issue myself.

Back in 2015 (!) Mattias Geniar also pondered about this, after being pulled into the subject from a sysadmin view. Now, when even sysadmins are talking about an HTML/UX issue you know there’s something going on. This made me wonder why browsers behaved like that and how we could solve it:

As a result I decided to open an issue at the WHATWG HTML Standard repo, suggesting for a way to fix this at spec level:

An attribute on <form> to tweak this behavior – instead of having to rely on JavaScript – would come in handy and form a nice addition to the spec.

I see two options to go forward:

  1. Browsers/the standard keeps the current behavior and allow multiple submits. Developers must opt-in to prevent multiple submissions using a preventmultiplesubmits attribute.
  2. Browsers/the standard adjust the current behavior to only submit forms once. Developers must opt-in to allow multiple submissions using a allowmultiplesubmits attribute.

Initial response on the issue was very low, and it looks like this isn’t that big of a priority.

Back then I was more in favor of the second solution, but now I’m quite undecided as changing the default behavior β€” which has been around for ages β€” is quite tricky.

~

Another way that this issue could be fixed is at the browser level: if they were to treat double clicks on form submit buttons as single clicks, then the double click problem β€” but not the double submit problem β€” could also be taken care of.

To then attach styles to forms being submitted a CSS Pseudo Class :submitting would come in handy. Come to think of it, this Pseudo Class would be a quite nice addition to CSS in itself, no matter whether this double click issue gets solved or not.

~

Winging back to the addition to the HTML spec I suggested: If you do think it could be something the HTML spec could benefit from, feel free to add a thumbs up to the issue to indicate that you want this, or add an extra comment to it if you have more input on the subject.

Did this help you out? Like what you see?
Thank me with a coffee.

I don't do this for profit but a small one-time donation would surely put a smile on my face. Thanks!

β˜•οΈ Buy me a Coffee (€3)

To stay in the loop you can follow @bramus or follow @bramusblog on Twitter.

Enhancing User Experience With CSS Animations

Great talk by StΓ©phanie Walter (starting at 39:00):

With practical examples, I show why certain animations work better than others and how to find the best timing and duration to build UI animations that β€œfeel right”. I explain how our brain works, why and how animations contribute to improving user experience. And what you need to be careful about to build inclusive and accessible motion and avoid making some people sick.

Be sure to check the post on her website too, as it contains a full writeup, videos, examples, slides, links to extra resources, etc.

Enhancing User Experience With CSS Animations →

Web Vitals – Essential metrics for a healthy site

Web Vitals is a new great set of articles on Web.dev (by Google) focussing on delivering a great user experience on the web. To help developers focus on what matters most, they’ve selected a set of metrics which they call β€œCore Web Vitals”.

The metrics that make up Core Web Vitals will evolve over time. The current set for 2020 focuses on three aspects of the user experience β€” loading, interactivity, and visual stability β€” and includes the following metrics (and their respective thresholds):

  • Largest Contentful Paint (LCP): measures loading performance. To provide a good user experience, LCP should occur within 2.5 seconds of when the page first starts loading.
  • First Input Delay (FID): measures interactivity. To provide a good user experience, pages should have a FID of less than 100 milliseconds.
  • Cumulative Layout Shift (CLS): measures visual stability. To provide a good user experience, pages should maintain a CLS of less than 0.1.

The easiest way to quickly see these metrics is to use the Web Vitals Chrome Extension.

To get data from your visitors there’s the web-vitals library that you can use to store the data yourself (or in Google Analytics if you’re using that)

import {getCLS, getFID, getLCP} from 'web-vitals';

function sendToAnalytics(metric) {
  const body = JSON.stringify(metric);
  // Use `navigator.sendBeacon()` if available, falling back to `fetch()`.
  (navigator.sendBeacon && navigator.sendBeacon('/analytics', body)) ||
      fetch('/analytics', {body, method: 'POST', keepalive: true});
}

getCLS(sendToAnalytics);
getFID(sendToAnalytics);
getLCP(sendToAnalytics);

The best part of the whole Web Vitals series is that not only they tell you what the important metrics are, they also tell you what you can do to score better on each individual metric.

Once you’ve measured the Core Web Vitals and identified areas for improvement, the next step is to optimize. The following guides offer specific recommendations for how to optimize your pages for each of the Core Web Vitals:

Those three articles are packed with solid and actionable advice. Every web developer should read this. Twice.

Web Vitals →

Guidelines for text fields design

Text fields are probably one of the most used interface components; contact forms, payment details, account creation, lead generation. Users definitely need to interact with text fields when using your product. The purpose of this post is to highlight some of the key guidelines which I follow when it comes to designing better text fields.

It’s pretty basic things that, unfortunately, a lot of people seem to forget.

Guidelines for text fields design →

πŸ’΅ This linked article is stuck behind Medium’s metered paywall, which may prevent you from reading it. Open the link in an incognito window to bypass Medium’s ridiculous reading limit.

Smooth Scrolling Sticky ScrollSpy Navigation

Yesterday evening I was working on some documentation pages. The page layout is quite classic, as it consists of a content pane on the left and a sidebar navigation on the right. Looking for a way to make the page less dull I decided to add a few small things to it:

  1. Smooth Scrolling when clicking internal links
  2. A Sticky Navigation, so that the sidebar navigation always stays in view
  3. A β€œScrollSpy” to update the active state of the navigation

These three additions make the page more delightful, and best of all is: they’re really easy to implement! In this post I’ll lay out the details.

~

The result

First things first, here’s a recording of the end result so that you get an idea of what I’m talking about:

~

The Markup

The markup is really basic:

  • A main element surrounds our content div and nav.
  • Each piece of content is wrapped in a section which gets an id attribute. The sidebar navigation then links to its id
<main>
	<div>
		<h1>Smooth Scrolling Sticky ScrollSpy Navigation</h1>
		<section id="introduction">
			<h2>Introduction</h2>
			<p>…</p>
		</section>
		<section id="request-response">
			<h2>Request &amp; Response</h2>
			<p>…</p>
		</section>
		<section id="authentication">
			<h2>Authentication</h2>
			<p>…</p>
		</section>
		…
		<section id="filters">
			<h2>Filters</h2>
			<p>…</p>
		</section>
	</div>
	<nav class="section-nav">
		<ol>
			<li><a href="#introduction">Introduction</a></li>
			<li><a href="#request-response">Request &amp; Response</a></li>
			<li><a href="#authentication">Authentication</a></li>
		…
			<li class=""><a href="#filters">Filters</a></li>
		</ol>
	</nav>
</main>

Sprinkle some CSS on top to lay everything out – using CSS Grid here – and you have a fully working – albeit dull – page:

See the Pen
Smooth Scrolling Sticky ScrollSpy Navigation (base layer)
by Bramus (@bramus)
on CodePen.

~

1. Smooth Scrolling

Enabling smooth scrolling is really easy, it you can enable it using a single line of CSS:

html {
	scroll-behavior: smooth;
}

😱 Yes, that’s it!

In the demo embedded below, click any of the links in the nav and see how smooth it scrolls:

See the Pen
Smooth Scrolling Sticky ScrollSpy Navigation (base layer)
by Bramus (@bramus)
on CodePen.

For browsers that don’t support this you could add this JS fallback:

// Smooth scrolling for browsers that don't support CSS smooth scrolling
if (window.getComputedStyle(document.documentElement).scrollBehavior !== 'smooth') {
    document.querySelectorAll('a[href^="#"]').forEach(internalLink => {
        const targetElement = document.querySelector(internalLink.getAttribute('href'));
        if (targetElement) {
            internalLink.addEventListener('click', (e) => {
                targetElement.scrollIntoView({
                    behavior: 'smooth',
                });
                e.preventDefault();
            });
        }
    });
}

However, browser’s that don’t support scroll-behavior: smooth; also don’t support behavior: "smooth" for Element#scrollIntoView, so there’s not real advantage to adding this JS fallback.

~

2. Sticky Navigation

To make the navigation stay in place as you scroll we can rely on position: sticky;. As with Smooth Scrolling, this is a really simple CSS addition:

main > nav {
	position: sticky;
	top: 2rem;
	align-self: start;
}

πŸ’β€β™‚οΈ Since we’re using CSS Grid to lay out the children of <main>, adding align-self: start; to <nav> is an important one here. If we would omit it, the nav element would be as high as the enclosing main element. If that were the case, then nav would never be able to stick.

In the demo embedded below, click any of the links in the nav and see how the nav now also stays in view while the rest of the page scrolls:

See the Pen
Smooth Scrolling Sticky ScrollSpy Navigation (base layer + smooth scrolling + sticky nav)
by Bramus (@bramus)
on CodePen.

~

πŸ‘‹ Like what you see so far? Follow @bramusblog on Twitter or subscribe to the RSS feed to stay up-to-date.

~

3. ScrollSpy with IntersectionObserver

Thanks to the almighty IntersectionObserver we can implement a ScrollSpy. Basically we use it to watch all section["id"] elements. If they are intersecting, we add the .active class to any link that links to it. For styling purposes we don’t add .active to the link itself, but to its surrounding li element.

In code, that becomes this little snippet:

window.addEventListener('DOMContentLoaded', () => {

	const observer = new IntersectionObserver(entries => {
		entries.forEach(entry => {
			const id = entry.target.getAttribute('id');
			if (entry.intersectionRatio > 0) {
				document.querySelector(`nav li a[href="#${id}"]`).parentElement.classList.add('active');
			} else {
				document.querySelector(`nav li a[href="#${id}"]`).parentElement.classList.remove('active');
			}
		});
	});

	// Track all sections that have an `id` applied
	document.querySelectorAll('section[id]').forEach((section) => {
		observer.observe(section);
	});
	
});

πŸ’‘ To make the transition to and from .active not too abrupt, add a little blob of CSS to ease things:

.section-nav a {
    transition: all 100ms ease-in-out;
}

~

Complete Demo

Putting everything together, we end up with this:

See the Pen
Smooth Scrolling Sticky ScrollSpy Navigation
by Bramus (@bramus)
on CodePen.

Delightful, no? 😊

~

πŸ’‘ If you’re also looking for more inspiration to make your interfaces more delightful, be sure to check Hakim El Hattab’s β€œBuilding Better Interfaces” talk. Recommended stuff!

Did this help you out? Like what you see?
Thank me with a coffee.

I don't do this for profit but a small one-time donation would surely put a smile on my face. Thanks!

β˜•οΈ Buy me a Coffee (€3)

To stay in the loop you can follow @bramus or follow @bramusblog on Twitter.