Ångström Style System? Use of styles. S2

    What is Ångström Style System?


    In a regular mobile application, the interface is one of the most complex and most important parts. What users see, what customers evaluate first of all, when accepting work.

    Even if the design is drawn before development, in the process of creating the first version, a huge amount of changes still appears. Here, repaint the button, move it here, increase / decrease the font, because it did not fit, and so on. Each of these changes requires refinement. Often there are several hundred or even thousands of such improvements if it is not possible to immediately select the correct parameters.

    In such a situation, it turns out to be very beneficial to separate the design settings from the interface itself and configure it independently. This problem should be solved by a style system, ideologically - like CSS for HTML.

    In the article I will show how exactly I try to organize styles in the application, and what means I use for this.

    The structure of the style system in the application


    When I develop an application, usually I get two layers of style settings. The first layer contains the most basic:

    • application colors
    • application fonts
    • basic styles of application texts,
    • basic parameters. For example, the total radius of the rounding of the corners, or some indentation steps for the grid.

    These are the parameters that permeate the entire application. If suddenly the designer decided that he chose an unsuccessful font and needs to be changed, then you need to change immediately everywhere. This layer is usually quite small and understandable to everyone. You can give it to editors / tuners to designers or even, in especially trusted cases, to the customer.

    From these parameters, in a huge number of variations, a second layer of styles is created. It already describes specific screens, specific interface components. For instance.

    • label time: font number two, color red-three, left alignment,
    • button two: font five, color blue-active, center alignment.

    The files of the second layer are usually voluminous, it is convenient to divide them into several, on screens or sections of the application.

    Advantages of styles in the application


    Styles, especially organized in two layers, very well separate the interface settings from the code . It’s easier to find in several style files where you can configure this font size than to search by hundreds of source codes or huge storyboards.

    If you learn how to load styles from a remote source (from a file in DropBox), then design setup is accelerated hundreds of times with remote development . It’s one thing when a designer can sit down with a developer and try to quickly tune some parameter, and when is it impossible? Each iteration can take hours and days. In this case, I updated the file, restarted the application (or made a tricky secret gesture), looked at the result.

    In some situations, it is convenient to use skins. For book readers, this, for example, is a necessity; it is convenient to read during the day with a light background, at night with a dark background. Sometimes the customer wants it, sometimes by design it is required for a “wow effect”. If you make two options for the first layer of styles, you can switch them, leaving the second layer the same. As a result, skins will be almost free .

    When did you need a style system?


    The need for a style system appeared at once from two sides:

    • An outsourcing company develops many applications. It's nice to have a general scheme of their work so that one developer can correct the code of another. And customers who are constantly changing requirements are also more common than we would like.
    • in the development of their applications, you always want to do "perfect." When we developed Angstrom with Ilya Birman, a design adjustment mechanism was required that would allow Ilya to play with the settings of complex components, such as a custom cursor .

    I prefer to test new ideas on my own projects, and not on customer projects, so Angstrom was lucky and I created a style system for him . It used a JSON file to describe the styles, to which I added the ability to include other files (including "from Dropbox"). The framework also implemented callbacks, which reported that the style had changed. I connected the style update to the shaking iPhone, it turned out fun. Corrected the file in Sublime, saved, shook the iPhone, the styles were uploaded and applied to the entire application.

    The disadvantages of the existing system, that "did not take off"?


    In general, the idea turned out to be so successful that it quickly spread to all of our created applications. In some cases, this allowed a significant reduction in development time (when the customer’s designer, for example, wanted to customize fonts or colors), sometimes the design changed several times from the moment of development to release, almost completely without the participation of the developer (or with minimal involvement, to set up thin moments).

    But I did not like a few points.

    The first is JSON. It constantly turned out that I accidentally forgot a comma or made another stupid mistake in the file, and all styles broke. JSON does not check the names of properties (it’s just strings and it doesn’t matter what is written there), typing is artificially introduced, which also prevents the early detection of errors, and so on.

    The same JSON made me encode data types right in the names. My colors ended with color, dots with center or point, and so on. As a result, the field names were much longer than required.

    After the first attempts to use the style system, it turned out that the most convenient option is when a hierarchy of classes is created using JSON, completely repeating the hierarchy of objects in JSON. A strictly-typed structure then appears in the application, which can be conveniently accessed as regular code. In principle, it was possible to bind UI components to updating the styles, so that the style knew that it applied to this button, and if it suddenly refreshed (a new style was downloaded from Dropbox), the button itself would be updated. But in reality it turned out that such magic only interferes with life, it is better to prescribe update rules explicitly.

    Thirdly, I did not create a console utility that would generate style classes. And the styles themselves were recreated when the application was launched in the simulator. This turned out to be a significant minus, both when connecting styles to the project, and during their subsequent update.

    Objective-C and the need to support older versions of iOS also interfered a bit. The code of the generated class turned out to be big and unpleasant, I would very much like to optimize it.

    S2 - a simpler and more efficient style system


    Having understood the flaws, I tried to improve the system so that it was better suited for the tasks being solved, in all respects:

    • Now the file format is KTV . It supports links, mixins, and types, including the base type color. KTV can read JSON files without changes, so my old styles migrated to the new structure without changes.
    • The style file is created from a ktv file on Swift. At the same time, if you do not use Objective-C support (which is also possible), it turns out to be compact, convenient for viewing and for using a class (hierarchy of nested classes) that do not clog the global namespace of the application.
    • Thanks to the support of types in KTV, it became possible to abandon the suffixes in the names, which simplified the names of properties and classes.
    • A console utility has appeared that can create style classes from a ktv file (or several files).

    For example, I’ll give a (very small) fragment of the original KTV:

    {
        maxWidthForIPad: 600
        darkTheme: false
        defaultFontName: HelveticaNeue-Light
        bolderFontName: HelveticaNeue
        boldFontName: HelveticaNeue-Medium
        defaultSymbolFontName: AngstromSymbols-Light
        bolderSymbolFontName: AngstromSymbols
        basicColors: {
            plateBackground: #edf5f4
            separators: #00407020
        }
        ilya: {
            aboutBackground: @basicColors.plateBackground
            listSeparator: @basicColors.separators
        }
        colors: {
            about: {
                background: @ilya.aboutBackgroundColor
                separator: @ilya.listSeparatorColor
            }
        }    
        about: {
            margins: [0, 0, 0, 0]
            separatorSpacing: 10
            background: @colors.about.backgroundColor
            separator: @colors.about.separatorColor
        }
    }

    And the corresponding fragment of the style file, which turns out:

    let S2 = CONStyle()
    public struct CONStyle: S2Object {
        private static let _rootStyle = S2
        public struct BasicColors: S2Object {
            let separators = UIColor(colorLiteralRed:Float(1.0), green:Float(1.0), blue:Float(1.0), alpha:Float(0.0))
            let plateBackground = UIColor(colorLiteralRed:Float(0.929411764705882), green:Float(0.929411764705882), blue:Float(0.929411764705882), alpha:Float(1.0))
        }
        let basicColors = BasicColors()
        public struct Ilya: S2Object {
            let aboutBackground = _rootStyle.basicColors.plateBackground
            let listSeparator = _rootStyle.basicColors.separators
        }
        let ilya = Ilya()
        public struct Colors: S2Object {
            public struct About: S2Object {
                let background = _rootStyle.ilya.aboutBackgroundColor
                let separator = _rootStyle.ilya.listSeparatorColor
            }
            let about = About()
        }
        public struct About: S2Object {
            let margins = UIEdgeInsets(top:0.0, left:0.0, bottom:0.0, right:0.0)
            let separatorSpacing = Int(10)
            let background = _rootStyle.colors.about.backgroundColor
            let separator = _rootStyle.colors.about.separatorColor
        }
    }

    Of course, using Swift will be inconvenient for fully Objective-C projects. This will drag the Swift-runtime into the project, which can significantly increase the size of the application. But, firstly, it’s not very difficult to write a generator of purely Objective-C code, and secondly, everything is now moving towards the massive use of Swift in applications, so the generated S2 code will be “in the subject”.

    Also popular now: