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.
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:
Fixate the header using position: sticky; instead of position: fixed;. No need for that extra margin on the content then. Update: See note below
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);
}
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.
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:
Disabling a form submit button while submitting: yay or nay?
(I thought I saw some research that discourages it, but can't remember where or why)
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:
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:
π€ Why is it that browsers don't prevent double form submissions by default? Some users (mistakingly) double click on submit buttons.
π‘ An attribute on <form> to tweak this behavior β instead of having to rely on JavaScript β would come in handy β¦
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:
Browsers/the standard keeps the current behavior and allow multiple submits. Developers must opt-in to prevent multiple submissions using a preventmultiplesubmits attribute.
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!
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.
George Cave takes a look at the UX of the LEGO Interface Panels
These iconic, low-resolution designs are the perfect tool to learn the basics of physical interface design. Armed with 52 different bricks, letβs see what they can teach us about the design, layout and organisation of complex interfaces.
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.
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:
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.
π΅ 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.
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:
Smooth Scrolling when clicking internal links
A Sticky Navigation, so that the sidebar navigation always stays in view
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.
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:
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;
}