Blog


Apr

How to annoy all moose a little less (by building accessible websites)


A moose putting its tongue out

I have a close relationship to moose. Of all the good things this has brought me in life, one that really stands out is that it gave me my current job as a designer here at Elabs. This makes me thankful to moose, and I want to give something back.

As a designer who writes code, I make choices daily that impact how well a moose can percieve and navigate our web applications. A lot of the time, I make it very hard for them. I could do better.

I'm by no means an expert on moose accessibility, but recently I've been eating the brains of smarter people to gain their knowledge. I would like to share a few tips on how to make life a tiny bit easier for moose that surf the web.

As a coincidental bonus, these tips should improve accessibility even more for humans (and their different function variations).

I'll focus on some very basic things that many of us still get wrong, but is easy to get right.

Remember the semantics

A link is not a button. An <a> with href should link to another resource, while a <button> performs an action in the current view (such as submitting a form or opening a modal window). Buttons come with built in behavior that links don't have, like being activated by pressing space. Want something more discreet than a button for a minor action? Style it like a link.

Mind your headings. Be carfeul when you're building your heading structure, to make sure you're not skipping levels. Headers are the backbone of the document and a common way of navigating it with a screen reader. Don't pick heading level based on style or font size. If you want the look of a <h3> where a <h2> should be, try styling it with <h2 class="h3"> or similar. Also, Harry Roberts argues that your logo is an image, not a <h1>.

Placeholders are not labels. We hear this all the time, but still get this wrong sometimes. Many site search inputs still use placeholder text as their label (or nothing at all, and solely relies on the "Search" button to convey its function). If you need a compact search form, hide the label in a way that doesn't hide it from screen readers, like this .visually-hidden class does.

Is it content? Serve it to everyone.

Provide text alternatives. Until Google finishes their automatic image captioning algorithms, we're stuck with writing alt texts ourselves, whenever we use an image that qualifies as content (as opposed to styling). It's especially important when the image is a link, since screen readers will read out the link URL as title if no alt is present for the image.

Links should be self explanatory. Using "Click here" or "Read more" as link text makes the destination a mystery if you're navigating a page by jumping through its links, which is often what screen reader users do. Ambiguous links is the third most problematic item on the web (after CAPTCHAs and Flash) according to a WebAIM Screen Reader User Survey.

Color alone can't be trusted. Don't rely on just color to convey meaning, make sure there are labels or other visual clues to complement. Keep it in mind when styling states of interactive elements, such as :focus states of inputs or :hover states of links. Around 8% of human males and 100% of female and male moose have a color vision deficiency. What if it was your moose?

Apples of different colors to illustrate color vision deficiency types

Types of color vision deficiency (dichromacy)

Stay legible

Zooming should not break anything. Being able to zoom a page is important for individuals with poor vision (like... every moose), or just anyone tired of tiny text. Make sure your site doesn't break when zooming. Use relative units for font sizes and margins/paddings. Font sizes set in px zooms well in many browsers today, but not at all in IE (not even in IE 11 – it's by design). Remember there's a difference between zooming an entire page, with images and everything, and increasing the browser's font size. The latter is common if you're finding the web site's default font size hard to read.

Check your contrast. It's not just for elderly people. Sometimes we forget about low quality monitors, sun glare, tiny handheld screens, or a combination of them. Use Lea Verou's contrast ratio tool, the Sketch color contrast plugin, or ask your friendly neighborhood moose to have a look.

Key value

Some people and most cats hate mice. Try to make all parts of your website keyboard accessible. Tab through it and make sure there is a :focus state for every :hover state. Be extra careful with any custom widgets, especially modal windows.

Keyboard with apples as keys

Äpple Bluetooth Keyboard (the number one input device among moose)

Going further

These tips were absolute basics, they should be a part of every project. Still I cheat and I get lazy, and I know I'm not alone. These things are not that hard, they're just tweaks to your workflow.

Once you've managed to annoy moose and people a little less, go the next mile and learn how to do some real difference:

Mar

Handle secret credentials in Ruby On Rails


This blog post aims to lay out a simple and concrete strategy for handling sensitive data in your Ruby On Rails applications, and to explain the importance of such a strategy.

Never, ever check them into source control

Even if your project is closed source and your trusted colleagues are the only ones with access, you never know when a freelancer or consultant might be joining the project. Even if that never occurs, how do you keep track of all the locations where that repository is checked out? Who knows on how many hard drives your company's credit card transaction secret API key might be stored. What happens when someone with a weak login password forgets their laptop on the bus or at the airport?

Also note that it's not always as simple as removing secrets after the fact, especially with version control. It's usually impossible to do this without drastically changing your entire project's history!

Do it right

For a long time, we've been using YAML files to store our application configuration. It's easy to manage and can be configured for different Rails environments. These YAML files could look like the following:

config/app.yml:

development: &defaults
  awesomeness_score: 3
  host: "localhost:3000"
  s3_bucket: "example-development-us"

production:
  <<: *defaults
  host: "example.com"
  s3_bucket: "example-production-us"

test:
  <<: *defaults

config/app_secret.yml.example:

  development: &defaults
  aws_access_key_id: ""
  aws_secret_access_key_id: ""

production:
  <<: *defaults

test:
  <<: *defaults

config/app_secret.yml:

development: &defaults
  aws_access_key_id: "ACTUAL-ID-WOULD-GO-HERE"
  aws_secret_access_key_id: "ACTUAL-SECRET-WOULD-GO-HERE"

production:
  <<: *defaults

test:
  <<: *defaults

Only the first two files would be checked in to source control, and the application's README would instruct developers to cp config/app_secret.yml.example config/app_secret.yml and fill in the gaps from the company keychain.

To make sure we never check in the secrets by mistake, we ignore the app_secret.yml file:

.gitignore:

# ...
/config/app_secret.yml

We then use the econfig gem written by Jonas Nicklas to easily merge them together:

Gemfile

# ...
gem "econfig", require: "econfig/rails"

config/application.rb

# ...
module YourApp
  extend Econfig::Shortcut
  # ...
end

Now we can access any configuration variable and secret credential:

YourApp.host # => "localhost:3000"
YourApp.aws_secret_access_key_id # => "ACTUAL-SECRET-WOULD-GO-HERE"

Deploy

When you deploy the application, you must manually manage the secrets on the server(s).

Capistrano

If you deploy with Capistrano, you'll want to place the app_secret.yml in your /shared folder. Once that's done, it can be copied to each release with symlink task:

deploy.rb

# ...
namespace :config do
  desc "Symlink application config files."
  task :symlink do
    run "ln -s {#{shared_path},#{release_path}}/config/app_secret.yml"  
  end
end

after "deploy", "config:symlink"

Heroku

If you're deploying your application where you don't have file access, such as Heroku, you're better off storing this kind of information in ENV. The econfig gem has built in support for this and a few other storage backends, but that's another blog post.

Conclusion

With this method, we now have a clear separation of sensitive and non-sensitive data. There's no risk of checking in any sensitive data, since we have only one place to put it all and it's hidden from source control. Data access within the application hasn't changed, and we no longer have to concern ourselves with how sensitive it is.

We can now be sure that giving access to a repository does not imply giving access to other systems.

Epilogue

If you have any feedback on how the blog post can be improved, or if you spot any errors, please let me know by posting a comment below!

Mar

Working with time zones in Ruby on Rails


Rails provides great tools for working with time zones but there's still a lot of things that can go wrong. This blog post aims to shed some light on these gotchas and provide solutions to the most common problems.

The one that probably has tricked me the most times is the fact that Rails fools you to believe it got you all covered all the time (pardon the pun). Don't get me wrong. I want Rails to do as much work for me as possible. But I've learnt the hard way that I can't get away with not knowing when and how Rails is helping me. Another gotcha is the fact that you have more time zones in play than you might first believe. Consider the following: db, server, dev machine, system configured, user specific configured and the browser.

Configure your Rails app

So what tools do we have at our disposal as Rails developers? The most important one is the config.time_zone configuration in your config/application.rb file. ActiveRecord will help you convert from and to (which the documentation fails to explain) UTC and the time zone of your choice. This means that if all you're doing is having users post times through a form and use Active Record to persist it you're good to go.

Processing time information

So what about actually doing something with the time information before persisting it? That's when it becomes tricky.

Parsing

When parsing time information it's important to never do it without specifying the time zone. The best way to do this is to use Time.zone.parse (which will use the time zone specified in config.time_zone) instead of just Time.parse (which will use the computer's time zone).

Work with Numerical and ActiveRecord attributes

Method calls like 2.hours.ago uses the time zone you've configured, so use these if you can! The same thing is true for time attributes on ActiveRecord models.

post = Post.first
post.published_at #=> Thu, 22 Mar 2012 00:00:00 CDT -05:00

ActiveRecord fetches the UTC time from the database and converts it to the time zone in config.time_zone for you.

Date vs Time

Time has date information but Date does NOT have time information. Even if you don't think you care you might realize that you do sooner then later. Be safe and use Time (or DateTime if you need support for times very far from the present).

But let's say you're stuck with a Date that you need to treat as a Time, at least make sure to convert it to your configured time zone:

1.day.from_now # => Fri, 03 Mar 2012 22:04:47 JST +09:00
Date.current.in_time_zone # => Fri, 02 Mar 2012 00:00:00 JST +09:00

Never use:

Date.today.to_time # => 2012-03-02 00:00:00 +0100

Querying

Since Rails know that your time information is stored as UTC in the database it will convert any time you give it to UTC.

Post.where(["posts.published_at > ?", Time.current])

Just be sure to never construct the query string by hand and always use Time.current as the base and you should be safe.

Working with APIs

Supplying

Building a web API for others to consume? Make sure to always send all time data as UTC (and specify that this is the case).

Time.current.utc.iso8601 #=> "2012-03-16T14:55:33Z"

Read more about why iso8601 is advisable here: http://devblog.avdi.org/2009/10/25/iso8601-dates-in-ruby/

Consuming

When you get the time information from an external API which you don't have control over you simply need to figure out the format and time zone it's sent to you with. Because Time.zone.parse might not work with the format you receive you might need to use:

Time.strptime(time_string, "%Y-%m-%dT%H:%M:%S%z").in_time_zone

This assumes time_string a iso8601 formated string. strptime will throw a very unintuitive error complaining on the format argument when in reality the problem is that the time string's format mismatches the format template argument. in_time_zone defaults to use the Rails configured time zone.

Why there's no strptime method on Time.zone when there's a parse beats me.

Working with multiple user time zones

Many systems needs to support users entering and viewing time information in a variety of time zones. To achieve this you need to store each user's time zone (probably just one of the time zone string names found in rake time:zones:all). Then to actually use that time zone the most common pattern is to simply create a private method in your ActionController and run it as an around filter.

around_filter :user_time_zone, :if => :current_user

def user_time_zone(&block)
  Time.use_zone(current_user.time_zone, &block)
end

This will do the same thing as config.time_zone but on a per request basis. I still recommend to change the default config.time_zone to a time zone that is a good default for your users. (Thank you Matt Bridges for pointing out the potential problems with using a before_filter instead of an around_filter.)

Testing

All the above is something that your tests should catch for you. The problem is that you as the user and your computer as the development server happen to reside in the same time zone. This is rarely the case once you push things to production.

There is Zonebie, a gem that helps you deal with this. I haven't had time to try it out myself yet, but it looks promising. If you find this to be overkill, at least make sure that your tests run with Time.zone set to another time zone than the one your development machine is in!

Cheat Sheet

DOs

2.hours.ago # => Thu, 27 Aug 2015 14:39:36 AFT +04:30
1.day.from_now # => Fri, 28 Aug 2015 16:39:36 AFT +04:30
Time.zone.parse("2015-08-27T12:09:36Z") # => Thu, 27 Aug 2015 16:39:36 AFT +04:30
Time.current # => Thu, 27 Aug 2015 16:39:36 AFT +04:30
Time.current.utc.iso8601 # When supliyng an API ("2015-08-27T12:09:36Z")
Time.strptime("2015-08-27T12:09:36Z", "%Y-%m-%dT%H:%M:%S%z").in_time_zone # If you can't use Time.zone.parse (Thu, 27 Aug 2015 16:39:36 AFT +04:30)
Date.current # If you really can't have a Time or DateTime for some reason (Thu, 27 Aug 2015)
Date.current.in_time_zone # If you have a date and want to make the best out of it (Thu, 27 Aug 2015 00:00:00 AFT +04:30)

DON'Ts

Time.now # Returns system time and ignores your configured time zone. (2015-08-27 14:09:36 +0200)
Time.parse("2015-08-27T12:09:36Z") # Will assume time string given is in the system's time zone. (2015-08-27 12:09:36 UTC)
Time.strptime("2015-08-27T12:09:36Z", "%Y-%m-%dT%H:%M:%S%z") # Same problem as with Time.parse. (2015-08-27 12:09:36 UTC)
Date.today # This could be yesterday or tomorrow depending on the machine's time zone, see https://github.com/ramhoj/time-zone-article/issues/1 for more info. (Thu, 27 Aug 2015)

Epilogue

I hope you've learned something from this post. I sure did while writing it! If you have any feedback on how it can be improved, or if you spot any errors, please let me know by posting a comment below!

Ruby and Rails version

This article was first written in March 2012. Back then Rails 3.2 was the new hot and as you all know a lot happens in Rails-land in two and a half years and will continue to do so. I will do my best to keep the article accurate and up to date with the latest versions of Rails. If you spot anything that is reported deprecated or not working please let me know in the comment section below!

  • Article publish date: 2012-03-20
  • Article last updated: 2015-08-27
  • Last verified Rails version: 4.2.4
  • Last verified Ruby version: 2.2.0p0 (49005)
  • OS: Mac OS X 10.10.4 (Yosemite)

There is a git repository which you can clone:

git clone git@github.com:ramhoj/time-zone-article.git
cd time-zone-article
bundle install
rake db:create:all db:migrate db:test:prepare
rspec spec/

The Rails application is running on the version defined above and has been verified to work under the described Ruby version above. If you want to make sure things are working in the version of Rails or Ruby that you're using please fork the repository and make the necessary adjustments and run the test suite. If you want more in-debt, hands-on of the examples this repository's test suite aims to help with this too.

Changelog

See the git repository's commits.

Dec

Scopes Are Obsolete


I admit, I've never been a big fan of named_scope or just scope as it's been renamed in Rails 3. When it was first introduced I remember not being particularly impressed, as a Merb acolyte we'd had this chaining functionality in Datamapper for ages, only it was much better. In fact in Datamapper, every query you could construct was chainable. Thankfully in Rails 3 and ActiveRecord 3, queries have finally grown up so that everything is now chainable in ActiveRecord too:

class Person < ActiveRecord::Base
  def self.alphabetically
    order(:first_name, :last_name)
  end

  def self.active
    where(:archived_at => nil)
  end
end

Person.active # => [...]
Person.alphabetically.active # => [...]

In this case it seems like the scope method will give us much nicer more concise syntax:

class Person < ActiveRecord::Base
  scope :alphabetically, order(:first_name, :last_name)
  scope :active, where(:archived_at => nil)
end

But imagine if we want to add a new kind of scope to find user's with a given last name:

scope :by_last_name, lambda { |name| where(:last_name => name) }

It's getting a bit less nice, aside from the gratuitous lambda, it's still pretty okay though.

What I believe is wrong with this code though is that it is essentially recreating Ruby functionality. We're defining a method called by_last_name which will execute some code when called, only we're doing it through meta-programming for essentially no reason at all. The above could have been written as:

def self.by_last_name(name); where(:last_name => name); end

And it would have worked exactly the same. The only difference that I can tell is that scope allows you to define extension methods by passing a block, which I'm sure no one has ever used, since it's so completely useless.

The problem becomes even more striking when the code is even the slightest bit complicated.

scope :for_user, lambda { |user|
  if user.admin?
    where(:active => true)
  else
    where(:active => true, :user_id => user.id)
  end
}

That's just horrible.

def self.for_user(user)
  if user.admin?
    where(:active => true)
  else
    where(:active => true, :user_id => user.id)
  end
end

This looks much more like Ruby code and less like some kind of weird JavaScript concoction.

Stop replicating functionality that already exists, stop using scope. It is obsolete.