Having a large amount of CSS is unavoidable in a modern web application. Using preprocessors such as Sass help us manage that CSS but as we write more
@function’s and adopt techniques such as object orientated css the complexity grows. At FT Labs we even use (or perhaps abuse) npm as a package manager for Sass only repositories for various projects, including the FT Web app, so that those styles can be shared across projects.
With this ever increasing complexity, the differences between writing CSS and any other programming language are eroding.
All this complexity adds risk
In other programming languages we mitigate this kind of risk with automated testing. It’s time to start testing our Sass.
Testing Sass with Travis CI
Sass isn’t a language that Travis CI currently has first class support for but we can get it working with just a small number of hacks to the
Apart from some Sass (which I’m assuming you have already) you will need a
.travis.yml file that looks something like this:
script: - "test/travis.rb" language: sass before_install: - gem install sass
Here I’m telling Travis to first install Sass then execute the file located at
If you use a task runner such as make, Rake, Grunt or another you’ll probably want to use it rather than a script like this but I wanted to keep things as simple and technology agnostic as possible.
language: option is actually optional and it even allows for invalid values – helpfully it will default to Ruby (the language Sass is written in). Optimistically I’ve set it to
sass but it may be more robust to set this to
The next step will be to tell Travis exactly what to build.
Here are the contents of my
#!/usr/bin/env ruby result = `sass main.scss built.css` raise result unless $?.to_i == 0 raise "When compiled the module should output some CSS" unless File.exists?('built.css') puts "Regular compile worked successfully"
I’m using back ticks rather than any of the other other ways to run shell commands in ruby so I can easily check the status code and output any errors thrown by Sass (which come through via stdout). I then check to see if the built files exists – and
raise an error if it does not.
An error thrown at either step will stop the script executing and cause the build to fail.
Protection against bad PR
What can we actually test?
Compiling is a good first step but that offers little more than a linter and will only catch the most basic of regressions. Here are some other ideas and examples of what could be tested:
@function‘s are relatively easy – test known outputs against known inputs.
- Many CSS libraries, such as @csswizardry‘s grid module offer the option of ‘silent output’. Your test could build the CSS library with the silent flag switched on and assert that the resulting built css file is empty.
Timeline in Chrome’s Dev Tools is really cool. It can help you get all sorts of data from dozens of metrics on the performance health of your web application.
The problem is making Chrome Timeline recordings to me is a bit like the Manual Burn scene in Apollo 13. The minute you hit the red button and move your cursor back to the website all hell breaks loose.
It becomes a race against time to kill the recording before it has filled up with so much information, triggered by so many different events that you lose all hope of hunting down those janks, layout thrashes and performance burps.
No matter how hard I try my timeline recording never look consistent, with a (relatively) clear root cause like this:
Note how in the first few frames the frame rate budget is being burst by scripting, rendering and painting.
Where do you even start?
I want a timeline that looks like this:
Tip: don’t use your mouse when using Timeline profiling. Give elements IDs and trigger the events on them that you want to profile via the console.
The result of doing this gives you a timeline that contains only the data that is relevant to the action you are profiling, leading to easy investigate, reproducible test cases:
This is an improvement but it doesn’t work in all cases – some times you might want to profile, say, a hover state. I’d really like more granular control over what can start Timeline recording. Something like Event Listener Breakpoints (Sources panel of Dev Tools) to choose the sort of events that kick off Timeline recording (to be honest even just a way to stop mouse moves from doing it would be a good start)…
Bonus tips – Keyboard Shortcuts
Cmd/Ctrl + E starts and stops Timeline (and other profilers) recordings but the Dev Tools window must be focused so if you need to use the mouse during profiling (and want to avoid collecting too many extraneous mouse move events) you are probably going to want to switch back and forth with the keyboard.
Unfortunately there’s no keyboard shortcut that I know of to directly switch between Dev Tools on the Timeline and the browser*. If you have Dev Tools undocked you can only use the native OS’s window switching shortcuts (Alt + Tab on Windows, Cmd + ~ on Mac).
* In Chrome 30 if you switch on "Enable Cmd + 1-9 shortcut to switch panels" – the last option in the General tab of Dev Tools settings (click the cog) you can open the timeline with the easy to remember Cmd + Shift + J [release] Cmd + 5.
If you have Dev Tools undocked you can use Cmd + Alt + J to show and hide Dev Tools (and switch what is the focused at the same time), but when Dev Tools closes any Timeline recording in progress will be killed :(.
Another really helpful feature Timeline has is when you hover over Layout events, it will show you the affected region by adding a semi transparent blue rectangle over the affected area on your web page.
If you accidentally hover over anything in the timeline report whilst it’s still recording it’ll still add blue highlighting to the elements affected – and that blue highlighting itself pollutes Timeline’s output:
All the Composite layers events (the green ones) recorded after the ~8 second mark were caused by interacting with Timeline output.
When I’m profiling I’d rather Chrome didn’t do any highlighting at all, but I feel it really, really shouldn’t fill up the report produced Timeline with irrelevant noise that I then have to filter out…
09:50 – 10:30: ES6 Uncensored (slides) Angus Croll – @angustweets
yield that I actually understood. Is it me or does ES6 look a lot like Scala?
Mind still buzzing with
- Control your Lego Mindstorms EV3 Tanks with an xbox controller over bluetooth
11:40 – 12:20: Mobile isn’t a thing, it is everything (slides) Joe McCann – @joemccann
Lots of charts of absurd growth and examples of mobile changing everything, from the most frivolous to the most life-saving and inspirational ways.
- Kinsa – the $1 thermometer and app for your smartphone – was my key wow moment from Joe’s talk
- Uber kitten delivery service
12:20 – 13:00: Pushing the limits of mobile performance (slides) Andrew Grieve – @GrieveAndrew
- Eliminate the 360ms delay on touch screen devices but be aware of the caveats
- Use the HTML5 AppCache, but read Jake Archibald’s post first
- Genius tip: send XHR requests on app start before the code to handle the response has run. Assign the response to a global variable and retrieve later.
14:30 – 15:10: Our web development workflow is completely broken (slides) Kenneth Auchenberg – @auchenberg
I imagine like many I had unquestioningly accepted the fact that when I use Chrome I must use Chrome dev tools; for Safari and iOS I must use Safari’s and so on.
We’ve all been doing it wrong.
— Thomas Parisot (@oncletom) November 8, 2013
Really, really cool demonstrations of what can be done with just CSS (and maths).
— Ben Darlow (@kapowaz) November 8, 2013
16:20 – 17:00: Building with web components using x-tags (slides) Angelina Fabbro – @angelinamagnum
The cutting edge of Web Components from the Mozilla camp.
17:00 – 17:40: Time (slides) Jeremy Keith – @adactio
And Full Frontal 2013 ended on Time – an exploration of the permanence of digital information, the longevity of formats and a brief history of time.
- "The original URL for this prediction (www.longbets.org/601) will no longer be available in eleven years."
- Swiss Fort Knoxx – "forget the Cloud, I want my data stored in a mountain"
- Ruth Belville, the "Greenwich Mean Time Lady"
- The "Powers of 10" video
What an excellent day at the seaside.
Another quick tip. Open all files with uncommitted changes in separate vim tabs:
vi -p `git status --porcelain | cut -c4-`
You can add it as an alias
alias vi-git-status to your
alias vi-git-status='vi -p `git status --porcelain | cut -c4-`'
Can’t believe how long I let this bother me before figuring it out:
Clear search highlighting in vim with
I met with an colleague from a previous company this evening who had gotten back in touch after stumbling across one of the tutorials I had written about the HTML5 History API and AppCache. This, I thought, was quite cool. What was less cool was that my post didn’t actually answer his question, which was:
Do pages visited via the HTML5 History API get automatically added to the Application Cache?
The answer: No, they don’t.
Basically, the AppCache can only store the URLs that are either:
- Listed in the AppCache manifest for the page that was originally loaded from the network.
- Or, are themselves the page that is loaded from the network. (See this wonderfully titled Stack Overflow post: "My HTML5 Application Cache is caching everything")
Note to self – what happens if you programmatically change the manifest attribute on the html tag? (Or set it if it was blank before?). The answer: absolutely nothing.
One of the more frustrating features of OS X when you’re using git that can really trip you up if you’re frequently switching between Linux and OS X is that file names on OS X are not case sensitive. It doesn’t understand the difference between blah and Blah.
This is fine if you always keep those files on you Mac, but usually at some point you’re going to want to move those to another machine. Sometimes that machine will be Linux based (it could be a webserver, for example) and you might like use git to share those files between those machines. At that point if a file’s name has the wrong case, scripts on the new machine will not be able to find those files anymore…
Also on OS X by default git won’t let you just rename a file to the same name with different case – it’ll warn you with a fatal error that the “destination exists”.
Luckily the solution is simple (the hard part is realising it’s the case that’s wrong).
To rename a file with git on OS X to the same name (but with in a different case) just add
src/ws/mattandre/ClassName.java would require the following command:
git mv --force src/ws/mattandre/classname.java src/ws/mattandre/ClassName.java
Based on this experience I have a compiled a list of basic rules for a component to follow.
A well behaved component…
Should not speak unless it is spoken to.
A well behaved component should be an instantiable object that exposes an API, which will enable the parent application to completely control that single instance of that component.
Unless instructed to by the parent application, the component should never search through, read from or write to the DOM.
When the component wants to inform the parent application of an event prefer to do so via the observer pattern. One very light, NPM/bower installable event library is Wilson Page’s event.
Cleans up after itself.
To avoid detached DOM nodes, it should leave no event listener bound to any DOM element (even if that DOM element has gone).
Is not an individual.
It should be able to cope with being instantiated multiple times. It, and none of its sub-components (or sub-sub-components, etc) should be singletons.
Asks politely for the things it needs (or brings them itself).
Similarly if the component is dependent on some CSS, it should expose a route to that CSS (either as pure text or a via URL) and the parent application should be responsible injecting or importing those style rules.
Should not get upset when it’s no longer wanted.
It should expect that it can be destroyed at any time. All callbacks to asynchronous logic should handle the case where the objects or DOM nodes that they were just talking to no longer exist.
Last night I received a text from Chinese low-cost carrier Spring Airlines notifying me that my flight from Shanghai to Beijing had been cancelled.
A quick look on their website seems to suggest all their Shanghai – Beijing flights have been cancelled over the Spring festival period.
Somewhat annoying for me because apparently the refund will take between 2 and 6 months to come back. I guess I should have already learnt this lesson when I lost £500 when one day Oasis Hong Kong, a defunct long-haul budget airline that operated a flights between London, Hong Kong and Vancouver, ceased to exist.
But I wonder if this is an early sign of struggle for the only Chinese low cost carrier.
Update: We got the money back :).