Skip to content

Support for Themes

Tim Barham edited this page Oct 14, 2016 · 1 revision

Overview

We need to support accessibility in cordova-simulate. Part of that is supporting high-contrast themes. Also, ideally, cordova-simulate should comply with the current visual style of its host. To enable both these scenarios, we will add support for themes to cordova-simulate.

In order to support different host environments, cordova-simulate won't provide pre-packaged themes. Rather, it will support the host providing a bundle of properties (colors and fonts) that it will apply. It should support providing these properties at startup, and notifying of changes.

Implementation in Cordova-Simulate

Format of Theme Property Bundle

The host will provide a theme to cordova-simulate as a property bundle stored in a JSON file. It will provide the path to the JSON file as a startup option (themefile), and cordova-simulate will provide a method on the Simulation object to change the theme on the fly.

The theme file is organized by element type, then element state (being the name of a state pseudo-class), then a collection of CSS properties that will be applied in that state.

Element types

The following element types are supported:

Element type Description
default Properties defined under default will be applied to any element that doesn't have that property specified.
input Applies to input element such as text, number and select input elements, and textarea elements.
button Applies to buttons.
label Applies to labels.
value Applies to read-only display of values.
panel Applies to the panel itself. Typically, only border and background properties are relevant.
panel-caption Applies to the panel caption. Typically, only font, color and background properties are relevant.

States

Any state can be supplied for any element, though typically only interactive elements would have styling supplied for different states. States should be described using the appropriate pseudo-class. Multiple states may be combined with a colon (so the same properties can be applied to multiple states). An empty string is used for the default state. A simplistic theme file that only styles a button might look like this:

{
  "button": {
    "": {
      "font": "16px normal 'Segoe UI'",
      "color": "black",
      "background": "white",
      "border": "1px solid gray"
    },
    "hover:active": {
      "border": "1px solid dark-gray"
    },
    "active": {
      "background": "gray"
    }
  }
}

Applying Theme Properties

Whenever cordova-simulate needs to apply a theme file (at startup, or when notified of a change), it will dynamically generate a CSS file file from the theme file. The CSS file will contain the following:

  • Properties defined in theme file linked to appropriate selectors
  • Additional properties that may be determined by values defined in the theme file, such as layout (panel width, margins etc) which might be based on font size.

For performance reasons, the generated CSS file will be cached. The cached CSS files will be saved in the cordova-simulate app data directory (jsUtils.getAppDataPath()). The files will be named to make tracking easy:

<sim-host UI renderer ID>-<hash of theme file name>-<hash of theme contents>.css

Where:

  • sim-host UI renderer ID is a unique, 8 character hex id provided by the sim-host UI renderer. This ensures the same CSS file name is never used for different renderers, since they will be incompatible. While it is unlikely the same theme file will be provided to different renderers, this ensures no clash if that does happen.
  • hash of theme file name is the first 8 characters of an MD5 hash of the fully qualified theme file name.
  • hash of theme contents is the first 8 characters of an MD5 hash of the contents of that file.

Look up will work as follows:

  • Generate the target CSS file name using a hash of the theme file name and contents. If an existing file is found, use that and skip the following steps.
  • If no existing file is found, generate the CSS file and save it.
  • See if there are other CSS files with the same file theme file hash and delete them (with the assumption that they have been superseded by the new file)

Performance note: Node has hashing functions implemented in native code that are extremely fast. Hashing the file contents is not expected to have any performance impact.

Breakdown of Responsibilities

Responsibilities of Simulation UI Renderer

A sim-host UI renderer is responsible for the following tasks:

  1. Providing information necessary to generate theme CSS file:

    The sim-host UI renderer is responsible for rendering cordova-simulate custom elements, and applying its own CSS. So the theme CSS we generate will be specific for a particular UI renderer. While most of the work can be done in shared cordova-simulate code, the renderer must provide a way to generate suitable CSS, either by:

    • Providing a template that core cordova-simulate will populate, or
    • Providing a dictionary that maps "element types" to selectors.

    Option 1 provides more flexibility, but option 2 means more of the work is done in shared code. Option 2 is probably preferable (the added flexibility of option 1 is likely unnecessary).

  2. Referencing the theme CSS file:

    The sim-host should refer to sim-host-theme.css in its sim-host.html file, after any other CSS file.

  3. Responding to dynamic theme changes:

    The sim-host may by notified that the theme has changed, at which point it needs to ensure it reloads the theme CSS file.

Responsibilities of Core Cordova-Simulate

Core cordova-simulate code is responsible for the following tasks:

  1. Generating the theme CSS file:

    All the logic described above regarding generating and caching the CSS file.

  2. Serving the theme CSS file:

    The server will track the current theme. When the sim-host requests sim-host-theme.css, it will provide the appropriate generated CSS file.

  3. Responding to theme changes:

    When the host requests a new theme, the server will generate the new CSS file (if necessary) and notify the sim-host of the change (via a websocket message).