Building a Single Page Web App for Mobile

I few weeks ago, I was faced with a di­lemma. I really like play­ing the game Car­cas­sonne, and I wanted to be able to bring the game with me when I traveled, but while the pieces – small card­board squares – can eas­ily fit into a small bag, the board used to tally play­ers’ scores is lar­ger and awk­ward to travel with. So I de­cided to solve this prob­lem by build­ing an app to re­place the board. The app needed to allow a se­lec­ted num­ber of users (who can choose their meeple color) to be able to in­cre­ment their count by click­ing the num­ber rep­res­ent­ing that count.

Most im­port­antly, the app also needed to be mo­bile-friendly from the start, be­cause it would mostly likely al­ways be ac­cessed from someone’s tele­phone while trav­el­ing. The focus of this pro­ject was to make the app mo­bile-browser ready, without need­ing to build iOS and An­droid apps. I’m going to walk through how I used JavaS­cript, HTML, CSS, re­spons­ive design prin­ciples, and Rack to make this simple app called Car­cas­sonne Counter. Ul­ti­mately this is an ex­ample of build­ing something with tools at hand that is en­tirely mo­bile re­spons­ive and user-friendly.

The first task was to get the basic logic and func­tion­al­ity in place for user in­ter­ac­tion. Cli­ent-side JavaS­cript is ob­vi­ously per­fect for this. I de­cided to go to town on the it as an ex­er­cise in writ­ing good, clean JavaS­cript, but that’s for an­other blog post. But to give a gen­eral back­ground of what the app is doing:

  • a user can select the number of players and their colors
  • .onClick() the user increments the score count by one
  • There are Player prototypes that:
    • keep track of player scores and color
    • methods to reset the game
    • decrement the score (if you make a mistake through overeager clicking)
    • and a bunch of jQuery powered front-end methods

Let’s dive in to some ef­fect­ive and easy ways to make a web app com­pletely mo­bile friendly, with as close as pos­sible func­tion­al­ity to any simple iOS or An­droid app that achieves the same thing.

The Layout

The app is a single page / single user ex­per­i­ence design. The main focus of the page is each play­ers’ block, which holds their score num­ber with the back­ground-color of whichever color they se­lec­ted. The tem­plates for each num­ber of play­ers is hard-coded into the index.html page, be­cause I think it makes more sense than dy­nam­ic­ally ren­der­ing it, given how there are only 5 dif­fer­ent tem­plates.

Here’s what the app looks like for a two player game with blue and red meeples:

The en­tire page, no mat­ter how many play­ers were in the game (2-6), needed to be en­tirely fit for a mo­bile screen. For this I set the height and width to view width (vw) and view height (vh). CSS se­lect­ors, from body to each con­tainer for a play­ers’ block, use vw and vh in­stead of pe­centages or pixels. This means that no mat­ter the size of the screen (an iPhone 6 vs a Nexus 7 vs a Moto X) the color block, given the HTML tem­plate, will fit the view’s width and height. This fixes the size of that HTML ele­ment to whatever “per­cent­age” is se­lec­ted. So for the above two player tem­plate, the CSS looks like this:

1
2
3
4
5
#container2 .player {
  width: 100vw;
  height: 50vh;
  line-height: 50vh;
}

This re­mains en­tirely re­spons­ive to whatever size screen / device the app is viewed on.

Clicking / Tapping Functionality

The main func­tion­al­ity of this app is click­ing, or in the case of mo­bile use, tap­ping. One prob­lem I en­countered with the tap­ping func­tion­al­ity was that most mo­bile devices ini­tial­ize double-tap­ping zoom as soon as tap­ping reaches a cer­tain speed. On this single page, given that the view’s width and height is al­ways fixed to the size of the screen, zoom­ing isn’t ne­ces­sary. I needed a way to dis­able that double-tap fea­ture to allow for rapid tal­ly­ing.

First step is in­clud­ing in the <head> a meta prop­erty that dis­ables the user’s abil­ity to change the view of the screen. This pre­vents the double-tap­ping zoom:

1
<meta name="viewport" content="user-scalable=no" />

Next, in order to util­ize that double-tap func­tion­al­ity for rapid tap­ping, I used a super simple and easy JavaS­cript plu­gin called jQuery Fin­ger.

In ad­di­tion to set­ting an event hand­ler on tap…

1
2
$(".player" + this.number).on("tap", function() {
  this.incrementNumber(); }.bind(this));

…you can also set one on double tap, using jQuery Fin­ger’s cus­tom event hand­ler:

1
2
$(".player" + this.number).on("doubletap", function() {
  this.incrementNumber(); }.bind(this));

Focused Swiping Functionality

What if a player ac­ci­dent­ally in­cre­ments their tally, and they need to sub­tract some points? Swip­ing back­wards on the num­ber area where you’re tap­ping to in­cre­ment is a lo­gical ges­ture. jQuery Fin­ger has an event hand­ler for that as well:

1
2
$(".player" + this.number).on("flick", function() {
  this.decrementNumber(); }.bind(this));

Rack

Fi­nally, I turned the single page ap­plic­a­tion into a Rack app. This was en­tirely out of lazi­ness, be­cause I wanted to be able to host it eas­ily, and Her­oku is great for that. There are pluses and minuses to static Rack web­sites, and I won’t go too much into that only to say I think it’s fine for small, single page, side pro­jects like this. I mostly fol­lowed this help­ful guide on Her­oku on how to set up a config.ru file.

Conclusion

Even­tu­ally I would love for Car­cas­sonne Counter to be turned into something that’s in the App Store / Google Play Store, but ul­ti­mately this solu­tion makes the most sense given the simple prob­lem and the usage. The app is com­pletely re­spons­ive for any sized screen, which makes it pretty ver­sat­ile for users, and has many of the same mo­bile ges­ture cap­ab­ilites as an iOS/An­droid app. Ul­ti­mately I’ll be build­ing a Rails backend to per­sist game data and sup­port other game count­ing/scor­ing as well, and at that point, this app will most likely be ex­pan­ded on sub­stan­tially, and per­haps I’ll even delve into mak­ing at iOS/An­droid then.

Comments