Linked radio buttons with CSS

Screenshot of styled radio buttons
Regular form elements, heavily styled.

Demo: codepen.io/shellbryson/pen/KWKyXN

It’s a pattern we see fairly often across the web: a series of ‘buttons’ connected by lines to indicate procession, ratings or steps. The trick is to make it as flexible and accessible as possible, and this is perfectly achievable without JavaScript.

Using regular form elements as our base, we can use a little extra markup and some trickery with CSS ::before and ::after to create attractive (or… excessively pink) results without sacrificing the underlying accessibility.

The HTML

So we start with a regular ordered list:

<ol class="nodes">
 <li class="node">
   <input class="radio" type="radio" id="r1" name="radio-set">
   <label class="label" for="r1">1</label>
 </li>
 <li> ... </li>
</ol>

We’re using accessible markup, making sure we have a label and that the label is referencing the field (via for=). Any interaction with the label will be passed onto the field itself, which is key as we’re going to play with the label to give us our fancy inputs.

The CSS

We take the label, and use ::before and ::after to position circles on top of the real form input to mask it, and getting our desired look. As these pseudo-elements are children of the <label>, any interaction with this label is passed through to the form field below. We simply layer things up:

 

 

 

.label {
 ...

 &::before {
  display: block;
  position: absolute;
  left: 0; right: 0; bottom: 0; top: 0; // trick to fill element
  margin: auto;
  z-index: 1; // ensures this is displayed above form field
  width: 60px;
  height: 60px;
  content: " ";
  border-radius: 50%;
  background-color: pink;
 }
 
 &::after {
  ...
 }
}

The final step is ensuring that the :checked state of the radio button is reflected by our fancy styling:

 .radio:checked + .label::after {
   background-color: white;
 }

As we want our list to adapt to the space available in the browser, we can use css flex with justification property to ensure each li is evenly spaced:

.node {
 display: flex;
 justify-content: space-between;
}

By adding an additional  ::after on the .node itself, we can join the nodes together. Note, we’re deliberately offsetting the start of the pseudo-element so that the line stretches from the centre of the element to the right edge of the <li>.

.node::after {
  display: block;
  position: absolute;
  left: 60%; right: 0; bottom: 0; top: 50%;
  content: " ";
  height: 6px; width: 100%;
  background-color: lighten($color-base, 20%);
 }

Finally, you’ll need to remove the line from the last child as we do not want a line going off into nowhere:

.node:last-child {
  &::after {
   display: none;
  }
}

Interaction and animation

By adding transforms and transition delays we can achieve all kinds of over-the-top effects. Advice: don’t over use this, remember the UX.

Go have a play (CodePen)

Dev notes

We’re making heavy use of display:flex and positioned elements here, and while it all remains flexible across most modern browsers, flex can be rather buggy. You could probably achieve much of this using CSS display:table, or if you were daring, regular float.

Magic social icons with CSS

Screenshot of a social icon selected with CSS
Social icons selected via CSS

This is a simple trick for automatically displaying icons (such as social icons) without any script. Using a CSS wildcard selector, we check the href of our link and display a pseudo-element with matching content.

a[href*="github.com"]::before {
 content: "\f113";
}

The above example uses a Font-Awesome glyph as content, but this could easily be replace with an image: content: url(/path/image.svg). Any subsequent link matching our selector would display our pseudo-element.

A demo of this technique can be found here on CodePen.

Dev notes

Be careful with your selectors. You could end up accidentally styling links that have been entered into a user-editable field (such as a CMS)… unless that’s what you really want. Wildcard selectors require the browser to do extra work, so you should use sparingly.

Playing with SCSS loops

Screenshot of tiles
Tiles using flex, loop and one preset colour.

SCSS offers a few control directives that enable us to perform some rather interesting effects.

In this example, I use a simple for loop to change a single colour value and apply that to a series of 25 <div> elements.

 

A basic loop is straight forward:

 @for $i from 1 through 25 {
   
 }

So, if we have a base colour $color-green: #00ff00 we can simply multiply the current iteration $i by an arbitrary value, and perform an SCSS filter (such as darken, lighten) on that value. Bingo:

$color-green: #00ff00;
$increment: 1.5;

.my-class {
  @for $i from 1 through 25 {
    &:nth-child( #{$i} ) {
      $col: $i * $increment;
      background-color: darken($color-green, $col);
    }
  }
}

This will loop through 25 elements that have the class my-class and apply an increasingly dark background colour to each.

An little expermiment featuring this can be found over here on CodePen.

Dev Notes

I’m using flex and vw/vh units to give us a perfectly tiled full-page result. This may not work in all browsers. I wrote a little about viewport units in another post earlier.

Battery Status API experiment

Screenshot of JavaScript battery API experiment
Battery UI element

The JavaScript Battery Status API offers some interesting additional functionality that we can leverage for UI elements. Browser support is getting better—at the time of writing (Feb, 2017), Chrome, FireFox and Opera are good to go, but leaves glaring omissions of Safari/iOS.

This little experiment creates a simple battery UI element, with its background an indicator of battery percentage remaining (plus percentage text): CodePen Experiment

The API provides a bunch of events we can hook into:

  • chargingchange – is the battery charging?
  • levelchangepercentage of battery remaining
  • chargingtimechangeamount of time until battery is charged (estimate)
  • dischargingtimechangeamount of time until batter is discharged (estimate)

All we need to do is check that we have support for the API and off we go:

navigator.getBattery().then(function(battery) { ... });

Once we know we have battery API support, we can go ahead and add some listeners for the new events and update our UI as we need. Note, we don’t use any libraries here, this is raw JavaScript:

const $batteryPercentage = document.querySelector(".js-guage");
const $batteryPercentageDisplay = document.querySelector(".js-percentDisplay");
const $batteryStatusDisplay = document.querySelector(".js-statusDisplay");

navigator.getBattery().then(function(battery) {
 
 function initBattery(){
   updateChargeInfo();
   updateLevelInfo();
 }
 
 battery.addEventListener('chargingchange', function(){
   updateChargeInfo();
 });
 
 battery.addEventListener('levelchange', function(){
   updateLevelInfo();
 });
 
 function updateChargeInfo(){
   let _batteryChargeStatus = battery.charging ? "Battery Charging" : "Battery Draining"; 
   $batteryStatusDisplay.innerHTML = _batteryChargeStatus;
 }

 function updateLevelInfo(){
   let _batteryLevel = Math.round(battery.level * 100) + "%";
   $batteryPercentage.style.width = _batteryLevel;
   $batteryPercentageDisplay.innerHTML = _batteryLevel;
 }
 
 initBattery();

});

What could we use the Battery Status API for?

  • Intensive or full-screen scripts could warn the user if the battery was low before beginning, such as Canvas, WebGL or media handling.
  • Coupled with up-coming CSS selector prefers-reduced-motion we could trigger low-animation CSS when the battery is getting tight.

Details of support for the Battery Status API can be found on Can I Use.

Responsive navigation UX experiment

Screenshot of mobile menu opened (links to demo CodePen)Mobile navigation frequently consists of a burger menu placed in the top right of the viewport. But consider this: a user with a large phone using just one hand, can their thumb stretch that far? This is particular issue on the larger format phones such as an iPhone 7 Plus.

Some of the UX niggles in mobile navigation:

  • Awkward one handed navigation
  • Fiddly menu and links
  • Inappropriate menu items for context of device
  • Little room for styled menus or call to actions

The following experiment explores an alternative to the hamburger menu, with a focus on improving the UX. I’m using a tiny bit of JavaScript to handle the mobile navigation toggle, but the layout is entirely through CSS.

The experiment

  1. Move the navigation to the bottom. By placing the menu toggle where it is easily reached in one-handed use. It also keeps the navigation controls for your site close to the control UI provided by most mobile browsers (bottom of the web view).
Screenshot of mobile menu
Mobile, menu located at viewport bottom

We reposition the menu with CSS, meaning the underlying HTML document remains logically (seminally) ordered for screen readers and document parsers.

Screenshot of desktop menu
The same menu, but on a desktop

Ultimately it’ll be worth considering an additional breakpoint for portrait tablets—one that follows a more traditional burger pattern. Why? Tablets are generally not held in one hand, but the screen remains practically too small for the full extent of a desktop-ish navigation. A combination of burger + shortened desktop menu could work here, but that would require a JavaScript solution based on viewport width (which is less attractive).

  1. Leave the brand where it is (top). We don’t want to waste our precious mobile screenspace, so we avoid making it “sticky” or “fixed” or anything else that obscures the content. In this example the brand is fairly large, but we should try to keep it small and out of the way. On visiting a site on a mobile, I want to know what that site is about quickly and without scrolling, so lets promote the content and not waste screen space with branding.
  1. Screenshot of expanded menu
    Mobile, menu expanded

    Tiles, not links. Links can be hard to tap on a mobile. We can significantly increase the hitbox for each menu entry by making them tiles. This also provides a lot of opportunity for branding or colourful styling—but not at the expense of a clear, readable label.

  1. You don’t have to show everything. I’ve noticed many mobile menus try to show all the things, which may not actually be that useful for different devices. Why not hide the menus we don’t want? In this experiment, we can tag menu elements with menu-item--hide-desktop class to hide (natch) content from the desktop. We could equally do that for mobile.
  1. Don’t be afraid to repeat links, sometimes it’s fine. Here, I’ve taken what I think are the three most important menu entries and explicitly called them out as additional ’tiles’ to display along side the mobile navigation button. Quick access for the user, without distraction.

What’s next

This experiment could be taken much further if we start introducing more JavaScript. I’ve also barely touched on accessibility, but this experiment is ‘fairly accessible’. There are additional tricks that I’ll address in some future post. Best tip, fork the demo on CodePen and have a play.

View this experiment on CodePen.

Dev notes

Encountered a strange issue with Safari while building this one. An absolutely positions display:fixed element (the mobile nav, viewport bottom) was not being rendered at all. Chrome and FireFox were displaying the menu perfectly. This was due to Safari ignoring width: 100% on the menu wrapper. Replacing width with left: 0; right: 0 resolved the issue.

This experiment is reliant on flexbox (display: flex) support in your browser. These days that covers most browsers, but if you do need to support older browsers, consider adding a fallback to CSS tables.

Visible password toggle with CSS and JS

Password fields usually mask their content, but is that really good UX?

UX challenges of password fields

  • User ‘loses their place’ forcing them to start entering the password again.
  • Password hard to type or complex, no visual guide to text being entered.
  • Difficulty in paste a password from elsewhere.
  • Difficult to visually check validity of password before submitting.
  • Hard to copy password elsewhere before submitting.

Technical solution

An increasingly common pattern is to allow the user to toggle the visibility of the password field content, which has a number of accessibility benefits.

Screenshot of form with regular password field
Regular password field.

Following progressive enhancement we can do this simply in JavaScript, however there are a few things to consider:

 

  1. Many users have password manager add-ons—such as LastPass—which may adapt the display of password fields, so we have to be careful when it comes to styling the field itself.
  2. Changing the field <input type="password" ...> to text in JavaScript is simple enough, but it may confuse password managers (even those built into a browser) and irritate users (because their password manager is now broken…).
  3. There’s a possibility that the browser could cache or save the state of the field in plain text, which would be a Bad Thing.
Screenshot of password field with its content being displayed.
Password field with content revealed.

Taking point 2, we can be a little smarter about how we handle form submits and ensure we only ever submit password fields, regardless of a toggled display state. This prevents point 3.

By placing the toggle outside of the password field, we can avoid most visual clashes with add-ons or password managers, covering point 1.

The are downsides to this solution: The user has to have JavaScript enable for start, which you cannot guarantee (but thanks to progressive enhancement, the form will still work). Secondly, it’s still possible for password managers to do strange things to the form fields which cannot be accounted for.

A CodePen demonstrating this technique can be found here.

Responsive modal dialogs

Screenshot of modal dialog masking content
Modal window with full-viewport mask

A super simple responsive modal window with minimal JavaScript.

  • Mobile friendly (mobile first, natch!), fills the viewport on mobiles
  • Keyboard navigable
  • Marked up for accessibility
  • Masks the content on desktop

Particular attention here on the accessible aspects of modal markup: aria-labeledby, aria-describedby and the toggling of aria-hidden on show/hide.

Basic modal markup with these aria properties:

<div class="modal"
 data-modal="dialog1"
 role="dialog" 
 aria-labelledby="modal-label"
 aria-describedby="modal-description"
 aria-hidden="false">

Then matching elements inside the modal itself:

<h2 id="modal-label">
 My modal title
</h2>
...
<p id="modal-description">Modal description</p>
Screenshot of modal dialog filling viewport
Fills viewport on smaller devices

A demo can be found on CodePen.

Using flex to change element display order

Here’s a dead simple trick for creating alternating boxes (flipping the order of element display) in CSS with display: flex. Each pair of boxes is wrapped in a div with flex applied. We can then select every odd wrapper and change the order of the content.

.inner:nth-child(odd) {
 .box--box1 {
   order: 1;
 }
}
Screenshot of boxes before ordering
Before order
Screenshot of alternating boxes
After ordering

See the it in action at CodePen.

Scaling content with CSS vw units

Screenshot of desktop with content scaled
Desktop with scaled content

While experimenting with a new Pattern Library based workflow and I put together something to play with scaling content based on css vw units (viewport width) and full-page splashy design.

All it takes is…

font-size: 10vw;

…and the browser will calculate the size on the fly. There are 4 related properties, vh, vw, vmin, and vmax which can be used to scale most content base don the dimensions of the browser viewport. These units are percentage of viewport size, so 10vw == 10% of the viewport width.

  • vh = % of viewport height
  • vw = % of viewport width
  • vmin= whichever of vh or vw has the smaller size*
  • vmax= whichever of vh or vw has the larger size*

* As of Jan 2017, Microsoft Edge browser doesn’t support vmax, but you are safe to use vh/vw in all modern browsers

You can see the page in action here: spacegirl.co.uk or check out the source on GitHub here: spacegirl-patternlibrary.

Menu positioning

Screenshot of mobile first design
Mobile menu

This bare bones experiment also deliberately puts the navigation elements at the bottom of the page on mobile—in theory closer to the users thumb. This was sparked from a discussion at the studio this week over mobile menus. The ubiquitous hamburger menu feels increasingly unwieldy UX as we become more attached to our mobile devices, especially phones. The thumb reach from the bottom of an iPhone 7 Plus to the top right corner (traditionally where the burger menu resides) is a stretch for me. A struggle for folks with smaller hands, which could results in phone-fumble annoyance. There are solutions—more app-like behaviour (think iOS apps, icon bars), tile menus, InstaGram style slide-in/out menus to name a few—but there’s the risk of user confusion. People are used to burger menus, even if they don’t have the most elegant UX. Food for thought (hmm, burgers).

 

Mobile-first responsive tables with CSS

Here’s a rehash of an experiment I did a while back with responsive tables. The goal is to make style a table ‘truly’ mobile first, while maintaining semantic HTML.

Update

The initial demo was using min-width to handle the breakpoint which resulted in overly complicated CSS. This second demo reworks the CSS to use a max-width based breakpoint instead. It allows the table to naturally return to a table-like layout after the breakpoint is hit.

Screenshot of mobile first table
Mobile first (max-width)
Screenshot of desktop table (using max-width)
Desktop

Updated demo (CodePen): codepen.io/shellbryson/pen/yggOME

Git: github.com/shellbryson/codetips-responsive-tables

Original demo

Starting with mobile, we unwrap the HTML, turning each of the table elements into blocks that we can easily position.

We use the a data-attribute on <th> cells in the table body as hidden content for smaller screens (the default view as we’re mobile-first).

Screenshot of mobile-first table
Mobile first

On desktop, we use a breakpoint to give the table HTML elements back their proper display property values and hide our hidden content (data-attribute) titles. Our friends here are:

display: table-header-group;
display: table-row-group
display: table-row;
display: table-cell;
Screenshot of desktop table
Desktop table

A CodePen can be found here (resize your browser to see the responsiveness).