Building an app requires iteration. Photoshop is a good place to start designing, AppCode is a good place to start developing, but when the first prototype is ready, design and development merge: most changes require coding. When the designer wants to adjust colors and fonts, refine interactions, he asks the developer to do it. This slows down the process and makes experimenting prohibitively costly.

When Ilya and I started making Ångström, we decided to create a flexible style engine, so that tinkering with the design would be easy for both of us.

Design considerations

The stylesheets must be stored separately from the code, be easy to read, write and parse. That’s why we use JSON, not Plist or CSS and keep them in a Dropbox.

The styles must reload on demand in the running app. No recompiling, no reinstalling, no restarting. That’s why we have Shake to Refresh.

The app must open fast no matter how long the stylesheet is. Ångström has ended up having hundreds of style rules, bigger apps will require thousands. That’s why we compile styles into binary.

The code related to styles should be easy to maintain. That’s why we do not address the style variables by string literals, i.e. [styler boolForKey:@"isTopBarHidden"]. A typo would end up being a debugging nightmare. Instead, we create an object with the corresponding fields, style.isTopBarHidden, and let the compiler do its job looking for errors.

ÅSS classes have prefix "AS". Example classes are prefixed with "AGR" for "Ångström".

Here I use the AGRStyle class as the root class for all my styles. This is the main Style Object. The last parameter is for automatic saving of the binary cache (serialized AGRStyle instance that is used to speed up the application startup). It is saved during Simulator launch and allows me to be sure that this cache is up to date.

Of course this cache must be updated right after style was changed. But this requires IDE plugin.

This choice allows to create a callback, that will be called when the style is updated. It's a good place to send some a message like [self setNeedsDisplay]; or something that will restyle your view.

After that you can use the style object as a usual Objective-C object.

How can you create this class? When the Style System runs in the Simulator, it can generate all the classes from JSON structure for you and write a ProjectStyles.h/m files with them. Autogeneration can be done with this line of code:

It will generate AGR[CapitalizedStyleName]Style classes for every style rule in the JSON file. For example, a style rule "editor" will generate a class AGREditorStyle, and if the "editor" style rule contains "toolbar" subrule, it will become AGREditorToolbarStyle. Main class will be AGRStyle.

Ideally, the class generation has to happen in the IDE on the fly during JSON editing, but it requires an IDE plugin and some more coding time. I hope to do this in the future.

Stylesheet format

Stylesheets are hierarchical. Any style can be referenced via all it's parent styles like this: superstyle.parentstyle.style.

Styler supports references. If a value looks like "@another.name", then it is replaced with the value of "another.name" style.

Also there is include support. That allows creating large and detailed file with all the styles and smaller, simplier files for remote editing. Here is the example: