Building The New Financial Times Web App (A Case Study)

Advertisement

Update (10.10.2013): Good news: according to recent tests, Flexbox layout isn’t slow1 any longer. Author’s comments about the performance of Flexbox refer to the original (legacy) flexbox that used display: box;. A head-to-head comparison of old vs. new syntax is available2 as well. — Ed.

When the mockups for the new Financial Times application3 hit our desks in mid-2012, we knew we had a real challenge on our hands. Many of us on the team (including me) swore that parts of interface would not be possible in HTML5. Given the product team’s passion for the new UI, we rolled up our sleeves and gave it our best shot.

We were tasked with implementing a far more challenging product, without compromising the reliable, performant experience that made the first app so successful.

promo-500-compr4

We didn’t just want to build a product that fulfilled its current requirements; we wanted to build a foundation that we could innovate on in the future. This meant building with a maintenance-first mentality, writing clean, well-commented code and, at the same time, ensuring that our code could accommodate the demands of an ever-changing feature set.

In this article, I’ll discuss some of the changes we made in the latest release and the decision-making behind them. I hope you will come away with some ideas and learn from our solutions as well as our mistakes.

Supported Devices

The first Financial Times Web app ran on iPad and iPhone in the browser, and it shipped in a native (PhoneGap5-esque) application wrapper for Android and Windows 8 Metro devices. The latest Web app is currently being served to iPad devices only; but as support is built in and tested, it will be rolled out to all existing supported platforms. HTML5 gives developers the advantage of occupying almost any platform. With 2013 promising the launch of several new Web application marketplaces (eg. Chrome Web Store6 and Mozilla Marketplace7), we are excited by the possibilities that lie ahead for the Web.

Fixed-Height Layouts

The first shock that came from the new mockups was that they were all fixed height. By “fixed height,” I mean that, unlike a conventional website, the height of the page is restricted to the height of the device’s viewport. If there is more content than there is screen space, overflow must be dealt with at a component level, as opposed to the page level. We wanted to use JavaScript only as a last resort, so the first tool that sprang to mind was flexbox. Flexbox gives developers the ability to declare flexible elements that can fill the available horizontal or vertical space, something that has been very tricky to do with CSS. Chris Coyier has a great introduction to flexbox8.

Using Flexbox in Production

Flexbox has been around since 2009 and has great support9 on all the popular smartphones and tablets. We jumped at the chance to use flexbox when we found out how easily it could solve some of our complex layouts, and we started throwing it at every layout problem we faced. As the app began to grow, we found performance was getting worse and worse.

We spent a good few hours in Chrome Developers Tools’ timeline and found the culprit: Shock, horror! — it was our new best friend, flexbox. The timeline showed that some layouts were taking close to 100 milliseconds; reworking our layouts without flexbox reduced this to 10 milliseconds! This may not seem like a lot, but when swiping between sections, 90 milliseconds of unresponsiveness is very noticeable.

Back to the Old School

We had no other choice but to tear out flexbox wherever we could. We used 100% height, floats, negative margins, border-box sizing and padding to achieve the same layouts with much greater performance (albeit with more complex CSS). Flexbox is still used in some parts of the app. We found that its impact on performance was less expensive when used for small UI components.

layout-time-with-flexbox-500_comp10
Page layout time with flexbox

layout-time-without-flexbox-500_comp11
Page layout time without flexbox

Update: This post sparked a lot of debate over whether Flexbox was ready for production. Chris Coyier wrote ‘Does Flexbox have a performance problem?’12 in which Paul Irish contributed some valuable metrics.

Paul concluded that old Flexbox (display: box) was ~2.3x slower than its newer counterpart (display: flex). He then more recently followed this up with a more in depth article entitled ‘Flexbox layout isn’t slow’13 over on HTML5 Rocks.

Truncation

The content of a fixed-height layout will rarely fit its container; eventually it has to overflow. Traditionally in print, designers have used ellipses (three dots) to solve this problem; however, on the Web, this isn’t the simplest technique to implement.

Ellipsis

You might be familiar with the text-overflow: ellipsis declaration in CSS. It works great, has awesome browser support14, but has one shortfall: it can’t be used for text that spans multiple lines. We needed a solution that would insert an ellipsis at the point where the paragraph overflows its container. JavaScript had to step in.

ellipsis-500_mini15
Ellipsis truncation is used throughout.

After an in-depth research and exploration of several different approaches, we created our FTEllipsis16 library. In essence, it measures the available height of the container, then measures the height of each child element. When it finds the child element that overflows the container, it caps its height to a sensible number of lines. For WebKit-based browsers, we use the little-known -webkit-line-clamp17 property to truncate an element’s text by a set number of lines. For non-WebKit browsers, the library allows the developer to style the overflowing container however they wish using regular CSS.

Modularization

Having tackled some of the low-level visual challenges, we needed to step back and decide on the best way to manage our application’s views. We wanted to be able to reuse small parts of our views in different contexts and find a way to architect rock-solid styling that wouldn’t leak between components.

One of the best decisions we made in implementing the new application was to modularize the views. This started when we were first looking over the designs. We scribbled over printouts, breaking the page down into chunks (or modules). Our plan was to identify all of the possible layouts and modules, and define each view (or page) as a combination of modules sitting inside the slots of a single layout.

Each module needed to be named, but we found it very hard to describe a module, especially when some modules could have multiple appearances depending on screen size or context. As a result, we abandoned semantic naming and decided to name each component after a type of fruit — no more time wasted thinking up sensible, unambiguous names!

An example of a module’s markup:

<div class="apple">
  <h2 class="apple_headline">{{headline}}</h2>
  <h3 class="apple_sub-head">{{subhead}}</h3>
  <div class="apple_body">{{body}}</div>
</div>

An example of a module’s styling:

.apple {}

.apple_headline {
  font-size: 40px;
}

.apple_sub-head {
  font-size: 20px;
}

.apple_body {
  font-size: 14px;
  column-count: 2;
  color: #333;
}

Notice how each class is prefixed with the module’s name. This ensures that the styling for one component will never affect another; every module’s styling is encapsulated. Also, notice how we use just one class in our CSS selectors; this makes our component transportable. Ridding selectors of any ancestral context means that modules may be dropped anywhere in our application and will look the same. This is all imperative if we want to be able to reuse components throughout the application (and even across applications).

What If a Module Needs Interactions?

Each module (or fruit) has its own markup and style, which we wrote in such a way that it can be reused. But what if we need a module to respond to interactions or events? We need a way to bring the component to life, but still ensure that it is unbound from context so that it can be reused in different places. This is a little trickier that just writing smart markup and styling. To solve this problem, we wrote FruitMachine.

Reusable Components

FruitMachine18 is a lightweight library that assembles our layout’s components and enables us to declare interactions on a per-module basis. It was inspired by the simplicity of Backbone19 views, but with a little more structure to keep “boilerplate” code to a minimum. FruitMachine gives our team a consistent way to work with views, while at the same time remaining relatively unopinionated so that it can be used in almost any view.

The Component Mentality

Thinking about your application as a collection of standalone components changes the way you approach problems. Components need to be dumb; they can’t know anything of their context or of the consequences of any interactions that may occur within them. They can have a public API and should emit events when they are interacted with. An application-specific controller assembles each layout and is the brain behind everything. Its job is to create, control and listen to each component in the view.

For example, to show a popover when a component named “button” is clicked, we would not hardcode this logic into the button component. Instead “button” would emit a buttonclicked event on itself every time its button is clicked; the view controller would listen for this event and then show the popover. By working like this, we can create a large collection of components that can be reused in many different contexts. A view component may not have any application-specific dependencies if it is to be used across projects.

Working like this has simplified our architecture considerably. Breaking down our views into components and decoupling them from our application focuses our decision-making and moves us away from baking complex, heavily dependent modules into our application.

The Future of FruitMachine

FruitMachine was our solution to achieve fully transportable view components. It enables us to quickly define and assemble views with minimal effort. We are currently using FruitMachine only on the client, but server-side (NodeJS) usage has been considered throughout development. In the coming months, we hope to move towards producing server-side-rendered websites that progressively enhance into a rich app experience.

You can find out more about FruitMachine and check out some more examples in the public GitHub repository20.

Retina Support

The Financial Times’ first Web app was released before the age of “Retina” screens. We retrofitted some high-resolution solutions, but never went the whole hog. For our designers, 100% Retina support was a must-have in the new application. We developers were sick of maintaining multiple sizes and resolutions of each tiny image within the UI, so a single vector-based solution seemed like the best approach. We ended up choosing icon fonts21 to replace our old PNGs, and because they are implemented just like any other custom font, they are really well supported. SVG graphics were considered, but after finding a lack of support22 in Android 2.3 and below, this option was ruled out. Plus, there is something nice about having all of your icons bundled up in a single file, whilst not sacrificing the individuality of each graphic (like sprites).

Our first move was to replace the Financial Times’ logo image with a single glyph in our own custom icon font. A font glyph may be any color and size, and it always looks super-sharp and is usually lighter in weight than the original image. Once we had proved it could work, we began replacing every UI image and icon with an icon font alternative. Now, the only pixel-based image in our CSS is the full-color logo on the splash screen. We used the powerful but rather archaic-looking FontForge23 to achieve this.

Once past the installation phase, you can open any font file in FontForge and individually change the vector shape of any character. We imported SVG vector shapes (created in Adobe Illustrator) into suitable character slots of our font and exported as WOFF and TTF font types. A combination of WOFF and TTF file formats are required to support iOS, Android and Windows devices, although we hope to rely only on WOFFs once Android gains support (plus, WOFFs are around 25% smaller in file size than TTFs).

icon-font-500-compr24
The Financial Times’ icon font in Font Forge

Images

Article images are crucial for user engagement. Our images are delivered as double-resolution JPEGs so that they look sharp on Retina screens. Our image service (running ImageMagick25) outputs JPEGs at the lowest possible quality level without causing noticeable degradation (we use 35 for Retina devices and 70 for non-Retina). Scaling down retina size images in the browser enables us to reduce JPEG quality to a lower level than would otherwise be possible without compression artifacts becoming noticeable. This article26 explains this technique in more detail.

It’s worth noting that this technique does require the browser to work a little harder. In old browsers, the work of scaling down many large images could have a noticeable impact on performance, but we haven’t encountered any serious problems.

Native-Like Scrolling

Like almost any application, we require full-page and subcomponent scrolling in order to manage all of the content we want to show our users. On desktop, we can make use of the well-established overflow CSS property. When dealing with the mobile Web, this isn’t so straightforward. We require a single solution that provides a “momentum” scrolling experience across all of the devices we support.

overflow: scroll

The overflow: scroll declaration is becoming usable on the mobile Web. Android and iOS now support it, but only since Android 3.0 and iOS 5. IOS 5 came with the exciting new -webkit-overflow-scrolling: touch property, which allows for native momentum-like scrolling in the browser. Both of these options have their limitations.

Standard overflow: scroll and overflow: auto don’t display scroll bars as users might expect, and they don’t have the momentum touch-scrolling feel that users have become accustomed to from their native apps. The -webkit-overflow-scrolling: touch declaration does add momentum scrolling and scroll bars, but it doesn’t allow developers to style the scroll bars in any way, and has limited support (iOS 5+ and Chrome on Android).

A Consistent Experience

Fragmented support and an inconsistent feel forced us to turn to JavaScript. Our first implementation used the TouchScroll27 library. This solution met our needs, but as our list of supported devices grew and as more complex scrolling interactions were required, working with it became trickier. TouchScroll lacks IE 10 support, and its API interface is difficult to work with. We also tried Scrollability28 and Zynga Scroller29, neither of which have the features, performance or cross-browser capability we were looking for. Out of this problem, FTScroller was developed: a high-performance, momentum-scrolling library with support for iOS, Android, Playbook and IE 10.

FTScroller

FTScroller30’s scrolling implementation is similar to TouchScroll’s, with a flexible API much like Zynga Scroller. We added some enhancements, such as CSS bezier curves for bouncing, requestAnimationFrame for smoother frame rates, and support for IE 10. The advantage of writing our own solution is that we could develop a product that exactly meets our requirements. When you know the code base inside out, fixing bugs and adding features is a lot simpler.

FTScroller is dead simple to use. Just pass in the element that will wrap the overflowing content, and FTScroller will implement horizontal or vertical scrolling as and when needed. Many other options31 may be declared in an object as the second argument, for more custom requirements. We use FTScroller throughout the Financial Times’ Web app for a consistent cross-platform scrolling experience.

A simple example:

var container = document.getElementById('scrollcontainer');
var scroller = new FTScroller(container);

The Gallery

The part of our application that holds and animates the page views is known as the “gallery.” It consists of three divisions: left, center and right. The page that is currently in view is located in the center pane. The previous page is positioned off screen in the left-hand pane, and the next page is positioned off screen in the right-hand pane. When the user swipes to the next page, we use CSS transitions to animate the three panes to the left, revealing the hidden right pane. When the transition has finished, the right pane becomes the center pane, and the far-left pane skips over to become the right pane. By using only three page containers, we keep the DOM light, while still creating the illusion of infinite pages.

Web32
Infinite scrolling made possible with a three-pane gallery

Making It All Work Offline

Not many Web apps currently offer an offline experience, and there’s a good reason for that: implementing it is a bloody pain! The application cache (AppCache) at first glance appears to be the answer to all offline problems, but dig a little deeper and stuff gets nasty. Talks by Andrew Betts33 and Jake Archibald34 explain really well the problems you will encounter. Unfortunately, AppCache is currently the only way to achieve offline support, so we have to work around its many deficiencies.

Our approach to offline is to store as little in the AppCache as possible. We use it for fonts, the favicon and one or two UI images — things that we know will rarely or never need updating. Our JavaScript, CSS and templates live in LocalStorage35. This approach gives us complete control over serving and updating the most crucial parts of our application. When the application starts, the bare minimum required to get the app up and running is sent down the wire, embedded in a single HTML page; we call this the preload.

We show a splash screen, and behind the scenes we make a request for the application’s full resources. This request returns a big JSON object containing our JavaScript, CSS and Mustache36 templates. We eval37 the JavaScript and inject the CSS into the DOM, and then the application launches. This “bootstrap” JSON is then stored in LocalStorage, ready to be used when the app is next started up.

On subsequent startups, we always use the JSON from LocalStorage and then check for resource updates in the background. If an update is found, we download the latest JSON object and replace the existing one in LocalStorage. Then, the next time the app starts, it launches with the new assets. If the app is launched offline, the startup process is the same, except that we cannot make the request for resource updates.

Images

Managing offline images is currently not as easy as it should be. Our image requests are run through a custom image loader and cached in the local database (IndexedDB38 or WebSQL39) so that the images can be loaded when a network connection is not present. We never load images in the conventional way, otherwise they would break when users are offline.

Our image-loading process:

  1. The loader scans the page for image placeholders declared by a particular class.
  2. It takes the src attribute of each image placeholder found and requests the source from our JavaScript image-loader library.
  3. The local database is checked for each image. Failing that, a single HTTP request is made listing all missing images.
  4. A JSON array of Base64-encoded images is returned from the HTTP response and stored separately in the local database.
  5. A callback is fired for each image request, passing the Base64 string as an argument.
  6. An <img> element is created, and its src attribute is set to the Base64 data-URI string.
  7. The image is faded in.

I should also mention that we compress our Base64-encoded image strings in order to fit as many images in the database as possible. My colleague Andrew Betts goes into detail40 on how this can be achieved.

In some cases, we use this cool trick to handle images that fail to load:

<img src="image.jpg" onerror="this.style.display='none';" />

Ever-Evolving Applications

In order to stay competitive, a digital product needs to evolve, and as developers, we need to be prepared for this. When the request for a redesign landed at the Financial Times, we already had a fast, popular, feature-rich application, but it wasn’t built for change. At the time, we were able to implement small changes to features, but implementing anything big became a slow process and often introduced a lot of unrelated regressions.

Our application was drastically reworked to make the new requirements possible, and this took a lot of time. Having made this investment, we hope the new application not only meets (and even exceeds) the standard of the first product, but gives us a platform on which we can develop faster and more flexibly in the future.

(al)

Footnotes

  1. 1 http://updates.html5rocks.com/2013/10/Flexbox-layout-isn-t-slow
  2. 2 http://updates.html5rocks.com/2013/10/Flexbox-layout-isn-t-slow
  3. 3 http://apps.ft.com/ftwebapp/postlaunch.html
  4. 4 http://www.smashingmagazine.com/wp-content/uploads/2013/05/promo-compr.png
  5. 5 http://phonegap.com/
  6. 6 https://chrome.google.com/webstore
  7. 7 https://marketplace.firefox.com/
  8. 8 http://css-tricks.com/old-flexbox-and-new-flexbox/
  9. 9 http://caniuse.com/flexbox
  10. 10 http://www.smashingmagazine.com/wp-content/uploads/2013/05/layout-time-with-flexbox_mini.jpg
  11. 11 http://www.smashingmagazine.com/wp-content/uploads/2013/05/layout-time-without-flexbox_mini.jpg
  12. 12 http://css-tricks.com/does-flexbox-have-a-performance-problem/
  13. 13 http://updates.html5rocks.com/2013/10/Flexbox-layout-isn-t-slow
  14. 14 http://caniuse.com/text-overflow
  15. 15 http://www.smashingmagazine.com/wp-content/uploads/2013/05/ellipsis_mini.jpg
  16. 16 http://github.com/ftlabs/ftellipsis
  17. 17 http://dropshado.ws/post/1015351370/webkit-line-clamp
  18. 18 http://github.com/ftlabs/fruitmachine
  19. 19 http://backbonejs.org/
  20. 20 http://github.com/ftlabs/fruitmachine
  21. 21 http://css-tricks.com/examples/IconFont/
  22. 22 http://caniuse.com/svg
  23. 23 http://fontforge.org/
  24. 24 http://www.smashingmagazine.com/wp-content/uploads/2013/05/icon-font-large-compr.png
  25. 25 http://www.imagemagick.org/
  26. 26 http://filamentgroup.com/lab/rwd_img_compression/
  27. 27 https://github.com/davidaurelio/TouchScroll
  28. 28 https://github.com/joehewitt/scrollability
  29. 29 https://github.com/zynga/scroller
  30. 30 https://github.com/ftlabs/ftscroller
  31. 31 https://github.com/ftlabs/ftscroller#options
  32. 32 http://www.smashingmagazine.com/wp-content/uploads/2013/05/the-gallery_mini.jpg
  33. 33 http://bdconf.com/ft
  34. 34 http://www.youtube.com/watch?v=cR-TP6jOSQM
  35. 35 https://developer.mozilla.org/en-US/docs/DOM/Storage#localstorage
  36. 36 http://mustache.github.io/
  37. 37 https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/eval
  38. 38 https://developer.mozilla.org/en-US/docs/IndexedDB
  39. 39 http://www.html5rocks.com/en/tutorials/webdatabase/websql-indexeddb/
  40. 40 http://labs.ft.com/2012/06/text-re-encoding-for-optimising-storage-capacity-in-the-browser/

↑ Back to topShare on Twitter

Wilson is a front-end developer at FT Labs in London. He enjoys pushing the limits of responsive design, and dreams in JavaScript. You can follow him on Twitter for frequent front-end goodness.

Advertising

Note: Our rating-system has caused errors, so it's disabled at the moment. It will be back the moment the problem has been resolved. We're very sorry. Happy Holidays!

  1. 1

    For so far this has been the best published article on Smashing Magazine where I lay my eyes on. Thank you for this very detailed case study! Im 100% sure that many will find it helpfull on their quest of app development. Thanks and take care Nils

  2. 3

    oh great! I love FT labs (and FT too…)

    • 4

      Spinelli? I was thinking… “I bet Matteo would like this”. Or maybe you are not the same Matteo I know :-)

  3. 5

    Just one correction, text-overflow: ellipsis; can be used for text that spans multiple lines, at least on webkit…

    • 6

      Nuno, are you referring to -webkit-line-clamp? We make use of this feature within our FTEllipsis library, but you have to state the number of lines explicitly. With components of flexible height, we don’t always know how many lines to truncate by, and this has to be calculated manually.

  4. 7

    Juarez P. A. Filho

    May 23, 2013 3:04 am

    Amazing. It will be great to implement on a upcoming job. Keep the great work. :)

  5. 8

    Finally! A quality article written for (and by) professionals on SM. To the SM Editor: we want more of this quality, please.
    Excellent work, Wilson! Thanks for sharing your experience.

  6. 10

    Daniel Schwarz

    May 23, 2013 3:21 am

    Amazing. I was seriously impressed. Best article ever on SM.

  7. 11

    Great insights especially with regards to what has been used in a production environment both with the CSS and JS. There isn’t always time to experiment with new techniques/code and this definitely sheds a lot of light on what can be achieved ‘today’.

  8. 12

    Great article and insight. I am familiar with some of the issues but haven’t had time to investigate resolutions to them all, I am looking forward to taking a look at the FT libraries and reading more into the processes and technologies.

    Thanks!

  9. 13

    Amazing article! Especially mentioning the problems you encountered and explaining the solutions is invaluable as it provides context for learning from your experiences.

    Thanks a lot for providing the insight as well as making the libraries you developed public.

    One of the best Smashing articles in recent months!

  10. 14

    Matt Fairbrass

    May 23, 2013 5:19 am

    You mentioned originally that you used flexbox to achieve the layout you wanted. However you didn’t mention which version of the flexbox specification you used that resulted in your performance headaches? display: box, display: flexbox, display: flex?

    • 15

      To clarify, we were working with the earliest (most widely supported) spec: display: box;

      • 16

        Matt Fairbrass

        May 24, 2013 8:23 am

        Ok, good to know. Obviously the spec has changed significantly since then, and browser support for the 2012 spec is not quite there yet, but it would be interesting to know what the benchmarks are in performance comparatively between the different specs.

        I would hope the latest version would be the more performant ;-).

        • 17

          I have observed much better performance with the latest display: flex spec, but non of our target platforms support this spec yet (iOS5-iOS6, Android) :(.

          • 18

            Ok, good to know. Obviously the spec has changed significantly since then, and browser support for the 2012 spec is not quite there yet, but it would be

  11. 19

    To echo the crowd, YES, amazing article for the overview of the process and the depth of information. Explaining the decision making in process and pitfalls helps us all move forward. Thank you for sharing.

  12. 20

    Great article Wilson, thank you. I found it quite inspiring and I have just implemented something learned from it on my own site (ArtSocket):

    -webkit-overflow-scrolling: touch;

    In fact today I noticed that particular annoyance with the way one of the popup windows didn’t have the same feel as the rest of the window on iPad so your article could have not come at a better time!

  13. 22

    Great article, very interesting! And thanks for the scroller – I just needed it :)

  14. 23

    Great article, it made me aware of some techniques and approaches of which I wasn’t aware :)

  15. 24

    I was fortunate enough to work with part of the team on a small piece of this.

    These guys do everything right. Consistent conventions, unit testing, continuous integration. They are the standard against which I now measure my own team.

    Now I see they excel at writing case studies too! Not surprising.
    Would love to see another post covering your toolchain.

  16. 25

    Articles like this makes me think: “Why on earth did I choose to become a web designer/developer?… what was I thinking?

  17. 27

    Michael McGranahan

    May 23, 2013 10:36 am

    Great write-up, particularly the modularization of views. Thought I’d share my similar experience. I started with the same use of long/multipart class names, and although it is performant, I found it a bit cumbersome to develop with. In my approach, I strictly adhered to representing each level of structure in the class name, so a link in the headline would have the class name ‘.apple_headline_link’. This made nesting or un-nesting elements require a lot of tedious class renaming, making rapid experimentation very burdensome.

    Instead, I switched to a convention that relies on the child combinator. E.g.,

    .apple { /* rules */ }
    .apple > .-headline { /* rules */ }
    .apple > .-headline > .-link { /* rules */ }
    .apple > .-subHead { /* rules */ }
    .apple > .-subHead > .-link { /* rules, can differ from header link */ }
    .apple > .-body { /* rules */ }

    (Sorry about the formatting, can’t seem to improve it.) The benefit of this comes when using a CSS preprocessor. I use Stylus, since I use node.js for build tooling.

    
    .apple
      // rules
      > .-headline
        // rules
        > .-link
          // rules
      > .-subHead
        // rules
        > .-link
          // rules
      > .-body
        // rules
      &._green
        background-color green
    

    Now moving elements around only requires adding/removing a selector and indenting/un-indenting a bunch of declaration blocks. Performance is still good, but theoretically not as good as single class selectors.

    A necessary part of this approach is that sub-elements have class names beginning with a dash, while root elements do not. By corollary, all class names beginning with a dash are only used in selector groups scoped to a root class. With this convention, sub-elements are like private variables in OOP, with no meaning in the global class namespace.

    I also use “flags” (e.g. “_green”, using an underscore prefix naming convention), which are analagous to a public boolean property in the OO world. Consumers are free to “set” any supported flag on a module’s root element.

    Anyway, thanks again for the great read.

    • 28

      This seems like a nice approach. See my response to this comment for some answer to your questions.

      With regard to .apple_headline_link I only ever use one underscore to denote the module this element lives in. So I would favour .apple_headline-link, or if it is the only link in the module: .apple_link.

      Regarding ‘flags’ I use like to use ‘stateful’ class names like .apple.is-selected.

  18. 30

    Really amazing article. I’ve never done a mobile targeted web-app, but I’ll definitely have this article around when the time comes. I’m curious as to how many development hours and programmers were involved in this?

    • 31

      We have about 5 developers working full time on the Web App. I would have to estimate, but I would say if we were to build again from scratch, it would probably take about 6 months, but this is taking into account everything we have already learnt the first time round.

  19. 32

    This was a really interesting and inspiring article, thank you! Can this be an alternative to services like Adobe DPS and MAG+?

  20. 33

    Fantastic article Wilson – I think the FT web app has done much to promote HTML5 based apps as a viable alternative to native and it’s great to read an article on how this was achieved.

    You mention that you tried various scrolling libs before writing your own. I wondered if you’d tried out iScroll http://cubiq.org/iscroll-4 ? (Apparently version 5 is on its way).

  21. 34

    Damn, what a walkthrough!

    Thank you so much for your effort on this and cheers for your good work!

  22. 35

    This article read like an excellent recipe for a complicated thanksgiving dinner. Lots of steps, lots of tricks, all of them well reasoned and properly explained. I especially like that you covered and explained the techs that didn’t fit your needs.

  23. 36

    Geert-Jan Brits

    May 24, 2013 1:20 pm

    As others have mentioned, absolutely one of the best articles on SM I’ve ever read, and I must be coming here for about 8 years now. Kudos!

    Instantly started following you guys on github as well, so prepare to get some feature-reqs your way ;)

    One thing, that I’d like to get some background about as to the ‘why’ (somehing @Michael McGranahan already touched upon) :

    Why use the ‘apple’ and ‘apple-headline’ css-convention instead of just ‘apple > headline’ ? The latter would be just as transportable (even more so imho) . Moreover with css-preprocessors you get a nice and compact css-definition of your module as well.

    Want to enlighten us on your reasoning?

    Thanks and again, awesome job!

    Geert-Jan

    • 37

      I prefer .apple_headline over .apple > .headline for three reasons:

      1. The selector is shorter so (minutely) more performant.
      2. Having an element within your module with just the class ‘headline’ in it exposes your module to potential style leaks from other modules. If your app holds CSS from other developers/organisations who are less disciplined than yourself, their styles may leak onto your module elements. Prefixed class names are safer.
      3. If .headline moves within .apple (to longer be a direct child) you will have to update your selector. It will have to become .apple > .some-element > .headline or .apple .headline. This is not a concern for the selector .apple_headline

  24. 38

    Hey Wilson, thanks for that interesting inside view. Are there are any specific reasons why your team picked FontForge instead of IcoMoon?

    • 39

      IconMoon looks very nice! FontForge may give you a little bit more control, as it can do so much. I have found FontForge painful to install and use, so I would advise people try this IcoMoon App first. I’ll try IcoMoon for my next project :)

  25. 40

    Great article, but one question left; how did you prevent vertical scrolling on iPad?

    • 41

      document.addEventListener('touchstart', function(event) {
      event.preventDefault();
      });

      Warning: Stopping touchstart’s at this level will prevent any native scrolling (overflow: scroll) from working. Touch events must reach the document for any native touch handling to work.

  26. 42

    Really interesting points about offline usage, requests for updated content and image storage, retrieval and compression. Really nice to see something broken down clearly and concisely, good job!

  27. 43

    Great article, Wilson. It’s quite enlightening and inspiring!

  28. 44

    Thank you! Nice article

  29. 45

    Bit late to the party, but wanted to add my kudos. The depth of information from design choices to technical ones is greatly appreciated. Seeing a ‘teardown’ of a project that does its job so well is refreshing. A move away from the in-depth analysis of ‘hey, look at this cool thing we do with XYZ!’ and a more comprehensive, global look back (and look forward, considering the attention given to future-forwardism) on a worthy project.

    However this ended up on SM, my thanks to everyone involved. Gems like this are a shining example of why Smashing is so highly respected. Of course, you know, you’ll be expected to top yourselves. Hard to imagine with this kind of content.

  30. 46

    This is a very generous technical article! Thank you for sharing your experience and expertise.

  31. 47

    Johan Johansson

    May 31, 2013 7:37 am

    Outstanding article.

  32. 48

    Beautiful article indeed, thank you very much for sharing!

    Can you share a couple of tips on what you did with ImageMagick? I mean the “outputs JPEGs at the lowest possible quality level without causing noticeable degradation” part :)

    Thanks!

  33. 49

    Add my name to the list of incredibly thankful designer/developers! Yes, fantastic stuff that I *will* be referring to often in the months ahead as I build more and more of similar Web apps for clients (mostly federal government). The tricks, tips, and “don’t forgets” you have given us is priceless, along with general thinking on why choices were made. I’m going to have to tweet this, of course, just to help get the word out about what you’ve written and what you guys are up to. Keep it up!

  34. 50

    Wilson,
    Thank you so much, to you and the FT team, for contributing to the community. Tell your marketing team that you’re suddenly gaining a number of tech-savvy subscribers as a result!

    I really appreciate the level of detail when it is necessary, but also the brisk overview when appropriate. You really hit the SM audience dead on, and we’re going to want more articles like this.

    I’ve hit a few of these problems before as well, especially the svg/android limitation and general bloat. It is great that your management has given you enough room to innovate, rather than just hack things together. It is a credit to your company to do that, and I’m trusting it will contribute to the bottom line.

    Best,
    DW

  35. 51

    Matt Litherland

    July 9, 2013 9:46 am

    This article was so insightful, i loved it! Thank you!

  36. 52

    This is rockin awesome. I hope you’re working on a smartphone version of this app :P Would love to read another post but specifically dedicated to small screens.

    Again, fucking well done.

  37. 53

    Great article, Wilson, thank you for sharing!

    I have a question. What is the install/delivery process for the new HTML5 app?
    Do you still wrap it with PhoneGap?

    Thanks!

    • 54

      Hi Horia,

      Matt here from FT Labs. Thanks for your question. We’ve never wrapped the app in PhoneGap. For iOS and Chrome for Android we simply ask users to go to http://app.ft.com in their browser and recommend that they add the application to their home screen.

      For the Android Play store we have a thin, home grown native wrapper that allows us to integrate with the Android platform (widgets, etc) and on Windows 8 we have use the Windows 8 HTML5 SKD.

      Thanks,

      Matt

Leave a Comment

Yay! You've decided to leave a comment. That's fantastic! Please keep in mind that comments are moderated and rel="nofollow" is in use. So, please do not use a spammy keyword or a domain as your name, or else it will be deleted. Let's have a personal and meaningful conversation instead. Thanks for dropping by!

↑ Back to top