Skip to content

About

The repo for themes used in HomeGlow.

Resources

Stars

1 star

Watchers

0 watching

Forks

Latest commit

 

History

23 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Theme Development Guide

How to make a HomeGlow theme: a folder of data (a manifest, pictures and fonts) that recolors and reshapes the dashboard and can animate a scene behind it. Themes never contain code, so they are safe to share.

Related reading:

1. The folder

aurora/
  theme.json          the manifest
  assets/             pictures: .svg, .png, .webp or .jpg
  fonts/              .woff2 files, with their license

Install the folder with Add a theme folder (§7) and choose the theme under Admin → Look → Appearance. To build a theme into the app instead, put the folder in client/src/themes/; HomeGlow finds it on the next build. The folder name must match the manifest's id.

2. The manifest

{
  "manifestVersion": 1,
  "id": "aurora",
  "name": "Aurora",
  "version": "1.0.0",
  "author": "Your name",
  "description": "Northern lights over a dark sky.",
  "extends": "classic",
  "modes": ["dark"],
  "variety": "load",
  "colors": { "primary": "#05070f", "secondary": "#7ff5c8", "accent": "#7ff5c8" },
  "fonts": [{ "family": "Aurora Sans", "weight": 400, "src": "fonts/aurora-400.woff2" }],
  "tokens": { "all": {}, "light": {}, "dark": {} },
  "mui": {},
  "ambience": []
}
Field Required Meaning
manifestVersion yes 1, or 2 for a theme that uses ornaments, the meter tokens or clumps. A HomeGlow older than the version refuses the theme.
id yes Lowercase slug (a-z, 0-9, hyphens), the same as the folder name.
name yes Shown in the theme picker.
version, author, description As in a plugin manifest. The author shows as "by ...".
extends Another theme's id. Its tokens, colors and MUI options apply first, then yours. Most themes extend classic.
modes ["light", "dark"] by default. A dark-only theme lists ["dark"] and shows dark whatever the display's mode.
variety How often the scene behind the widgets changes: load (each page load, the default), day (every display shows the same scene, and it changes daily) or fixed.
colors primary, secondary and accent as hex. Plugins are told these.
fonts .woff2 files in your fonts/ folder: family, weight (100 to 900), optional style (normal or italic), src. A font downloads only when text uses it.
tokens Values for the CSS custom properties, in all, light and dark. See §3.
mui, muiModes Options for buttons, inputs, dialogs and sliders; muiModes.light and .dark override per mode. See utils/themes.js, MUI_TYPES.
ambience Layers drawn behind the widgets. See §4.
ornaments Pictures drawn on every widget frame. See §4a.
confetti The theme's own celebration confetti: colors (a list, or per mode), shapes (some of square, circle, streamer), pictures (as in a sprites layer, height in px) and mix (the share of pieces that are pictures, 0 to 1). Without it, a theme gets Classic's confetti.

3. Tokens

Every token has a type, and a value that doesn't fit is rejected. Values may never contain ;, braces, url( or similar, so a theme can't inject a stylesheet. The full list is THEME_TOKENS in client/src/utils/themes.js; the most useful:

Token Type What it sets
--background, --surface, --text, --text-secondary, --border color The page and its text
--accent, --accent-rgb color, r, g, b Highlights
--hg-page-image gradient An image over the page background
--hg-frame-bg, --hg-frame-radius, --hg-frame-shadow color, lengths, shadow Each widget's frame
--hg-frame-decoration-width, -style, -color lengths, keyword, up to four colors A border drawn over the frame's edge (Starship's elbows)
--hg-frame-inset 0 to 24px a side Room inside the frame for its decoration and ornaments; widget content, plugins included, sits inside it
--hg-frame-image, --hg-frame-overlay gradient A tint under the frame's content, a reflection over it (Reef's glass)
--dock-bg, --dock-active-bg, --dock-active-image color, gradient The dock
--hg-font-body, --hg-font-heading font list Type
--hg-grid-gap 0, 8px, 16px or 24px The gap between widgets; rows keep their pitch
--hg-meter-track, --hg-meter-fill color The empty and filled parts of anything showing a level or progress
--hg-meter-thickness, --hg-meter-cap 1px to 12px; round, square or butt A meter's line and its ends

Themes never change spacing, font size or line height: widget heights are fixed, and content must still fit.

4. Ambience: a scene behind the widgets

ambience is a list of layers, drawn in order (later ones on top). Each names a building block in layer, may limit itself to modes, and sets that block's options. Numbers may be a range, [low, high]: HomeGlow picks a value for each item, so with variety set to load or day the scene varies.

Pictures (src) are files in your assets/ folder. A picture can differ by mode, { "light": "assets/a-day.svg", "dark": "assets/a-night.svg" }, and so can a tint. A one-color silhouette can be recolored with tint, so one file serves both modes; give multi-color pictures one file per mode.

A sprites layer can mix kinds of thing with pictures: a list of { src, aspect, height, tint, weight }. Each copy picks one, by weight, and taller copies are drawn behind shorter ones. Reef scatters eight kinds of coral this way, still ("motion": "none") and randomly mirrored ("flip": true), so every load is a different reef.

Any layer can also set chance, from 0 to 1: how likely it is to be in a scene at all. Starship's planets have a chance of 0.75 and its galaxies 0.6, so some nights there is a ringed planet and the Milky Way, and some nights only stars.

Layer Draws Options
image A still picture, or one of a list src (a picture or a list), anchor (bottom, top, center, fill), height (e.g. "42vh"), angle (turns it, growing it to cover the screen), flip, opacity
sprites Copies of a picture, or a mix of pictures, spread across the screen src or pictures, count, height (vh), aspect (width ÷ height), span ([from, to] %), base (distance from the bottom), clumps and clumpWidth (gather copies into that many patches, each that many percent wide, with open ground between, as plants grow), lift (percent of the screen above base, drawn per copy), hue (degrees to turn each copy's colors), motion (sway, bob, pulse, none), angle (sway degrees) or distance (bob px), seconds, tint, flip, current, opacity
particles Many small things src or colors (plain dots), motion (rise, fall, twinkle), count, size (px), seconds, drift (px), span, opacity
dots A still field of dots, painted once count, colors, size, opacity. Each dot's size and opacity follow one brightness, mostly dim, so dim dots are small; the brightest get a halo.
field Drifting light (caustics) strength (soft, bright), spacing, seconds
streaks An occasional streak on a random path color, length, every (pause, seconds), seconds, burst (streaks per pass: a meteor storm)
flyby Now and then, one picture crossing the screen pictures, height (vh), every (pause, seconds), seconds (to cross), span ([from, to] % from the top), tilt (degrees of climb), spin (degrees of tumble per crossing), opacity. Draw pictures facing right.
blobs Soft color blobs drifting and swelling colors, count, size (vmin), seconds, opacity

colors and tint take a list, or { "color": "#...", "weight": 3 } entries for a weighted pick.

Each sprite moves on two periods that don't divide evenly, so its motion never quite repeats. With "current": true, a slow third motion rolls across the sprites like a passing current.

Limits: 12 layers, 40 particles and 16 sprites per layer, 400 dots, durations of 1 to 120 seconds (a flyby's pause, up to an hour).

4a. Ornaments: pictures on every frame

ornaments is a list (at most 8) of pictures from your assets/ folder, drawn on every widget frame, native and plugin alike, above the frame and under its decoration. They are still, take no pointer events, and are clipped to the frame's shape.

Option Meaning
src A picture, or { "light", "dark" } pictures
anchor top-left, top, top-right, right, bottom-right, bottom, bottom-left, left, center, or corners: one picture drawn for the top-left corner, mirrored into all four
size Height of the picture (an edge picture's thickness), e.g. "28px"; default 24px
aspect Width ÷ height, default 1
stretch For an edge: run the picture along the whole edge
tint Recolor a one-color silhouette, as for sprites; may differ by mode
opacity 0 to 1

Ornaments take no space. Make room for them with --hg-frame-inset, at most 24px a side, since every pixel comes out of a fixed-height widget. Keep each ornament inside that room, or it will sit over the widget's content.

"ornaments": [
  { "src": "assets/bezel.svg", "anchor": "corners", "size": "26px", "tint": { "light": ["#0b6e8a"], "dark": ["#4fe3d6"] } }
]

5. Performance rules

A wall display can be a Raspberry Pi. The engine keeps every layer to these rules, and a theme should not fight them:

  • Only transform and opacity animate.
  • Nothing animates for someone who prefers reduced motion, and nothing renders while the photo screensaver covers the dashboard.
  • No live blur (backdrop-filter) over a moving scene, and no full-screen blend modes: either one costs a slow GPU most of its frames. Reef's glass is a still tint and highlight instead.
  • Check your theme in Chromium with the CPU slowed 4× (DevTools, Performance, CPU throttling). It should hold about 60 frames a second.

6. Checking a theme

validateThemePackage(manifest, { assets }) in client/src/utils/themes.js reports every problem as a readable sentence, and npm test runs it over every theme folder. Then pick the theme in Admin and look at it in light and dark, on a desktop and a phone width.

7. Installing and sharing a theme

HomeGlow builds in only Classic. Other themes are installed in Admin → Look → Themes:

  • Get themes lists the themes repository (jherforth/HomeGlowThemes), one folder per theme at its root, named by the theme's id. A preview.png beside theme.json shows in the list.
  • Add a theme folder installs a folder from your computer. The admin page runs validateThemePackage before sending it, and shows what's wrong.

Installed themes are kept by the server and offered to every display. Each display runs the same check before using one, so a theme that fails is listed as unusable and never partly applied. Files other than theme.json, assets/ and fonts/ (a README, a preview) are ignored. SVGs may not carry script. A theme may hold 64 files, 2 MB each, 8 MB in all.

An installed theme with a built-in theme's id replaces it, except Classic. To share a theme, open a pull request adding its folder to the themes repository.

8. Examples

All are in the themes repository.

  • Starship (starship/) layers a sometimes-galaxy (image, picked from three, at a random angle, with a chance), a starfield in stellar colors (dots), up to three planets (sprites with lift and hue), twinkles, shooting stars and meteor storms (streaks with burst), and two flyby layers: five kinds of ship, and the occasional tumbling alien. Its confetti mixes tinted sparkles, tiny ships and an alien into LCARS-colored streamers; Reef's throws fish, starfish, shells and bubbles.
  • Reef (reef/) uses six layers: field caustics, particles bubbles from bubble.svg, a sea floor per mode (image), a still sprites layer scattering eight kinds of coral, swaying tinted kelp with a current, and a swaying mix of fans, whips and anemones. Every load is a new reef.
  • Spring, Summer, Autumn and Winter (spring/, summer/, autumn/, winter/) are the simplest kind of theme: no pictures or fonts, only tokens and two blobs layers per mode, a few large slow washes of seasonal color under smaller, quicker ones. Each has a light and a dark palette, and confetti in the season's colors.

About

The repo for themes used in HomeGlow.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors