Jieiku / abridge

Fast & Lightweight Zola Theme
https://abridge.pages.dev/
MIT License
147 stars 41 forks source link

Allow setting default dark/light for the switcher mode without js #125

Closed eugenesvk closed 1 year ago

eugenesvk commented 1 year ago

In the switcher mode when the client disables JS, the mode is always dark since afaik the css rules use the :not(.light) catch-all, which is used in the absence of any code getting/setting the page's light property.

It would be nice if you could instead have proper .light and .dark modes and then add the dark/light styling to the unstyled root depending on a COPY-TO-ROOT-SASS/abridge.scss user editable variable. This way you can still use the switcher as usual, but have a user-customizable fallback

(this could also potentially be useful if you move from a binary set of modes to include, e.g., .sepia)

Jieiku commented 1 year ago

The idea behind using :not(.light) was to have zero unstyled flash of color for anyone that has disabled js or has not played with the switcher button.

Most if not all Zola themes that have a style selector, have at least some Flash of inaccurate color theme. https://css-tricks.com/flash-of-inaccurate-color-theme-fart/

I did the best that I could to minimize this affect for abridge, by loading the tiny theme.js alone as the first file in the Head so that it would be processed as quickly as possible, and also by using the :not(.light) catchall type.

I spent more time than I care to admit working on this initially, as even well known css frameworks that have been around for years seemed to have this issue pretty badly.

EDIT: when JS is disabled, or when you have NOT yet clicked the switcher, you are not actually FORCED into dark mode, you are forced into your OS/Browser preference. You can test this by changing Firefox default mode preference in settings, or for a convenient switch use this firefox plugin: https://addons.mozilla.org/en-US/firefox/addon/theme-switcher-for-firefox/ (after you change your settings, or use the plugin, simply refresh the page.)

I am hesitant to change the way this works, because I want to avoid unstyled flash of color the best that I can.

One idea is to add an additional switcher mode, maybe we could call it selector mode instead, this way we don't have to change the current behavior of the binary switcher.

eugenesvk commented 1 year ago

when JS is disabled, or when you have NOT yet clicked the switcher, you are not actually FORCED into dark mode, you are forced into your OS/Browser preference

that's what I don't get In this abridge.scss code (with exras removed) when you use a swither button, so you set $abridgeMode : "switcher", then you're forced into the dark mode due to it being the only condition that matches without JS, right?

@if $abridgeMode == "auto" {
} @else if $abridgeMode == "dark" {
} @else if $abridgeMode == "light" {
} @else {//switcher (default)
  :root {
    @include font;
    @include root;
  }
  :root:not(.light) { @include colors.dark( $dark... );}
  :root.light       { @include colors.light($light...);}
}

The broswer preferences are only accounted for in the "auto" mode with @media (prefers-color-scheme: light) {, right?

So I just thought instead of this you could do something like

} @else {//switcher (default)
  :root {
    @include font;
    @include root;
    @if $abridgeSwitcherDefaultMode == "light" { @include colors.light($light...);
    } @else {                                    @include colors.dark ($dark... );}}
  :root.dark {                                  @include colors.dark ($dark... );}
  :root.light {                                  @include colors.light($light...);}

then if there is no JS, the is no root modifier set in the storage, so you only get the default root style, which would depend on your user variable

I am hesitant to change the way this works, because I want to avoid unstyled flash of color the best that I can.

I agree that flashes are worse than a dark default without JS

Jieiku commented 1 year ago

ah, your absolutely right.

You are forced into dark mode if JS is disabled.... I worked on this so long ago that I thought I had done it differently.

I am going to see what I can do about this.

I do remember fighting with the flash of color forever, and so that was the main thing that I remembered.

I think one of my earlier ideas will work, so kinda a spin on this.

Jieiku commented 1 year ago

I had an idea to use .switch as the class name instead of .light, then we would just set the default and switch colors appropriately, and still use the catchall :root:not(.switch)

however the js that handles this assumes that the default is dark mode, and then it applies the .light class only if the media query does not match dark preference or if the user has clicked the switch button:

https://github.com/Jieiku/abridge/blob/refactor/static/js/theme.js

This essentially meant that for dark mode there should really be zero color flash, and for light mode I optimized the loading speed by loading it first in the head.

By only using the localstorage for one of the two themes, you ensure that one of the two themes works flawlessly, because local storage never even comes into the picture. (I think at the time I wanted the default to work flawlessly, so I made the default dark mode, and optimized the light mode the best I could)

I will brainstorm this a bit more, there might be a clever way I have not yet thought of. I have one idea but it may increase the size of the resulting css a little, find a way to use the same logic as I did for the "auto" mode but apply some switcher overrides.

Ideally I would like it to work so that localstorage is not even used for whatever the default may be.

eugenesvk commented 1 year ago

By only using the localstorage for one of the two themes, you ensure that one of the two themes works flawlessly, because local storage never even comes into the picture.

But if you have separate themes in the switcher and the default theme in the root, you could still have that, the default will work flawlessly without any JS, and then JS will set a different theme (dark/light/sepia/whatever) that will just override the default

Jieiku commented 1 year ago

Yes your right, but still have to work this out, it works on the basis that dark mode is the default:

https://github.com/Jieiku/abridge/blob/refactor/static/js/theme.js

!window.matchMedia("(prefers-color-scheme: dark)").matches

I will try a few things and see what I can come up with.

Jieiku commented 1 year ago

This is now complete: https://github.com/Jieiku/abridge/commit/cd71559564732e4f2b4752560ff1b6de6f737a79

There are two config values, one in scss to set the default theme, and one in config.toml to load the correct theme.js file.

https://github.com/Jieiku/abridge/blob/cd71559564732e4f2b4752560ff1b6de6f737a79/COPY-TO-ROOT-SASS/abridge.scss#L13

https://github.com/Jieiku/abridge/blob/cd71559564732e4f2b4752560ff1b6de6f737a79/config.toml#L205