Skip to content

Add dark/light theme swapper, make light theme less harsh, and some other small style changes #12565

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Jun 8, 2021

Conversation

dylemma
Copy link
Contributor

@dylemma dylemma commented May 22, 2021

Aims to address #12525

The highlights:

  • Add a dark theme.
    • Added a switch next to the "back to top" link to swap between dark and light mode.
  • Adjusted colors for the default (light) theme.
    • The sidebar is now a much lighter shade of the original color, with dark text/foreground.
    • Use a light shade of gray instead of pure white for the background, and a dark shade of gray instead of pure black for the foreground (text).
    • Use HSL notation for the highlighter colors, so it's easier to tell the difference between the "dark" and "light" versions (for the most part it's a difference of about 15% on the "L" channel)
    • Slightly tweaked the syntax highlighting colors. I thought the orange used for string-interpolated expressions was too bright so I darkened it a tiny bit. Also converted the hljs-related css color constants to variables in order to play nice with theme swapping.
    • Tweaked the appearance of the filtering widget.
      • The buttons now become gray when disabled instead of white.
      • Made the select/deselect button text larger (it was so small as to be unreadable, before). Moved those buttons to be inline with their associated "title" text, which makes things work better when there's less horizontal space to work with.
      • Made the section background the same color as the member backgrounds (gray) instead of the sidebar color, which I think works better for both dark/light themes.
      • Made the > icon match the body text color instead of the blue "active" color since that seemed to work better for both dark/light themes.
  • Cleaned up the border style on the member blocks. If you look closely at the original docs, the colored left border pokes out beyond the top/bottom of the blocks, like you're seeing just the left chunk of a picture frame. That was due to the margin between the blocks being implemented as a border. By using an actual margin to separate the blocks, and only setting border-left, you don't get the little spikes.

Before

image

After

image

Dark

image

Syntax Highlighting Before/After/Dark

image
image
image

I don't claim to be an expert in making choosing colors, but I think this is an improvement. My methodology was to start with the original sidebar color, which was hsl(200deg 100% 14%) aka #003047, keep that hue, and adjust the saturation/lightness values to make sure the overall theme was relatively cohesive.

One caveat about the dark theme is that the footer doesn't swap colors due to image-related difficulties. The "social links" icons are hard-coded to either black or white, and the Scala3doc logo is black, so making the footer dark would just make them harder to see. I briefly experimented with a CSS solution for the social icons, using background-image: url(...), but then realized that the approach wouldn't work with Custom icons (since the background-images needed to be hard-coded in the CSS). Eventually I decided not to have the footer swap from light to dark with the theme. As for the icons in the sidebar, the white icons seem to work just fine in either theme.

For the theme swapper, I'm making it work by toggling a class attribute on the <html> element, and setting up a :root.theme-dark selector in scalastyle.css to override the color variables. I had to make some changes in the "backend" since it was hard-coded to set defer="true" on all scripts in the header, but in order to avoid a very jarring FOUC on page load, where you see the light theme before the script runs and swaps to dark theme, I had to make sure the "theme.js" runs ASAP, by omitting the defer attribute.

@dylemma
Copy link
Contributor Author

dylemma commented May 22, 2021

Paging @TheElectronWill and @pikinier20 as likely reviewers based on the conversation/assignment in the original issue

}

/* This needs to happen ASAP so we don't get a FOUC of bright colors before the dark theme is applied */
const initiallyDark = supportsLocalStorage && (localStorage.getItem("use-dark-theme") === "true");
Copy link
Member

@smarter smarter May 22, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If no theme has been explicitly selected by the user, the default should be dark if the system theme is dark, in CSS this can be queried using prefers-color-scheme: https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks like window.matchMedia('(prefers-color-scheme: dark)') should do the trick in JS.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ooooh that's pretty cool, I didn't know about that feature. I just pushed a commit to add that logic. I wired it up via JS as opposed to CSS since it needs to interact with the switch, and I think a CSS-only solution would require me to duplicate all of the variable settings (one for each media query).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is really nice!

@dylemma dylemma requested a review from smarter May 22, 2021 19:42
@TheElectronWill
Copy link
Contributor

Thanks a lot for the PR!
It's great to have a theme switcher and I like where this is going. 😃

The white footer in the dark theme is a bit troublesome to me. I find it weird because the contrast with the rest of the page is extreme, grabbing visual attention to the footer, which we do not want (or not this much). If all the icons were SVG we could swap their colors easily 🤔

Second point: have you checked the contrast of both versions? I'm worried that the background of the light theme might be too gray/dark (that is, not white enough).

@smarter
Copy link
Member

smarter commented May 25, 2021

If all the icons were SVG we could swap their colors easily thinking

The CSS filter property should work on non-SVG images too: https://dev.to/pqina/poor-man-s-dark-mode-using-css-filter-211n

@dylemma
Copy link
Contributor Author

dylemma commented May 26, 2021

I'll give the CSS invert+hueRotate filter a shot when I next have some time to play with this. Hopefully that'll work out to get the footer to fit the theme.

have you checked the contrast of both versions?

What I'm inferring from this question is that there's some kind of tool for measuring contrast on a page? I'm not familiar with one if that exists. All of this was just me fiddling until I decided I liked it, nothing so scientific. Admittedly I'm kinda biased on this one (I'm the redditor who made the post mentioned in #12525) - I felt there was too much contrast so I aimed to mellow it out a bit. I'll revisit the light theme during daylight hours and see if I can be happy with whiter background.

@TheElectronWill
Copy link
Contributor

Sorry, I should have explained that.
Yes there are some tools to check contrast (and other accessibility things). Firefox and Chrome have an integrated contrast checker in their devtools.

Here are some standard guidelines for contrast on the web.

Idea for another PR: you've mentioned on Reddit that it was difficult to navigate the links in the sidebar without wheel-scrolling. Maybe Mozilla's accessibility doc and the browser's accessibility checker can give useful hints to improve that.

Copy link
Contributor

@pikinier20 pikinier20 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very good job! I'm not eligible to decide about colors, though.

I've seen that social icons are not switched to black/white on theme switch but I think I can help you with that. There's already some logic with socials written in Scala.js and it would be nice to make it work together.

It would be much easier if we rewrote theme.js script to Scala.js but there I've got question for you (maybe my knowledge of JavaScript is not good enough): why did you need to add theme script without deffer attribute? AFAIK deffer doesn't affect execution order. I ask because currently we attach Scala.js output with deffer and I don't know if we should change it.

@dylemma
Copy link
Contributor Author

dylemma commented May 26, 2021

@pikinier20 Thanks!

The reason for not using defer is to avoid a "flash of unstyled content" while things load. The important part of the script is to determine whether or not the user wants the dark theme, and subsequently apply a class attribute to the document root (<html> element). If there is any delay in doing that (i.e. if you use defer), the html and CSS load, the page renders in "light" mode (as that is the default), and then the script runs and switches it to dark mode. It's really jarring if you were expecting a dark page.

The alternative to avoiding defer on that script would be to use pure CSS, but that causes other issues; the switch would need to be removed (which I think is bad because the user shouldn't necessarily have to swap between light and dark at an OS level - the switch is much more convenient). Or if not removing the switch, you'd need to duplicate the CSS rules for light/dark themes based on the :root.theme-dark CSS selector crossed with the (prefers-color-scheme: dark) media query.

Regarding the use of Scala.js, I personally disagree about it being easier. I found that making modifications to the Scala.js parts of scaladoc were the most painful, primarily due to the slow iteration time. It takes 5-10 minutes to compile after making changes (using scaladoc-js/compile or scaladoc/generateScalaDocumentation in SBT), versus manually copying the JS file from the "resources" folder to the corresponding "output" folder which takes mere seconds. I got the impression that the scaladoc-js project was depending on a lot of other things that seemed unnecessary for my purposes, but I couldn't find a more fine-grained command to run.

@TheElectronWill Thanks for the clarification about the contrast stuff. I'll check those out later today.

@smarter
Copy link
Member

smarter commented May 26, 2021

It takes 5-10 minutes to compile after making changes

That seems like way too much, part of the problem is that scaladoc-js/compile calls fullOptJS when it should probably call fastOptJS when we're not publishing a jar to iterate faster, but even with full optimizations I haven't seen it take more than a couple of minutes, maybe you aren't running sbt with a big enough heap?

@smarter
Copy link
Member

smarter commented May 26, 2021

Or maybe you're restarting sbt between every compilation round?

@BarkingBad BarkingBad linked an issue May 26, 2021 that may be closed by this pull request
@pikinier20
Copy link
Contributor

Regarding the use of Scala.js, I personally disagree about it being easier. I found that making modifications to the Scala.js parts of scaladoc were the most painful, primarily due to the slow iteration time. It takes 5-10 minutes to compile after making changes (using scaladoc-js/compile or scaladoc/generateScalaDocumentation in SBT), versus manually copying the JS file from the "resources" folder to the corresponding "output" folder which takes mere seconds. I got the impression that the scaladoc-js project was depending on a lot of other things that seemed unnecessary for my purposes, but I couldn't find a more fine-grained command to run.

Maybe it would be useful to create sbt task in scaladoc project that would update resources - compile JS and copy all scripts, styles etc.

For me it takes like 10-20s to recompile scaladoc-js so it's not very inconvenient.

@dylemma
Copy link
Contributor Author

dylemma commented May 28, 2021

Setting aside the compile time issue, I still think that setting up the theme script as a Scala.js project is a needless complication. As far as I can tell, the entire scaladoc-js project exists for the purpose of generating searchbar.js and its corresponding CSS file, based on this, which I found in project/Build.scala :

Compile / resourceGenerators += Def.task {
  val jsDestinationFile = (Compile / resourceManaged).value / "dotty_res" / "scripts" / "searchbar.js"
  sbt.IO.copyFile((`scaladoc-js` / Compile / fullOptJS).value.data, jsDestinationFile)
  Seq(jsDestinationFile)
}.taskValue,

Since the theme.js needs to not have the defer attribute on it, it seems appropriate to have it separate from the other scripts which seem to be okay with being deferred, which leads me to the conclusion that I should set up another project in SBT whose purpose is to generate theme.js and make sure it gets copied to the appropriate output location. But that seems like overkill especially given that there were other plain-old-js files like ux.js, components/Filter.js, etc already being used.

@TheElectronWill I checked the contrasts via Firefox's tool. This led to a few more minor adjustments (which I just pushed) but nothing major. The only complaints from the tool now are about the grayed-out keywords (e.g. protected and override) that appear in the method signatures, and in the syntax highlighting in light mode. Neither complaint is new (it makes the same complaints about the live version of the site), so I didn't aim to fix those.

The new, dark-mode footer:
image

@dylemma dylemma requested a review from TheElectronWill June 1, 2021 22:35
@smarter
Copy link
Member

smarter commented Jun 7, 2021

@dylemma would you mind rebasing your PR? It looks like there's a conflcit now.
@pikinier20 is anything missing for us to merge this?

@dylemma
Copy link
Contributor Author

dylemma commented Jun 7, 2021

@smarter sure thing. I'll take care of that after work today (I'm GMT-04:00 so that'll be a while). Looks like I need to do the same with my search UX PR too.

@dylemma
Copy link
Contributor Author

dylemma commented Jun 8, 2021

Should be all set now (unless you would prefer a literal git rebase and a force-push? I just did a normal merge and resolved conflicts from there)

@smarter
Copy link
Member

smarter commented Jun 8, 2021

Rebases are preferred since they keep the history of the PR linear.

@dylemma dylemma force-pushed the scaladoc-color-themes branch from c70e7ab to 85574c7 Compare June 8, 2021 12:40
@dylemma
Copy link
Contributor Author

dylemma commented Jun 8, 2021

Done

@smarter smarter assigned pikinier20 and unassigned dylemma Jun 8, 2021
@dylemma
Copy link
Contributor Author

dylemma commented Jun 8, 2021

Gah, something else must've got merged in the meantime so I have some more merge conflicts to figure out.

dylemma added 2 commits June 8, 2021 17:59
Used the "poor man's dark mode" approach to deal with the images in the
footer in dark mode, i.e. invert and hue rotate (without the hue rotate,
the red scala3doc logo becomes blue)

Also fix an issue where if you click "back to top" and then refresh the
page, the "container" element gains the "expand" class, which causes
the main signature (i.e. `class List[A] ...`) to become 'inline' instead
of a block, and it also triggers the 6.5em left-margin that's intended
for method signatures when they get expanded.

Also consolidated the `footer` styles, since they were spread all over.
@dylemma dylemma force-pushed the scaladoc-color-themes branch from 85574c7 to be8fbba Compare June 8, 2021 22:38
Copy link
Member

@smarter smarter left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for this very nice contribution!

@smarter smarter enabled auto-merge June 8, 2021 22:40
@smarter smarter merged commit fdbb94f into scala:master Jun 8, 2021
@Kordyjan Kordyjan added this to the 3.0.2 milestone Aug 2, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Scaladoc colors should works well with dark and light themes
5 participants