Today Icelab Learned

Using the flexbox order property to rearrange elements

Flexbox introduces a whole bunch of neat layout tricks that we can start using today. Things that we once needed JavaScript (or duplicate markup) to achieve are now doable with plain old CSS.

One such property that I’ve found myself using repeatedly is order. We can assign a numeric value to a set of elements and the browser will reorder those elements on the screen — without manipulating the DOM.

Adjust the order property in the following example to see the technique in effect:

The flexbox order property mAVYGy

So where is this useful? There’s a bunch of scenarios you might like to utilise this property: one example is bringing a navigation <aside> to the top of the screen on a mobile optimised layout.

If you resize your browser to a phone-width and inspect the source of, you’ll see that the navigation bar in the footer exists below the contact detail elements in the DOM. But thanks to our friend order, we can completely rearrange the layout for this context. Neat!

Detect Google Analytics blocking

In a world filled with ad blockers, you can’t rely on Google Analytics dependencies being available when you want to use them. We hit upon this problem when doing some outbound link tracking like so:

function trackOutboundLink (e, url) {
  ga('send', 'event', 'outbound', 'click', url, {
    'transport': 'beacon',
    'hitCallback': function() {
      document.location = url

If Google Analytics is blocked, this function would fail to log the outbound click and worse, it’d also stop the link from being followed at all. We can adjust to handle this more gracefully however:

function trackOutboundLink (e, url) {
  if ( && {
    ga('send', 'event', 'outbound', 'click', url, {
      'transport': 'beacon',
      'hitCallback': function() {
        document.location = url;

By checking for both and before calling preventDefault we ensure that the links work when Google Analytics is blocked, and that outbound clicks are tracked whenever we can.

Improving accessibility with visually hidden labels

Accessibility is something that’s always in the front of our minds when building websites at Icelab. It’s particularly important for Government work which requires specific WCAG adherence.

A pattern we use often is to visually hide additional descriptive labels. These labels will be announced by screen readers but not rendered on the screen. Take this common snippet: href=""
      i.fa.fa-twitter href=""

With that we have a nice Twitter bird and envelope icon (courtesy of Font Awesome). Perfect!

…well, not so perfect really. We can make the experience much nicer for visually impaired users with a little extra effort: href=""
      span#twitter-label.hide-visually Follow Andy McCray on Twitter
      i.fa.fa-twitter aria-labelledby="twitter-label" href=""
      span#email-label.hide-visually Email Andy McCray
      i.fa.fa-envelope aria-labelledby="email-label"

We’re also using the aria-labelledby attribute to provide further clarification to screen readers.

And the CSS:

 * Hide only visually, but have it available for screen readers:

.hide-visually {
  border: 0;
  clip: rect(0 0 0 0);
  height: 1px;
  margin: -1px;
  overflow: hidden;
  padding: 0;
  position: absolute;
  width: 1px;

And with that our screen reader users will have a much friendlier experience getting in touch.

URI regular expressions

Let’s go ahead and write some regex to validate a URI in Ruby:

Good Grief


That’s it!

We can use it like so


If you have a look at the output of URI.regexp in irb, you’ll see the pattern that’s used to match against. It’s relatively complex, but each capture group is documented.

  1. Scheme
  2. Opaque (e.g. scheme:foo/bar)
  3. User Info
  4. Host
  5. Port
  6. Registry
  7. Path
  8. Query
  9. Fragment
irb(main):001:0> URI.regexp.match("")
=> #<MatchData "" 1:"http" 2:nil 3:"username:password" 4:"" 5:"80" 6:nil 7:"/baz.html" 8:"query=string" 9:"frag">

Creating and storing SSL certificates

As part of the SSL Certificate generation process you’ll need to create a Certificate Signing Request. You can do this with the command:

openssl req -nodes -newkey rsa:2048 -sha256 -keyout server.key -out server.csr

This will generate two files: a public .csr file, and a private .key file

You can view the files with the commands cat server.csr and cat server.key

Here at Icelab we recommend saving this information in a new 1Password secure note named with the following format:


Save the data from both files into the secure note and continue to be happy and confident in the face of adversity :) with a block works well as a keyed cache

We’re all familiar with the @foo ||= expensive_computation technique to memoize (i.e. cache) the output of slow computations or to avoid unnecessary object creation.

If you want to do the same but with results that will vary by a single parameter, you can use Ruby’s hash with its block-based initializer to handle the caching for you:

def my_cache
  @my_cache ||= do |hash, key|
    hash[key] = some_slow_computation(key)

# Computed once

# Then cached

You can see this work in practice inside dry-component’s Injector, where it caches objects to allow arbitrarily long chaining of injector strategies without creating duplicate injector objects.

Selecting colours in HTML

Somehow in the excitement around HTML5’s new input types I missed the addition of an input type specifically for selecting colours:

<input type="color">

Include this and you’ll get a platform-native colour selector that takes and returns hex colour values, like my debugging colour of choice #ff0099:

Selecting colours in HTML vGogBW

Note that the value needs to a fully hex string, not a shortcut like #f09. And, alas, if you’re viewing this in Safari you’ll notice that it isn’t supported at all.

Creating an IAM user for access to an S3 bucket

When you create an S3 bucket for a web app to use, you should create a specific IAM user to grant access to that bucket.

To do this, first create a user:

  1. Visit the IAM section of the AWS console
  2. Go to the “Users” section and click the “Create New Users” button
  3. Enter the user name(s) you want to create (e.g. my-bucket-s3 for a bucket named my-bucket)
  4. When you’re shown the user credentials, save them somewhere safe, like our team 1Password vault
  5. When you return to the IAM users list, go to the newly created user and click on their “Permissions” tab. Click on the “Inline Policies” section and create a new policy
  6. Choose to create a “Custom Policy”
  7. Name the policy “my-bucket-s3-access” (or something similarly descriptive) and paste the following into the “Policy Document” area (replacing my-bucket with your bucket’s name):

      "Statement": [
          "Effect": "Allow",
          "Action": "s3:ListAllMyBuckets",
          "Resource": "arn:aws:s3:::*"
          "Effect": "Allow",
          "Action": "s3:*",
          "Resource": [
  8. Click “Apply Policy”

The access policy should now be active and your app should be able to access the bucket with the new IAM user’s credentials.


ChromeVox is a screen reader extension for Chrome.

I found it a helpful extension when addressing accessibility issues on a recent project. I was able to replicate the voice prompts noted in the accessibility audit. Thus, fixing them was much easier!

One thing to note is that you’ll want to change the default voice. The default just seems to cackle at you. You can do this via Settings > Extensions > ChromeVox > Options.

Using CloudFront as ‘origin pull’ (with bonus gzip)

CloudFront can act as a CDN that ‘pulls’ content from an origin server. It’s really easy to set up, you simply set the ‘Origin Domain Name’ to wherever you’re hosting your app: for example and then … nothing. That’s all you need to do. Requests that come through CloudFront will pass through to your origin server and then be cached thereafter.

For bonus points it’ll also gzip your content for you without you having to do much work at all. Set ‘Compress Objects Automatically’ to ‘Yes’ and you’re done. If CloudFront can serve a gzipped version (and the client can receive one) it’ll serve up a compressed version of your file instead.