Return to Blog

Brandon Him

instagram/@codewithhim

Creator and Tech Enthusiast

Development

Creating Color Themes for Watchfaces

By Brandon Him
May 10th, 2020

Simplifying the user experience with themes can be a great asset in delivering your watchface design.

However, the Fitbit SDK provides plenty of examples on styling individual elements using document.getElementById, but how would you create preset themes for users without having to coloring individual elements?

The answer is simple, getElementsByClassName.

In this tutorial, we are going to go over how we can utilize this API, and create preset color themes for watchface users.

For brevity, let's create a basic app with a few static texts that we will color:

Start by creating your app, npx create-fitbit-app colorize-tutorial

cli output

Let's create text elements in the resources/index.gui with two seperate class names "label" and "value" and set some basic inline styles -- for simplicity

<svg>
  <rect id="background"/>
  <text class="label" y="10%" text-length="7" fill="black" font-size="32">Label 1</text>
  <text class="value" y="20%" text-length="7" fill="grey" font-size="20">Value 1</text>
  <text class="label" y="50%" text-length="7" fill="black" font-size="32">Label 2</text>
  <text class="value" y="60%" text-length="7" fill="grey" font-size="20">Value 2</text>
</svg>

It should look like such: image-1

Next, let's create a ColorSelect element to represent three themes (warm, cool, and bright):

<ColorSelect
        centered={true}
        settingsKey="theme"
        colors={[
          {color: 'tomato',     value: 'warm'},
          {color: 'deepskyblue',value: 'cool'},
          {color: 'gold',       value: 'bright'}
        ]}
      />

Now we can up the companion to track changes to our settings, which will notify our app.

You'll also want to add logic to track changes in the event if the companion was not running during a settings change, for now, we will just assume things are up and running.

import { settingsStorage } from "settings";
import * as messaging from "messaging";

settingsStorage.onchange = function(evt) {
  if (evt.key === 'theme') {
    if (messaging.peerSocket.readyState === messaging.peerSocket.OPEN) {
      messaging.peerSocket.send({
          key: evt.key,
          value: JSON.parse(evt.newValue)
      });
    } else {
      console.log("No connection");
    }
  }
}

// Add some other logic to handle restores and unopen companions!

Sweet, now let's set up the foundation for listening to these events on our device:

import * as messaging from "messaging";
import document from "document";

messaging.peerSocket.onmessage = function(evt) {
    const data = evt.data;

    if (data.key === 'theme') {
      // Code for changes
    }
}

⭐️: You can use a common file to define constant event keys for easier maintainability

From the code above, we will know when the user is activating changes within the theme setting, let's now create a theme map to store our colors!

So there are many approaches to storing the themes device-side, each with their own trade-offs. However, will use a sample hash map storing a comma delimited string of two hexs as this is pretty straightfoward.

⭐️ Different approaches include nested objects, arrays, storing in i18n file, etc.

const theme = {
    // themeName: 'label hex1,value hex2'
  'warm': '#ff4757,#ff7f50',
  'cool': '#1e90ff,#a4b0be',
  'bright': '#f7b731,#fed330'
};

Next, let's store our label and value elements so we can style them.

let labels = document.getElementByClassName('label');
let values = document.getElementsByClassName('value');

Perfect! Time to combine it all together and watch it in action.

import * as messaging from "messaging";
import document from "document";

let labels = document.getElementsByClassName('label');
let values = document.getElementsByClassName('value');

const theme = {
  // themeName: 'label hex1,value hex2'
  'warm': '#ff4757,#ff7f50',
  'cool': '#1e90ff,#a4b0be',
  'bright': '#f7b731,#fed330'
};

messaging.peerSocket.onmessage = function(evt) {
    const data = evt.data;
    if (data.key === 'theme') {
        const colors = theme[data.value].split(',');

        labels.forEach(element => element.style.fill = colors[0]);
        values.forEach(element => element.style.fill = colors[1]);
    }
}

And here's a quick demo of what we've just created:

demo

Hopefully, you can take that further and deliver even better experiences for your future watchfaces!

You can refer to the GitHub repository, if you need to compare your code. I'm also on the Unofficial Fitbit Developer Discord, just message @brando and say hi.