Skip to content

Instantly share code, notes, and snippets.

@pylixonly
Last active April 4, 2025 08:21
Show Gist options
  • Save pylixonly/6b65af45443d6adc362ebcc3188cb9ce to your computer and use it in GitHub Desktop.
Save pylixonly/6b65af45443d6adc362ebcc3188cb9ce to your computer and use it in GitHub Desktop.
Wintry theme proposal

A persisted store will be used to store

  • Themes/components install metadata and options (id, source URL, date installed, etc.).
  • Currently applied manifest from the last runtime

The store state would look similar to this:

{
    installedThemes: [
        {
            id: "pylixonly.winter",
            sourceUrl: "https://example.com/pylixonly.winter.json",
            etag: "1234567890abcdef",
            installedAt: "2023-10-01T12:00:00Z",
            updatedAt: "2023-10-01T12:00:00Z",
        }
    ],
    installedComponents: [
        {
            id: "pylixonly.winter.palette",
            type: "palette",
            from: "pylixonly.winter", // <- Optional, if not specified, this component is considered standalone, not part of a theme
            etag: "1234567890abcdef",
            sourceUrl: "https://example.com/pylixonly.winter.palette.json",
            installedAt: "2023-10-01T12:00:00Z",
            updatedAt: "2023-10-01T12:00:00Z",
        },
        {
            id: "pylixonly.winter.background",
            type: "background",
            from: "pylixonly.winter",
            etag: "1234567890abcdef",
            sourceUrl: "https://example.com/pylixonly.winter.background.json",
            installedAt: "2023-10-01T12:00:00Z",
            updatedAt: "2023-10-01T12:00:00Z",
        },
        {
            id: "name.solar.json",
            type: "icons",
            sourceUrl: "https://example.com/name.solar.json",
            etag: "1234567890abcdef",
            installedAt: "2023-10-01T12:00:00Z",
            updatedAt: "2023-10-01T12:00:00Z",
        },
        {
            id: "name.ggsans",
            type: "fonts",
            sourceUrl: "https://example.com/name.ggsans.json",
            etag: "1234567890abcdef",
            installedAt: "2023-10-01T12:00:00Z",
            updatedAt: "2023-10-01T12:00:00Z",
        }
    ],
    // Snapshot of the currently applied theme and components from the last runtime.
    // This is stored here because we're storing the installed themes and components in FS and FS is async.
    // Since we need to apply the theme and components synchronously, we need to store the last applied theme and components here.
    activeComponents: {
        palette: {
            // ... The rest of the palette component manifest
        },
        background: {
            // ... The rest of the background component manifest
        },
        icons: {
            // ... The rest of the icons component manifest
        },
        fonts: {
            // ... The rest of the font component manifest
        }
    }
}

The manifests for each themes and components are saved in the file system in a directory, it should be stored as it was fetched from the server:

.
└── themes/
    └── manifests/
        ├── pylixonly.winter.json (Theme)
        ├── pylixonly.winter.palette.json (Palette)
        ├── pylixonly.winter.background.json (Background)
        ├── name.solar.json (Icons)
        └── name.ggsans (Fonts)

Once the app starts, theme components will be applied synchronously from the restored store state. A fetch to the source URL will be made to check for an update. If there is any, components that can be applied during runtime (such as background) will be updated and user will be prompted to update for the fully updated version of the theme.

Note

This proposal is incomplete and is subject to change.

Theme Pack Format

The theme pack format is a flexible way to define and organize different elements of a theme. Each theme is made up of multiple components like colors, backgrounds, icons, and fonts. These components are modular, meaning you can change one without affecting the others, making it easier to tweak your theme exactly how you want it.

The Basics of a Theme Pack

A theme pack is essentially a collection of references to different components that make up the look of an app. Here's how it looks:

{
    "id": "me.my-theme",
    "type": "theme",
    "main": {
        "palette": {
            "source": "https://yada.my/colors.json"
        },
        "background": {
            "source": "https://yada.my/background.json"
        },
        "icons": {
            "source": "https://yada.my/icons.json"
        },
        "fonts": {
            "source": "https://yada.my/font.json"
        }
    }
}

What’s Happening Here?

  • id: This is just the unique name for the theme, so it can be easily identified.
  • type: This tells us it’s a theme pack.
  • main: Inside here, we have references to the main components (colors, background, icons, and fonts), and each of those points to a JSON file (or URL) where the specific data is stored.

Components Breakdown

1. Color Palette

Colors are defined in two parts: raw and semantic.

  • Raw colors: These are specific color values that can be used across the theme.
  • Semantic colors: These are like variables that represent certain UI elements (like buttons or text), so you don’t need to repeat color codes everywhere.
{
    "id": "me.my-theme.palette",
    "type": "palette",
    "main": {
        "base": "dark",
        "raw": {
            "RED_400": "#ff0000",
            "YELLOW_300": "#ffff00",
            "YELLOW_400": "$YELLOW_300"
        },
        "semantic": {
            "BG_BACKDROP": {
                "value": "#000000FF"
            },
            "BADGE_BRAND_BG": {
                "value": "$RED_400"
            },
            "TEXT_PRIMARY": {
                "value": "$YELLOW_300",
                "opacity": 0.8
            }
        },
        "targeted": {
            "unread_badge_color": "$RED_400",
            "message_background_highlight": {
                "mentioned": [
                    "$YELLOW_300",
                    null
                ]
            }
        }
    }
}

Here’s how it works:

  • raw: You define your base colors like RED_400 or YELLOW_300.
  • semantic: These are mappings to UI elements (e.g., TEXT_PRIMARY for text color).
  • targeted: This is a bit more advanced. It lets you adjust specific elements (like the background of a mentioned message) without changing the entire color.

2. Background

Backgrounds are pretty simple—you can set an image, adjust its blur, and set the opacity.

{
    "id": "me.my-theme.background",
    "type": "background",
    "main": {
        "image": "https://yada.my/bg.jpg",
        "blur": 0.3,
        "opacity": 0.5
    }
}
  • image: Link to the background image.
  • blur: Adjust the blur (0 = no blur, 1 = full blur).
  • opacity: Adjust how see-through the background is (0 = fully transparent, 1 = fully opaque).

3. Icon Pack

With icon packs, you define where to get your icons from (a URL).

{
    "id": "me.my-icon-pack",
    "type": "icons",
    "main": {
        "paths": {
            "*": "https://example.com/solar-icons/{path}",
            "../../../rn/arrow-back.png": "https://example.com/solar-icons/_/arrow-back.png"
        }
    }
}
  • paths: Defines the URL pattern for where to fetch icons from.

4. Font Pack

Font packs let you link to custom fonts that will be used in the theme. You can define the fonts you want to include and specify where to fetch them from.

{
    "id": "me.my-font-pack",
    "type": "fonts",
    "main": {
        "include": [
            "ABCGintoNord-*",
            "ggsans-*",
            "NotoSans-*",
            "ggmono-*"
        ],
        "link": "https://my-website.com/my-font-pack/{name}.ttf"
    }
}
  • include: A list of font names or patterns to include.
  • link: Where to get the font files (in this case, .ttf files from a URL).

Key changes

  • Flexibility: You can swap out colors, backgrounds, icons, and fonts without changing the whole theme. Just update the specific part you want to change.
  • Extensibility: You can add more components down the line, in case there's any in the future.
  • Customizable: The targeted option allows for more fine-tuned control, letting you tweak specific UI elements without affecting the entire color palette.

Final Thoughts

This system is designed to make theme management more customizable. You can mix and match components, make theme changes quickly, and keep everything modular.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment