From 4ed402f21704a94822abe0e4a0fc065b6f352ed6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Bobbio=20=28Lunar=29?= Date: Thu, 2 Feb 2023 10:57:52 +0100 Subject: [PATCH] Degrade gracefully when JavaScript is disabled MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Support for light/dark themes has been implemented using a `data-theme` attribute set on the `` tag. As this attribute is set using JavaScript, this meant that it was left unset when visitors had JavaScript disabled. This resulted in several important CSS rules not being matched and a “broken feeling” due to wrong colors, and logo or images shown twice. To better support browsers with JavaScript disabled: 1. Add the same CSS rules as for the light theme when the `data-theme` attribute is not set. This creates a safe fallback in every situation. 2. If `default_mode` is set, write its value to the `data-theme` attribute when writing the HTML files. This enables theme users to present their preferred mode to visitors with JavaScript disabled. 3. Use JavaScript to add the search, theme switcher and version switcher interface as they require JavaScript to work. This avoid unusable UI elements to be shown to visitors with JavaScript disabled. 4. Use JavaScript to write the logo for the “other theme”, depending on the value of `default_mode`, defaulting to “light” if unset. 5. Use JavaScript to write the announcement block to the HTML if the announcement is a remote URL. While this last change might seem redundant considering the other ones, it does make the resulting pages better for search engines and text browsers. Closes: pydata/pydata-sphinx-theme#1145 --- .../assets/styles/variables/_color.scss | 100 +++++++++++------- .../components/navbar-logo.html | 12 ++- .../components/search-button.html | 15 ++- .../components/theme-switcher.html | 10 +- .../components/version-switcher.html | 14 ++- .../theme/pydata_sphinx_theme/layout.html | 11 +- .../sections/announcement.html | 7 +- tests/test_build.py | 55 +++++++++- tests/test_build/navbar_switcher.html | 24 +++-- tests/test_build/navbar_theme.html | 23 ++-- tests/test_build/sidebar_subpage.html | 23 ++-- 11 files changed, 195 insertions(+), 99 deletions(-) diff --git a/src/pydata_sphinx_theme/assets/styles/variables/_color.scss b/src/pydata_sphinx_theme/assets/styles/variables/_color.scss index c7b85341c..08ddb4d43 100644 --- a/src/pydata_sphinx_theme/assets/styles/variables/_color.scss +++ b/src/pydata_sphinx_theme/assets/styles/variables/_color.scss @@ -80,53 +80,71 @@ $pst-semantic-colors: ( /******************************************************************************* * write the color rules for each theme (light/dark) -* -* NOTE: @each {...} is like a for-loop -* https://sass-lang.com/documentation/at-rules/control/each -* and #{...} inserts a variable into a CSS selector or property name -* https://sass-lang.com/documentation/interpolation */ -@each $mode in (light, dark) { - html[data-theme="#{$mode}"] { - @each $name, $value in $pst-semantic-colors { - // check if this color is defined differently for light/dark - @if type-of($value) == map { - $value: map-get($value, $mode); - } + +/* NOTE: + * Mixins enable us to reuse the same definitions for the different modes + * https://sass-lang.com/documentation/at-rules/mixin + * #{...} inserts a variable into a CSS selector or property name + * https://sass-lang.com/documentation/interpolation + */ +@mixin theme-colors($mode) { + // check if this color is defined differently for light/dark + @each $name, $value in $pst-semantic-colors { + @if type-of($value) == map { + $value: map-get($value, $mode); + } + & { --pst-color-#{$name}: #{$value}; } - // assign the "duplicate" colors (ones that just reference other variables) + } + // assign the "duplicate" colors (ones that just reference other variables) + & { --pst-color-link: var(--pst-color-primary); --pst-color-link-hover: var(--pst-color-warning); - // adapt to light/dark-specific content - @if $mode == "light" { - .only-dark { - display: none !important; - } - } @else { - .only-light { - display: none !important; - } - /* Adjust images in dark mode (unless they have class .only-dark or - * .dark-light, in which case assume they're already optimized for dark - * mode). - */ - img:not(.only-dark):not(.dark-light) { - filter: brightness(0.8) contrast(1.2); - } - /* Give images a light background in dark mode in case they have - * transparency and black text (unless they have class .only-dark or .dark-light, in - * which case assume they're already optimized for dark mode). - */ - .bd-content img:not(.only-dark):not(.dark-light) { - background: rgb(255, 255, 255); - border-radius: 0.25rem; - } - // MathJax SVG outputs should be filled to same color as text. - .MathJax_SVG * { - fill: var(--pst-color-text-base); - } + } + // adapt to light/dark-specific content + @if $mode == "light" { + .only-dark { + display: none !important; + } + } @else { + .only-light { + display: none !important; + } + /* Adjust images in dark mode (unless they have class .only-dark or + * .dark-light, in which case assume they're already optimized for dark + * mode). + */ + img:not(.only-dark):not(.dark-light) { + filter: brightness(0.8) contrast(1.2); } + /* Give images a light background in dark mode in case they have + * transparency and black text (unless they have class .only-dark or .dark-light, in + * which case assume they're already optimized for dark mode). + */ + .bd-content img:not(.only-dark):not(.dark-light) { + background: rgb(255, 255, 255); + border-radius: 0.25rem; + } + // MathJax SVG outputs should be filled to same color as text. + .MathJax_SVG * { + fill: var(--pst-color-text-base); + } + } +} + +/* Defaults to light mode if data-theme is not set */ +html:not([data-theme]) { + @include theme-colors("light"); +} + +/* NOTE: @each {...} is like a for-loop + * https://sass-lang.com/documentation/at-rules/control/each + */ +@each $mode in (light, dark) { + html[data-theme="#{$mode}"] { + @include theme-colors($mode); } } diff --git a/src/pydata_sphinx_theme/theme/pydata_sphinx_theme/components/navbar-logo.html b/src/pydata_sphinx_theme/theme/pydata_sphinx_theme/components/navbar-logo.html index c96399788..6336e1371 100644 --- a/src/pydata_sphinx_theme/theme/pydata_sphinx_theme/components/navbar-logo.html +++ b/src/pydata_sphinx_theme/theme/pydata_sphinx_theme/components/navbar-logo.html @@ -13,8 +13,16 @@ {% set is_logo = "light" in theme_logo["image_relative"] %} {% set alt = theme_logo.get("alt_text", "Logo image") %} {% if is_logo %} - {{ alt }} - {{ alt }} + {# Theme switching is only available when JavaScript is enabled. + # Thus we should add the extra image using JavaScript, defaulting + # depending on the value of default_mode; and light if unset. + #} + {% if default_mode is undefined %} + {% set default_mode = "light" %} + {% endif %} + {% set js_mode = "light" if default_mode == "dark" else "dark" %} + {{ alt }} + {% endif %} {% if not is_logo or theme_logo.get("text") %}

{{ theme_logo.get("text") or docstitle }}

diff --git a/src/pydata_sphinx_theme/theme/pydata_sphinx_theme/components/search-button.html b/src/pydata_sphinx_theme/theme/pydata_sphinx_theme/components/search-button.html index 13262852b..76fc231f9 100644 --- a/src/pydata_sphinx_theme/theme/pydata_sphinx_theme/components/search-button.html +++ b/src/pydata_sphinx_theme/theme/pydata_sphinx_theme/components/search-button.html @@ -1,4 +1,11 @@ -{# A button that, when clicked, will trigger a search popup overlay #} - +{# A button that, when clicked, will trigger a search popup overlay. + # + # As this function will only work when JavaScript is enabled, we add it through JavaScript. + #} + diff --git a/src/pydata_sphinx_theme/theme/pydata_sphinx_theme/components/theme-switcher.html b/src/pydata_sphinx_theme/theme/pydata_sphinx_theme/components/theme-switcher.html index 65c483166..63c8cd239 100644 --- a/src/pydata_sphinx_theme/theme/pydata_sphinx_theme/components/theme-switcher.html +++ b/src/pydata_sphinx_theme/theme/pydata_sphinx_theme/components/theme-switcher.html @@ -1,5 +1,11 @@ - - - + diff --git a/tests/test_build/navbar_theme.html b/tests/test_build/navbar_theme.html index 61345ebd2..e3dfce411 100644 --- a/tests/test_build/navbar_theme.html +++ b/tests/test_build/navbar_theme.html @@ -1,14 +1,9 @@ - + diff --git a/tests/test_build/sidebar_subpage.html b/tests/test_build/sidebar_subpage.html index 89c4b9d92..ff7fd9910 100644 --- a/tests/test_build/sidebar_subpage.html +++ b/tests/test_build/sidebar_subpage.html @@ -36,20 +36,15 @@