feat: add option to set default theme and hide toggle button (#146)

resolves #135 

Light / dark theme can be configured via:

```yaml
  theme:
    # light | dark | system
    default: system
    displayToggle: true
```
This commit is contained in:
Xin 2023-10-21 17:18:04 -04:00 committed by GitHub
parent 93cb788e52
commit 97e6945c04
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 114 additions and 58 deletions

View File

@ -1,40 +1,51 @@
// Dark theme toggle // Light / Dark theme toggle
(function () {
const defaultTheme = '{{ site.Params.theme.default | default `system`}}'
const themeToggleButtons = document.querySelectorAll(".theme-toggle"); const themeToggleButtons = document.querySelectorAll(".theme-toggle");
// Change the icons inside the button based on previous settings // Change the icons of the buttons based on previous settings or system theme
if ( if (
localStorage.getItem("color-theme") === "dark" || localStorage.getItem("color-theme") === "dark" ||
(!("color-theme" in localStorage) && window.matchMedia("(prefers-color-scheme: dark)").matches) (!("color-theme" in localStorage) &&
) { ((window.matchMedia("(prefers-color-scheme: dark)").matches && defaultTheme === "system") || defaultTheme === "dark"))
themeToggleButtons.forEach((el) => el.dataset.theme = "dark"); ) {
} else { themeToggleButtons.forEach((el) => el.dataset.theme = "dark");
themeToggleButtons.forEach((el) => el.dataset.theme = "light"); } else {
} themeToggleButtons.forEach((el) => el.dataset.theme = "light");
}
themeToggleButtons.forEach((el) => { // Add click event handler to the buttons
el.addEventListener("click", function () { themeToggleButtons.forEach((el) => {
if (localStorage.getItem("color-theme")) { el.addEventListener("click", function () {
if (localStorage.getItem("color-theme") === "light") { if (localStorage.getItem("color-theme")) {
document.documentElement.classList.add("dark"); if (localStorage.getItem("color-theme") === "light") {
document.documentElement.style.colorScheme = "dark"; setDarkTheme();
localStorage.setItem("color-theme", "dark"); localStorage.setItem("color-theme", "dark");
} else {
setLightTheme();
localStorage.setItem("color-theme", "light");
}
} else { } else {
document.documentElement.classList.remove("dark"); if (document.documentElement.classList.contains("dark")) {
document.documentElement.style.colorScheme = "light"; setLightTheme();
localStorage.setItem("color-theme", "light"); localStorage.setItem("color-theme", "light");
} else {
setDarkTheme();
localStorage.setItem("color-theme", "dark");
}
} }
} else { el.dataset.theme = document.documentElement.classList.contains("dark") ? "dark" : "light";
if (document.documentElement.classList.contains("dark")) { });
document.documentElement.classList.remove("dark");
document.documentElement.style.colorScheme = "light";
localStorage.setItem("color-theme", "light");
} else {
document.documentElement.classList.add("dark");
document.documentElement.style.colorScheme = "dark";
localStorage.setItem("color-theme", "dark");
}
}
el.dataset.theme = document.documentElement.classList.contains("dark") ? "dark" : "light";
}); });
});
// Listen for system theme changes
window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change", (e) => {
if (defaultTheme === "system" && !("color-theme" in localStorage)) {
e.matches ? setDarkTheme() : setLightTheme();
themeToggleButtons.forEach((el) =>
el.dataset.theme = document.documentElement.classList.contains("dark") ? "dark" : "light"
);
}
});
})();

View File

@ -5,7 +5,7 @@ weight: 2
Hugo reads its configuration from `hugo.yaml` in the root of your Hugo site. Hugo reads its configuration from `hugo.yaml` in the root of your Hugo site.
The config file is where you can configure all aspects of your site. The config file is where you can configure all aspects of your site.
You can find the config file for this site in `exampleSite/hugo.yaml` as a good starting point. Check out the config file for this site [`exampleSite/hugo.yaml`](https://github.com/imfing/hextra/blob/main/exampleSite/hugo.yaml) on GitHub to get a comprehensive idea of available settings and best practices.
<!--more--> <!--more-->
@ -181,6 +181,26 @@ Include both `favicon.ico` and `favicon.svg` files in your project to ensure you
While `favicon.ico` is generally for older browsers, `favicon.svg` is supported by modern ones. The optional `favicon-dark.svg` can be included for a tailored experience in dark mode. While `favicon.ico` is generally for older browsers, `favicon.svg` is supported by modern ones. The optional `favicon-dark.svg` can be included for a tailored experience in dark mode.
Feel free to use tools like [favicon.io](https://favicon.io/) or [favycon](https://github.com/ruisaraiva19/favycon) to generate these icons. Feel free to use tools like [favicon.io](https://favicon.io/) or [favycon](https://github.com/ruisaraiva19/favycon) to generate these icons.
### Theme Configuration
Use the `theme` setting to configure the default theme mode and toggle button, allowing visitors to switch between light or dark mode.
```yaml {filename="hugo.yaml"}
params:
theme:
# light | dark | system
default: system
displayToggle: true
```
Options for `theme.default`:
- `light` - always use light mode
- `dark` - always use dark mode
- `system` - sync with the operating system setting (default)
The `theme.displayToggle` parameter allows you to display a toggle button for changing themes.
When set to `true`, visitors can switch between light or dark mode, overriding the default setting.
### Page Width ### Page Width
@ -193,8 +213,7 @@ params:
width: wide width: wide
``` ```
There are three available options: `full`, `wide`, and `normal`. There are three available options: `full`, `wide`, and `normal`. By default, the page width is set to `normal`.
By default, the page width is set to `normal`.
Similarly, the width of the navbar and footer can be customized by the `params.navbar.width` and `params.footer.width` parameters. Similarly, the width of the navbar and footer can be customized by the `params.navbar.width` and `params.footer.width` parameters.
@ -215,7 +234,8 @@ params:
index: content index: content
``` ```
available options for `flexsearch.index`: Options for `flexsearch.index`:
- `content` - full content of the page (default) - `content` - full content of the page (default)
- `summary` - summary of the page, see [Hugo Content Summaries](https://gohugo.io/content-management/summaries/) for more details - `summary` - summary of the page, see [Hugo Content Summaries](https://gohugo.io/content-management/summaries/) for more details
- `heading` - level 1 and level 2 headings - `heading` - level 1 and level 2 headings

View File

@ -109,6 +109,11 @@ params:
# full (100%), wide (90rem), normal (1280px) # full (100%), wide (90rem), normal (1280px)
width: normal width: normal
theme:
# light | dark | system
default: system
displayToggle: true
footer: footer:
displayCopyright: true displayCopyright: true
displayPoweredBy: true displayPoweredBy: true

View File

@ -1,4 +1,5 @@
{{- $enableFooterSwitches := .Scratch.Get "enableFooterSwitches" | default false -}} {{- $enableFooterSwitches := .Scratch.Get "enableFooterSwitches" | default false -}}
{{- $displayThemeToggle := site.Params.theme.displayToggle | default true -}}
{{- $copyright := (T "copyright") | default "© 2023 Hextra." -}} {{- $copyright := (T "copyright") | default "© 2023 Hextra." -}}
@ -11,16 +12,19 @@
{{ end -}} {{ end -}}
{{- end -}} {{- end -}}
<footer class="hextra-footer bg-gray-100 pb-[env(safe-area-inset-bottom)] dark:bg-neutral-900 print:bg-transparent"> <footer class="hextra-footer bg-gray-100 pb-[env(safe-area-inset-bottom)] dark:bg-neutral-900 print:bg-transparent">
{{- if $enableFooterSwitches }} {{- if $enableFooterSwitches -}}
<div class='mx-auto flex gap-2 py-2 px-4 {{ $footerWidth }}'> <div class="mx-auto flex gap-2 py-2 px-4 {{ $footerWidth }}">
{{- partial "language-switch.html" (dict "context" .) -}} {{- partial "language-switch.html" (dict "context" .) -}}
{{- partial "theme-toggle.html" -}} {{- with $displayThemeToggle }}{{ partial "theme-toggle.html" }}{{ end -}}
</div> </div>
{{ end -}} {{- if or site.IsMultiLingual $displayThemeToggle -}}
<hr class="dark:border-neutral-800" /> <hr class="dark:border-neutral-800" />
{{- end -}}
{{- end -}}
<div <div
class='{{ $footerWidth }} mx-auto flex justify-center py-12 pl-[max(env(safe-area-inset-left),1.5rem)] pr-[max(env(safe-area-inset-right),1.5rem)] text-gray-600 dark:text-gray-400 md:justify-start' class="{{ $footerWidth }} mx-auto flex justify-center py-12 pl-[max(env(safe-area-inset-left),1.5rem)] pr-[max(env(safe-area-inset-right),1.5rem)] text-gray-600 dark:text-gray-400 md:justify-start"
> >
<div class="flex w-full flex-col items-center sm:items-start"> <div class="flex w-full flex-col items-center sm:items-start">
{{- if (.Site.Params.footer.displayPoweredBy | default true) }}<div class="font-semibold">{{ template "theme-credit" . }}</div>{{ end }} {{- if (.Site.Params.footer.displayPoweredBy | default true) }}<div class="font-semibold">{{ template "theme-credit" . }}</div>{{ end }}

View File

@ -33,13 +33,25 @@
<script> <script>
/* Initialize light/dark mode */ /* Initialize light/dark mode */
if (localStorage.getItem("color-theme") === "dark" || (!("color-theme" in localStorage) && window.matchMedia("(prefers-color-scheme: dark)").matches)) { const defaultTheme = '{{ site.Params.theme.default | default `system`}}';
const setDarkTheme = () => {
document.documentElement.classList.add("dark"); document.documentElement.classList.add("dark");
document.documentElement.style.colorScheme = "dark"; document.documentElement.style.colorScheme = "dark";
} else { }
const setLightTheme = () => {
document.documentElement.classList.remove("dark"); document.documentElement.classList.remove("dark");
document.documentElement.style.colorScheme = "light"; document.documentElement.style.colorScheme = "light";
} }
if ("color-theme" in localStorage) {
localStorage.getItem("color-theme") === "dark" ? setDarkTheme() : setLightTheme();
} else {
defaultTheme === "dark" ? setDarkTheme() : setLightTheme();
if (defaultTheme === "system") {
window.matchMedia("(prefers-color-scheme: dark)").matches ? setDarkTheme() : setLightTheme();
}
}
</script> </script>
{{ partial "custom/head-end.html" . }} {{ partial "custom/head-end.html" . }}

View File

@ -1,4 +1,4 @@
{{- $jsTheme := resources.Get "js/theme.js" -}} {{- $jsTheme := resources.Get "js/theme.js" | resources.ExecuteAsTemplate "theme.js" . -}}
{{- $jsMenu := resources.Get "js/menu.js" -}} {{- $jsMenu := resources.Get "js/menu.js" -}}
{{- $jsTabs := resources.Get "js/tabs.js" -}} {{- $jsTabs := resources.Get "js/tabs.js" -}}
{{- $jsLang := resources.Get "js/lang.js" -}} {{- $jsLang := resources.Get "js/lang.js" -}}

View File

@ -34,17 +34,21 @@
{{ end -}} {{ end -}}
</div> </div>
{{/* Hide theme switch when sidebar is disabled */}} {{/* Hide theme switch when sidebar is disabled */}}
{{ $switchesClass := cond $disableSidebar "md:hidden" "" }} {{ $switchesClass := cond $disableSidebar "md:hidden" "" -}}
<div class="{{ $switchesClass }} {{ with site.IsMultiLingual }}justify-end{{ end }} sticky bottom-0 bg-white dark:bg-dark mx-4 py-4 shadow-[0_-12px_16px_#fff] flex items-center gap-2 dark:border-neutral-800 dark:shadow-[0_-12px_16px_#111] contrast-more:border-neutral-400 contrast-more:shadow-none contrast-more:dark:shadow-none border-t" data-toggle-animation="show"> {{ $displayThemeToggle := (site.Params.theme.displayToggle | default true) -}}
{{- with site.IsMultiLingual }}
{{ partial "language-switch" (dict "context" $context "grow" true) }} {{ if or site.IsMultiLingual $displayThemeToggle }}
{{ partial "theme-toggle" (dict "hideLabel" true) }} <div class="{{ $switchesClass }} {{ with site.IsMultiLingual }}justify-end{{ end }} sticky bottom-0 bg-white dark:bg-dark mx-4 py-4 shadow-[0_-12px_16px_#fff] flex items-center gap-2 dark:border-neutral-800 dark:shadow-[0_-12px_16px_#111] contrast-more:border-neutral-400 contrast-more:shadow-none contrast-more:dark:shadow-none border-t" data-toggle-animation="show">
{{ else }} {{- with site.IsMultiLingual -}}
<div class="flex grow flex-col"> {{- partial "language-switch" (dict "context" $context "grow" true) -}}
{{ partial "theme-toggle" }} {{- with $displayThemeToggle }}{{ partial "theme-toggle" (dict "hideLabel" true) }}{{ end -}}
</div> {{- else -}}
{{ end -}} {{- with $displayThemeToggle -}}
</div> <div class="flex grow flex-col">{{ partial "theme-toggle" }}</div>
{{- end -}}
{{- end -}}
</div>
{{- end -}}
</aside> </aside>
{{- define "sidebar-main" -}} {{- define "sidebar-main" -}}