AmoebeLabs / swiss-army-knife-card

The versatile custom Swiss Army Knife card for Home Assistant allows you to create your unique visualization using several graphical tools, styling options and animations.
232 stars 19 forks source link

Generic Dark / Light theme implementation #80

Closed AmoebeLabs closed 1 year ago

AmoebeLabs commented 2 years ago

The Problem To Be Solved

Home Assistant supports themes with separate dark and light settings. HA also responds to dark/light system settings using the window.matchMedia('(prefers-color-scheme: dark)') media query to switch themes.

Therefore, all the theme CSS settings will change automatically: nothing needs to be done by the card itself.

However: in some cases, when the card uses its own colorstops/lists, some of those colors might conflict with a dark or light color scheme: it would be nice if this can be adjusted to the currently active mode of the theme!

As this issue proposes a generic implementation, all places where styles are used should be able to extend the styling with specific dark and light mode support.

Additional background:

HA responds itself to the media query change, but can also override the dark/light setting manually.

Additionally, HA makes some of the settings & configuration of the theme available through the hass property which is set using the set hass() function call.

The hass object has the following properties for themes:

  themes:
    darkMode: true/false
    theme: <name of active theme>
    themes:
      - <name of active theme>
        modes:
          dark:
          light:

Related Issues

There are a few related issues, that can be closed as this issue supersedes them:

(Optional): Suggested Solution

YAML definitions

The generic solution extends (not breaks) the current style and color implementation. It uses the same approach as Home Assistant has done by extending its themes with a dark and light mode:

  <some style definition>
  <another style definition>
  modes:
    light:
    dark:

So, the card itself should recognize the modes: extension, and - depending on the theme mode - should add or overwrite the default style definitions.

The change in mode seems to be notified directly using the hass object update, so any card can respond to this change and act upon that change.

Below is a color swatch example (OLD).

color_swatch_rgb:
  template:
    type: colorswatch
  colorswatch:
    colorswatch: color_palette_default_gradient_white_orange
    colors:
      --sak-cs-default-text: 'var(--primary-text-color)'
      --sak-cs-default-background: 'var(--primary-background-color)'
      modes:
        light:
          --sak-cs-default-01: 'darkred'
          --sak-cs-default-02: 'darkgreen'
          --sak-cs-default-03: 'darkblue'
        dark:
          --sak-cs-default-01: 'red'
          --sak-cs-default-02: 'green'
          --sak-cs-default-03: 'blue'

@2023.05.17 Below is a color swatch example (NEW). This example is consistent with the theme definitions HA uses:

color_swatch_rgb:
  template:
    type: colorswatch
  colorswatch:
    color_palette_default_gradient_white_orange:
      sak-cs-default-text: 'var(--primary-text-color)'
      sak-cs-default-background: 'var(--primary-background-color)'
      modes:
        light:
          sak-cs-default-01: 'darkred'
          sak-cs-default-02: 'darkgreen'
          sak-cs-default-03: 'darkblue'
        dark:
          sak-cs-default-01: 'red'
          sak-cs-default-02: 'green'
          sak-cs-default-03: 'blue'

Hmmm. Would the template still work this way?

  swatchpalette:
    color_swatch_rgb:
      template:
        name: color_swatch_rgb

Eventually, it should look like this:
```yaml
  swatchpalette:
    default_swatch:
      modes:
        light:
        dark:

      whatever_swatch:
      ...

And I think using templates would lead to this declaration, which won't work, as it overwrites the swatch definition each time. Hmmmmm:

  swatchpalette:
    swatch:
      default_swatch:
        modes:

   swatch:
     whatever_swatch:
       modes:

This can also be applied to the styles section of each tool:

  - type: line
    position:
      cx: 50
      cy: 50
      orientation: vertical
      length: 50
    styles:
      line:
        fill: var(--primary-text-color)
        opacity: 0.5
        modes:
          light:
            opacity: 0.8

The extended colorstop/lists can also use this generic extension!

And last but not least: the animations section can also be extended with this solution.

Implementation

Extending the styles section means that the current direct use of styleMaps() and classMaps() should be changed as those maps won't handle the modes part automagically 😄 . Of course, those maps will still be used, but they need extra calculations to process the theme mode settings.

Start simple with testing for a single tool's styles section, extend to classes and animations (for that tool), and then test with multiple tools.

Although the processing should be generic, each tool should be changed because of the use of styleMaps() and classMaps().

AmoebeLabs commented 1 year ago

Closed with #218