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:

Dec

Has Microsoft become altruistic or is cooperation simply better than competition?


The market economy prides itself on its competitive nature. Through competition we’re promised better and cheaper products and services. I believe this to be true in countless cases. I also believe that we need to be better at considering both the benefits and the inefficiencies that the competitive model brings. As with any dogma it can do just as much harm as good unless constantly being evaluated from a case-to-case perspective.

Retrieved from http://www.teslamotors.com/models/photo-gallery

Consider for example what companies do to protect their investments in research and development. Companies are constantly suing each other over patents and spending billions on lobbying to make IP-laws stricter and more far-reaching. We buy this concept because we believe competition is more effective than cooperation. It’s been this way for so long that it’s hard to even imagine the alternative. Who knows what kind of cars we would have if all the car manufactures cooperated in their RnD. Maybe we would all have been driving the Tesla Model S ten years ago, maybe we would still be stuck in a Trabant, who knows?

Given this blog post being on a software development firm’s blog you’ve probably already figured out where this is going. Open Source is a large-scale, unconditional, more or less all-in choice of cooperation before competition, and it works! 59% of all web sites run on open source web servers. 95% of smart phones run on open source technology. 82% of users access the Internet using a web browser built on an open source foundation. With Microsoft’s recent release of the .NET framework as open source pretty much all major programming environments are now open source.

Retrieved from http://www.flickr.com/photos/gforsythe/8174197748/ Illustration by Giulia Forsythe

In my experience open source projects tend to be numerous and diverse, allowing the user to choose a project that suits their needs. Often times they have large and helpful communities, answering questions in time zones all around the globe. Open source products are often free, and thus available to more users. Simply giving more people access to others’ creations can spawn innovation that would be lost in a closed environment.

As a software consultant I know that trust is key. When there is lack of trust there is legal overhead, misinformation and static control structures that prevent agility. These projects tend to be more expensive and deliver inferior results. Open source requires a whole bunch of trust. Put yourself in Microsoft’s shoes. Would you find it easy to “give away” technology that you’ve spent billions developing? You’d need to put your trust in people you’ve never met, hoping you will get back as much as you’ve given.

Open source in the software industry might be the most obvious example of trusting others to do something great(er) together. But in fact our entire civilization requires us to trust in each other. Without trust currency would be hugely inconvenient, e-commerce would be impossible and we would have no taxes to pay for services such as law, defense and education.

I don’t believe every sector would benefit from unconditional cooperation but I’m certain that there are vast possibilities to create better, cheaper, fairer, and safer products and services if we start trusting each other more!

Which sectors do you think would benefit from replacing competition with cooperation? How can the software industry benefit from even more cooperation? Or have we gone too far? Use the comment section below!

Nov

Refile: Fixing Ruby File Uploads


Six years ago, Merb was on the scene. I was one of its acolytes. I wanted a nice upload plugin for Merb. Since I had some experience in this area from writing (never popular) UploadColumn, I wrote one. It was called merb-upload, and my intention was for this to eventually become part of merb-more. You've probably never heard of merb-upload, but you may have heard of what it eventually became. It slowly grew Rails support, and to reflect the diversification, I renamed it CarrierWave.

It actually took a while for CarrierWave to become succesful, so it was the new kid for the long time, but it's actually only about a year younger than Paperclip. While both of these libraries, the two most popular for file uploads by far, have changed drastically over time, they are nevertheless both over six years old.

A lot has changed since then.

Over the years I've been contemplating what I would have done differently if I had to do it all over again.

  1. CarrierWave's killer feature is fixing file uploads to work even if validations failed and the form needs to be redisplayed. Unfortunately this has never worked reliably when files were uploaded to S3.

  2. Processing files on upload can be prohibitively expensive, and CarrierWave doesn't provide a good solution to this. When the size of an image should be changed CarrierWave requires re-processing all files. Dragonfly has a much better solution to this problem: processing images on the fly and serving them up directly, behind a CDN.

  3. Direct to S3 uploads are cumbersome. I find myself needing this time after time, and while there are bolt-on solutions, nothing really feels very solid.

  4. The internal design of CarrierWave is just far too flexible and strange. Uploader classes, while a nice relief from the monstrous configuration in model classes that other plugins require, are also quite brittle. I also found myself never actually using any of the insane flexibility that CarrierWave provides. It's really hard to figure out what actually happens inside CarrierWave.

  5. The integrations have changed. When CarrierWave was written, Ruby web frameworks were different than they were today. Rails 2.3 with support for Rack wasn't released until a year after its inception. Merb was a serious contender. As a result, there's a lot of boilerplate in CarrierWave which isn't really necessary.

All of these problems have been on my mind off and on again over the past couple of years. Even though I haven't been involved in the CarrierWave project in years, I still found myself unsatisfied with its current state, and with the status quo of file uploads in general.

So I wrote a new one. This is my third attempt at a file upload plugin, and I'm hoping that this time, maybe I got it right.

It probably isn't for everyone. It's far, far more opinionated than CarrierWave or Paperclip. I don't expect it to outright replace either. But if you're looking for something which is simple, where you can get direct to S3 uploads with the addition of a single config option, where you don't really have to think about how and where files are stored. This may be the one for you.

It's called Refile. Please check it out and let me know what you think. All feedback and especially all contributions are very welcome.

Feb

Using websockets in native iOS and Android apps


This is an example of using websockets to communicate in real time from browser, iOS, and Android clients.

Why would we want to do that? Since the same real time backend can be reused for all platforms, we can keep the architecture simple and the number of distinct components that can fail to a minimum. Or maybe we just want to add a native client to an existing web service that already uses websockets, without having to change stuff on the backend. This post will demonstrate that it is quite straightforward to connect to a websocket server from iOS and Android as well as from the browser.

We recently used this approach in the Need for Speed™ Network application, which is available for all three platforms. Check out the trailer video on YouTube to see it in action. We used websockets to send the real time in-game positions of the user's friends in order to display them on a map, among other things.

The websocket client API is very simple, and is pretty much identical across the different client implementations used in this example. Adding TLS encryption is also pretty easy from the client's point of view, since we just need to change the ws:// URL to a wss:// one.

The example app is a simple chat service. There are no separate channels or nicknames to keep it as bare bones as possible. When a new client connects, it automatically sends a message including what device or browser it is running on.

The full example code (server and the three clients) is available at https://github.com/elabs/mobile-websocket-example. The apps are very basic and only intended to show how to use the API - they contain very little error handling, no tests, are not prepared for localization, etc.

Server

This is a basic websocket server implemented in Ruby using the EM-WebSocket gem.

Here is the entire code for the server implementation:

require "em-websocket"

EventMachine.run do
  @channel = EM::Channel.new

  EventMachine::WebSocket.start(host: "0.0.0.0", port: 8080, debug: true) do |ws|
    ws.onopen do
      sid = @channel.subscribe { |msg| ws.send(msg) }
      @channel.push("#{sid} connected")

      ws.onmessage { |msg| @channel.push("<#{sid}> #{msg}") }

      ws.onclose { @channel.unsubscribe(sid) }
    end
  end
end

The server accepts websocket connections on port 8080. When a new client connects, it subscribes to an internal channel. Every time a client sends data it is pushed to the channel, and when new data is available on the channel, it is sent to all connected clients. Clients are identified by their subscription ID which is an incrementing integer. When the client disconnects, it is unsubscribed from the channel.

To get the server running, run bundle install and then ruby server.rb.

Browser client

The biggest reason to use websockets in the first place (instead of, say, regular sockets) is that they are possible to use from a modern browser context. The JavaScript code looks like this:

$(document).ready(function() {
  ws = new WebSocket("ws://" + location.hostname + ":8080/");

  ws.onmessage = function(event) {
    $("#messages").append("<p>" + event.data + "</p>");
  };

  ws.onclose = function() {
    console.log("Socket closed");
  };

  ws.onopen = function() {
    console.log("Connected");
    ws.send("Hello from " + navigator.userAgent);
  };

  $("#new-message").bind("submit", function(event) {
    event.preventDefault();
    ws.send($("#message-text").val());
    $("#message-text").val("");
  });
});

We connect to the server on port 8080 (the port we listened to in the server) and implement callbacks for when the connection is established, when a message arrives, and when the connection is closed.

We also add an event handler for sending a new message when the user submits a form.

This code only supports modern browsers. There are projects that can be used to support older browsers via a Flash implementation.

To start serving the browser client on port 8000, run python -m SimpleHTTPServer from the directory with the HTML and JS file.

iOS client

To build the iOS client you need a Mac and the Apple developer tools (Xcode) installed. To run it on an actual device (and not the simulator), an Apple developer program membership is required.

For iOS, we use the websocket client implementation SocketRocket. The easiest way to include the library is via Cocoapods. This is what our Podfile looks like:

platform :ios, "7.0"

pod "SocketRocket"

If you haven't got Cocoapods installed, run gem install cocoapods and pod setup. Then run pod install from the iOS directory with the Podfile, and we're good to go.

The relevant code is in ViewController.m. To connect to the websocket server:

- (void)connectWebSocket {
  webSocket.delegate = nil;
  webSocket = nil;

  NSString *urlString = @"ws://localhost:8080";
  SRWebSocket *newWebSocket = [[SRWebSocket alloc] initWithURL:[NSURL URLWithString:urlString]];
  newWebSocket.delegate = self;

  [newWebSocket open];
}

Replace localhost with the relevant hostname if not running the simulator on the same computer that runs the websocket server.

Then we implement the SRWebSocketDelegate protocol:

- (void)webSocketDidOpen:(SRWebSocket *)newWebSocket {
  webSocket = newWebSocket;
  [webSocket send:[NSString stringWithFormat:@"Hello from %@", [UIDevice currentDevice].name]];
}

- (void)webSocket:(SRWebSocket *)webSocket didFailWithError:(NSError *)error {
  [self connectWebSocket];
}

- (void)webSocket:(SRWebSocket *)webSocket didCloseWithCode:(NSInteger)code reason:(NSString *)reason wasClean:(BOOL)wasClean {
  [self connectWebSocket];
}

- (void)webSocket:(SRWebSocket *)webSocket didReceiveMessage:(id)message {
  self.messagesTextView.text = [NSString stringWithFormat:@"%@\n%@", self.messagesTextView.text, message];
}

Note that this is very similar to the Websockets browser Javascript API.

Here is how to send a new message:

- (IBAction)sendMessage:(id)sender {
  [webSocket send:self.messageTextField.text];
  self.messageTextField.text = nil;
}

The remaining code in the iOS project is mainly for dealing with resizing views when showing or hiding the keyboard. The app targets iOS 7.0 and is not optimized for iPad.

Android client

The Android app is built using Android Studio.

For Android we will use the client library Java WebSockets. Since we're using Android Studio, the easiest way to include the code from Maven is by listing it in the gradle dependencies in build.gradle:

dependencies {
  compile "org.java-websocket:Java-WebSocket:1.3.0"
}

Gradle will take care of downloading it and making it available to the project the next time we build.

The relevant code for interacting with the websocket server is in MainActivity.java. To connect to the websocket server:

private void connectWebSocket() {
  URI uri;
  try {
    uri = new URI("ws://websockethost:8080");
  } catch (URISyntaxException e) {
    e.printStackTrace();
    return;
  }

  mWebSocketClient = new WebSocketClient(uri) {
    @Override
    public void onOpen(ServerHandshake serverHandshake) {
      Log.i("Websocket", "Opened");
      mWebSocketClient.send("Hello from " + Build.MANUFACTURER + " " + Build.MODEL);
    }

    @Override
    public void onMessage(String s) {
      final String message = s;
      runOnUiThread(new Runnable() {
        @Override
        public void run() {
          TextView textView = (TextView)findViewById(R.id.messages);
          textView.setText(textView.getText() + "\n" + message);
        }
      });
    }

    @Override
    public void onClose(int i, String s, boolean b) {
      Log.i("Websocket", "Closed " + s);
    }

    @Override
    public void onError(Exception e) {
      Log.i("Websocket", "Error " + e.getMessage());
    }
  };
  mWebSocketClient.connect();
}

Make sure to change websockethost to whichever address hosts the websocket server.

And to send a new message:

public void sendMessage(View view) {
  EditText editText = (EditText)findViewById(R.id.message);
  mWebSocketClient.send(editText.getText().toString());
  editText.setText("");
}

The Android app targets Android 4.0+. It requires the android.permission.INTERNET permission to connect to the websocket server.

Hopefully this example illustrates that using Websockets from native mobile apps is a viable alternative, especially if there is a web browser client consuming the same API.

Again, all the example code above is available at https://github.com/elabs/mobile-websocket-example. If you clone that repo you should have everything you need to build and run the server and all three clients.

Jan

The Year 2013 — A Summary


Every December, I write a recap of the year that’s gone. I’m a couple of days late this time, but I’d still like to write about some of our highlights from 2013.

Need For Speed™ Network

2013 was dominated by our biggest and most ambitious project yet: Need For Speed™ Network for our new clients Ghost Games and EA. We had pretty much the full team working on this project for most of the year. We’re not completely done with it yet, and we’ll have more to say when we’re finished. In the mean time, check out the promo video for the app.

ProjectPuzzle

Our own product ProjectPuzzle has been left mostly unattended since we launched it in 2012. We had several new customers sign up during 2013 though, and we received lots of great feedback from people who tried it out. We’ve started working on the next version of ProjectPuzzle, and I’m really looking forward to sharing the progress of that with you in the coming months.

Nordic Ruby

The 4th annual Nordic Ruby was the best one yet. It was probably also the last one, in a way. We’re changing things a bit for 2014.

I wrote a blog post about Nordic Ruby 2013 with lots of great photos and video. Check it out if you haven’t already.

Part of the team at Nordic Ruby 2013

Anders T

At the end of November we said goodbye to Anders Törnqvist. Anders was one of the original 4 team members at Elabs, and after 5 great years here he wanted to try switching from consulting to working in a product company. We wish him the best of luck with his new job at PugglePay.

Anders Törnqvist

What’s next?

Right now we’re working hard on finishing the last pieces of the Need For Speed™ Network project, and after that we’re looking for new client projects. If you have something you think we can help with, please get in touch.

Other than client work, we’re also very excited about getting to work on the next versions of ProjectPuzzle and Nordic Ruby.

Here’s to 2014.

/ CJ