mirror of
https://github.com/glanceapp/glance.git
synced 2025-06-21 10:27:45 +02:00
feat: theme switcher
This commit is contained in:
parent
abbb4950a5
commit
62e9c32082
@ -228,25 +228,41 @@ Example:
|
|||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
theme:
|
theme:
|
||||||
background-color: 100 20 10
|
background-color: 186 21 20
|
||||||
primary-color: 40 90 40
|
contrast-multiplier: 1.2
|
||||||
contrast-multiplier: 1.1
|
primary-color: 97 13 80
|
||||||
|
|
||||||
|
presets:
|
||||||
|
my-custom-dark-theme:
|
||||||
|
background-color: 229 19 23
|
||||||
|
contrast-multiplier: 1.2
|
||||||
|
primary-color: 222 74 74
|
||||||
|
positive-color: 96 44 68
|
||||||
|
negative-color: 359 68 71
|
||||||
|
my-custom-light-theme:
|
||||||
|
light: true
|
||||||
|
background-color: 220 23 95
|
||||||
|
contrast-multiplier: 1.0
|
||||||
|
primary-color: 220 91 54
|
||||||
|
positive-color: 109 58 40
|
||||||
|
negative-color: 347 87 44
|
||||||
```
|
```
|
||||||
|
|
||||||
### Themes
|
### Themes
|
||||||
If you don't want to spend time configuring your own theme, there are [several available themes](themes.md) which you can simply copy the values for.
|
If you don't want to spend time configuring your own theme, there are [several available themes](themes.md) which you can simply copy the values for.
|
||||||
|
|
||||||
### Properties
|
### Properties
|
||||||
| Name | Type | Required | Default |
|
| Name | Type | Required | Default |
|
||||||
| ---- | ---- | -------- | ------- |
|
| ---- |-------|----------| ------- |
|
||||||
| light | boolean | no | false |
|
| light | boolean | no | false |
|
||||||
| background-color | HSL | no | 240 8 9 |
|
| background-color | HSL | no | 240 8 9 |
|
||||||
| primary-color | HSL | no | 43 50 70 |
|
| primary-color | HSL | no | 43 50 70 |
|
||||||
| positive-color | HSL | no | same as `primary-color` |
|
| positive-color | HSL | no | same as `primary-color` |
|
||||||
| negative-color | HSL | no | 0 70 70 |
|
| negative-color | HSL | no | 0 70 70 |
|
||||||
| contrast-multiplier | number | no | 1 |
|
| contrast-multiplier | number | no | 1 |
|
||||||
| text-saturation-multiplier | number | no | 1 |
|
| text-saturation-multiplier | number | no | 1 |
|
||||||
| custom-css-file | string | no | |
|
| custom-css-file | string | no | |
|
||||||
|
| presets | array | no | |
|
||||||
|
|
||||||
#### `light`
|
#### `light`
|
||||||
Whether the scheme is light or dark. This does not change the background color, it inverts the text colors so that they look appropriately on a light background.
|
Whether the scheme is light or dark. This does not change the background color, it inverts the text colors so that they look appropriately on a light background.
|
||||||
@ -279,6 +295,26 @@ theme:
|
|||||||
custom-css-file: /assets/my-style.css
|
custom-css-file: /assets/my-style.css
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### `presets`
|
||||||
|
Define theme presets that can be selected from a dropdown menu in the webpage. Example:
|
||||||
|
```yaml
|
||||||
|
theme:
|
||||||
|
presets:
|
||||||
|
my-custom-dark-theme: # This will be displayed in the dropdown menu to select this theme
|
||||||
|
background-color: 229 19 23
|
||||||
|
contrast-multiplier: 1.2
|
||||||
|
primary-color: 222 74 74
|
||||||
|
positive-color: 96 44 68
|
||||||
|
negative-color: 359 68 71
|
||||||
|
my-custom-light-theme: # This will be displayed in the dropdown menu to select this theme
|
||||||
|
light: true
|
||||||
|
background-color: 220 23 95
|
||||||
|
contrast-multiplier: 1.0
|
||||||
|
primary-color: 220 91 54
|
||||||
|
positive-color: 109 58 40
|
||||||
|
negative-color: 347 87 44
|
||||||
|
```
|
||||||
|
|
||||||
> [!TIP]
|
> [!TIP]
|
||||||
>
|
>
|
||||||
> Because Glance uses a lot of utility classes it might be difficult to target some elements. To make it easier to style specific widgets, each widget has a `widget-type-{name}` class, so for example if you wanted to make the links inside just the RSS widget bigger you could use the following selector:
|
> Because Glance uses a lot of utility classes it might be difficult to target some elements. To make it easier to style specific widgets, each widget has a `widget-type-{name}` class, so for example if you wanted to make the links inside just the RSS widget bigger you could use the following selector:
|
||||||
|
@ -17,6 +17,16 @@ import (
|
|||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type CssProperties struct {
|
||||||
|
BackgroundColor *hslColorField `yaml:"background-color"`
|
||||||
|
PrimaryColor *hslColorField `yaml:"primary-color"`
|
||||||
|
PositiveColor *hslColorField `yaml:"positive-color"`
|
||||||
|
NegativeColor *hslColorField `yaml:"negative-color"`
|
||||||
|
Light bool `yaml:"light"`
|
||||||
|
ContrastMultiplier float32 `yaml:"contrast-multiplier"`
|
||||||
|
TextSaturationMultiplier float32 `yaml:"text-saturation-multiplier"`
|
||||||
|
}
|
||||||
|
|
||||||
type config struct {
|
type config struct {
|
||||||
Server struct {
|
Server struct {
|
||||||
Host string `yaml:"host"`
|
Host string `yaml:"host"`
|
||||||
@ -31,6 +41,7 @@ type config struct {
|
|||||||
} `yaml:"document"`
|
} `yaml:"document"`
|
||||||
|
|
||||||
Theme struct {
|
Theme struct {
|
||||||
|
// Todo : Find a way to use CssProperties struct to avoid duplicates
|
||||||
BackgroundColor *hslColorField `yaml:"background-color"`
|
BackgroundColor *hslColorField `yaml:"background-color"`
|
||||||
PrimaryColor *hslColorField `yaml:"primary-color"`
|
PrimaryColor *hslColorField `yaml:"primary-color"`
|
||||||
PositiveColor *hslColorField `yaml:"positive-color"`
|
PositiveColor *hslColorField `yaml:"positive-color"`
|
||||||
@ -39,6 +50,8 @@ type config struct {
|
|||||||
ContrastMultiplier float32 `yaml:"contrast-multiplier"`
|
ContrastMultiplier float32 `yaml:"contrast-multiplier"`
|
||||||
TextSaturationMultiplier float32 `yaml:"text-saturation-multiplier"`
|
TextSaturationMultiplier float32 `yaml:"text-saturation-multiplier"`
|
||||||
CustomCSSFile string `yaml:"custom-css-file"`
|
CustomCSSFile string `yaml:"custom-css-file"`
|
||||||
|
|
||||||
|
Presets map[string]CssProperties `yaml:"presets"`
|
||||||
} `yaml:"theme"`
|
} `yaml:"theme"`
|
||||||
|
|
||||||
Branding struct {
|
Branding struct {
|
||||||
|
@ -3,6 +3,7 @@ package glance
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"html/template"
|
"html/template"
|
||||||
"log"
|
"log"
|
||||||
@ -125,8 +126,9 @@ func (a *application) transformUserDefinedAssetPath(path string) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type pageTemplateData struct {
|
type pageTemplateData struct {
|
||||||
App *application
|
App *application
|
||||||
Page *page
|
Page *page
|
||||||
|
Presets string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *application) handlePageRequest(w http.ResponseWriter, r *http.Request) {
|
func (a *application) handlePageRequest(w http.ResponseWriter, r *http.Request) {
|
||||||
@ -137,9 +139,20 @@ func (a *application) handlePageRequest(w http.ResponseWriter, r *http.Request)
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
presets := a.Config.Theme.Presets
|
||||||
|
keys := make([]string, 0, len(presets))
|
||||||
|
for key := range presets {
|
||||||
|
keys = append(keys, key)
|
||||||
|
}
|
||||||
|
presetsAsJSON, jsonErr := json.Marshal(presets)
|
||||||
|
if jsonErr != nil {
|
||||||
|
log.Fatalf("Erreur lors de la conversion en JSON : %v", jsonErr)
|
||||||
|
}
|
||||||
|
|
||||||
pageData := pageTemplateData{
|
pageData := pageTemplateData{
|
||||||
Page: page,
|
App: a,
|
||||||
App: a,
|
Page: page,
|
||||||
|
Presets: string(presetsAsJSON),
|
||||||
}
|
}
|
||||||
|
|
||||||
var responseBytes bytes.Buffer
|
var responseBytes bytes.Buffer
|
||||||
|
@ -2,6 +2,30 @@ import { setupPopovers } from './popover.js';
|
|||||||
import { setupMasonries } from './masonry.js';
|
import { setupMasonries } from './masonry.js';
|
||||||
import { throttledDebounce, isElementVisible, openURLInNewTab } from './utils.js';
|
import { throttledDebounce, isElementVisible, openURLInNewTab } from './utils.js';
|
||||||
|
|
||||||
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
const theme = localStorage.getItem('theme');
|
||||||
|
|
||||||
|
if (!theme) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const html = document.querySelector('html');
|
||||||
|
const jsonTheme = JSON.parse(theme);
|
||||||
|
if (jsonTheme.themeScheme === 'light') {
|
||||||
|
html.classList.remove('dark-scheme');
|
||||||
|
html.classList.add('light-scheme');
|
||||||
|
} else if (jsonTheme.themeScheme === 'dark') {
|
||||||
|
html.classList.add('dark-scheme');
|
||||||
|
html.classList.remove('light-scheme');
|
||||||
|
}
|
||||||
|
|
||||||
|
html.classList.add(jsonTheme.theme);
|
||||||
|
document.querySelector('[name=color-scheme]').setAttribute('content', jsonTheme.themeScheme);
|
||||||
|
Array.from(document.querySelectorAll('.dropdown-button span')).forEach((button) => {
|
||||||
|
button.textContent = jsonTheme.theme;
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
async function fetchPageContent(pageData) {
|
async function fetchPageContent(pageData) {
|
||||||
// TODO: handle non 200 status codes/time outs
|
// TODO: handle non 200 status codes/time outs
|
||||||
// TODO: add retries
|
// TODO: add retries
|
||||||
@ -638,6 +662,101 @@ function setupTruncatedElementTitles() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {Object} HslColorField
|
||||||
|
* @property {number} Hue
|
||||||
|
* @property {number} Saturation
|
||||||
|
* @property {number} Lightness
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {Object} Theme
|
||||||
|
* @property {HslColorField} BackgroundColor
|
||||||
|
* @property {HslColorField} PrimaryColor
|
||||||
|
* @property {HslColorField} PositiveColor
|
||||||
|
* @property {HslColorField} NegativeColor
|
||||||
|
* @property {boolean} Light
|
||||||
|
* @property {number} ContrastMultiplier
|
||||||
|
* @property {number} TextSaturationMultiplier
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {Record<string, Theme>} ThemeCollection
|
||||||
|
*/
|
||||||
|
function setupThemeSwitcher() {
|
||||||
|
const presetsContainers = Array.from(document.querySelectorAll('.custom-presets'));
|
||||||
|
const userThemesKeys = Object.keys(userThemes);
|
||||||
|
|
||||||
|
presetsContainers.forEach((presetsContainer) => {
|
||||||
|
userThemesKeys.forEach(preset => {
|
||||||
|
const presetElement = document.createElement('div');
|
||||||
|
presetElement.className = 'theme-option';
|
||||||
|
presetElement.setAttribute('data-theme', preset);
|
||||||
|
presetElement.setAttribute('data-scheme', userThemes[preset].Light ? 'light' : 'dark');
|
||||||
|
presetElement.textContent = preset;
|
||||||
|
presetsContainer.appendChild(presetElement);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const dropdownButtons = Array.from(document.querySelectorAll('.dropdown-button'));
|
||||||
|
const dropdownContents = Array.from(document.querySelectorAll('.dropdown-content'));
|
||||||
|
|
||||||
|
dropdownButtons.forEach((dropdownButton) => {
|
||||||
|
dropdownButton.addEventListener('click', (e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
dropdownContents.forEach((dropdownContent) => {
|
||||||
|
dropdownContent.classList.toggle('show');
|
||||||
|
});
|
||||||
|
dropdownButton.classList.toggle('active');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
document.addEventListener('click', (e) => {
|
||||||
|
if (!e.target.closest('.theme-dropdown')) {
|
||||||
|
dropdownContents.forEach((dropdownContent) => {
|
||||||
|
dropdownContent.classList.remove('show');
|
||||||
|
});
|
||||||
|
dropdownButtons.forEach((dropdownButton) => {
|
||||||
|
dropdownButton.classList.remove('active');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
document.querySelectorAll('.theme-option').forEach(option => {
|
||||||
|
option.addEventListener('click', () => {
|
||||||
|
const selectedTheme = option.getAttribute('data-theme');
|
||||||
|
const selectedThemeScheme = option.getAttribute('data-scheme');
|
||||||
|
const previousTheme = localStorage.getItem('theme');
|
||||||
|
dropdownContents.forEach((dropdownContent) => {
|
||||||
|
dropdownContent.classList.remove('show');
|
||||||
|
});
|
||||||
|
dropdownButtons.forEach((dropdownButton) => {
|
||||||
|
const html = document.querySelector('html');
|
||||||
|
if (previousTheme) {
|
||||||
|
html.classList.remove(JSON.parse(previousTheme).theme);
|
||||||
|
}
|
||||||
|
dropdownButton.classList.remove('active');
|
||||||
|
dropdownButton.querySelector('span').textContent = option.textContent;
|
||||||
|
html.classList.add(selectedTheme);
|
||||||
|
|
||||||
|
if (selectedThemeScheme === 'light') {
|
||||||
|
html.classList.remove('dark-scheme');
|
||||||
|
html.classList.add('light-scheme');
|
||||||
|
} else if (selectedThemeScheme === 'dark') {
|
||||||
|
html.classList.add('dark-scheme');
|
||||||
|
html.classList.remove('light-scheme');
|
||||||
|
}
|
||||||
|
|
||||||
|
document.querySelector('[name=color-scheme]').setAttribute('content', selectedThemeScheme);
|
||||||
|
localStorage.setItem('theme', JSON.stringify({
|
||||||
|
theme: selectedTheme,
|
||||||
|
themeScheme: selectedThemeScheme
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
async function setupPage() {
|
async function setupPage() {
|
||||||
const pageElement = document.getElementById("page");
|
const pageElement = document.getElementById("page");
|
||||||
const pageContentElement = document.getElementById("page-content");
|
const pageContentElement = document.getElementById("page-content");
|
||||||
@ -646,6 +765,7 @@ async function setupPage() {
|
|||||||
pageContentElement.innerHTML = pageContent;
|
pageContentElement.innerHTML = pageContent;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
setupThemeSwitcher();
|
||||||
setupPopovers();
|
setupPopovers();
|
||||||
setupClocks()
|
setupClocks()
|
||||||
setupCarousels();
|
setupCarousels();
|
||||||
|
@ -55,6 +55,28 @@
|
|||||||
--font-size-h6: 1.1rem;
|
--font-size-h6: 1.1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.dark {
|
||||||
|
--scheme: ;
|
||||||
|
--bgh: 240;
|
||||||
|
--bgs: 8%;
|
||||||
|
--bgl: 9%;
|
||||||
|
--bghs: var(--bgh), var(--bgs);
|
||||||
|
--cm: 1;
|
||||||
|
--tsm: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.light {
|
||||||
|
--scheme: 100% -;
|
||||||
|
--bgh: 240;
|
||||||
|
--bgs: 50%;
|
||||||
|
--bgl: 98%;
|
||||||
|
--bghs: var(--bgh), var(--bgs);
|
||||||
|
--cm: 1;
|
||||||
|
--tsm: 1;
|
||||||
|
--color-primary: hsl(43, 50%, 70%);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
.light-scheme {
|
.light-scheme {
|
||||||
--scheme: 100% -;
|
--scheme: 100% -;
|
||||||
}
|
}
|
||||||
@ -1625,7 +1647,6 @@ details[open] .summary::after {
|
|||||||
padding: 15px var(--content-bounds-padding);
|
padding: 15px var(--content-bounds-padding);
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
overflow-x: auto;
|
|
||||||
scrollbar-width: thin;
|
scrollbar-width: thin;
|
||||||
gap: 2.5rem;
|
gap: 2.5rem;
|
||||||
}
|
}
|
||||||
@ -1872,3 +1893,83 @@ details[open] .summary::after {
|
|||||||
.list-gap-20 { --list-half-gap: 1rem; }
|
.list-gap-20 { --list-half-gap: 1rem; }
|
||||||
.list-gap-24 { --list-half-gap: 1.2rem; }
|
.list-gap-24 { --list-half-gap: 1.2rem; }
|
||||||
.list-gap-34 { --list-half-gap: 1.7rem; }
|
.list-gap-34 { --list-half-gap: 1.7rem; }
|
||||||
|
|
||||||
|
/*
|
||||||
|
### Theme Dropdown ###
|
||||||
|
*/
|
||||||
|
.theme-dropdown {
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown-button {
|
||||||
|
padding: 10px 15px;
|
||||||
|
background: var(--color-widget-background);
|
||||||
|
border: 1px solid var(--color-widget-content-border);
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
min-width: 150px;
|
||||||
|
transition: border-color .2s;
|
||||||
|
color: var(--color-text-highlight);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown-button:hover {
|
||||||
|
border-color: var(--color-text-subdue);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown-content {
|
||||||
|
display: none;
|
||||||
|
position: absolute;
|
||||||
|
top: 100%;
|
||||||
|
left: 0;
|
||||||
|
background: var(--color-widget-content-border);
|
||||||
|
min-width: 150px;
|
||||||
|
box-shadow: 0 2px 5px rgba(0,0,0,0.2);
|
||||||
|
border-radius: 4px;
|
||||||
|
z-index: 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobile-navigation-page-links .dropdown-content {
|
||||||
|
top: unset;
|
||||||
|
bottom: 38px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown-content.show {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-option {
|
||||||
|
padding: 10px 15px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background-color 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-option:hover {
|
||||||
|
background-color: #f8f9fa;
|
||||||
|
}
|
||||||
|
|
||||||
|
.separator {
|
||||||
|
height: 1px;
|
||||||
|
background-color: #dee2e6;
|
||||||
|
margin: 5px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.arrow {
|
||||||
|
border: solid #666;
|
||||||
|
border-width: 0 2px 2px 0;
|
||||||
|
display: inline-block;
|
||||||
|
padding: 3px;
|
||||||
|
transform: rotate(45deg);
|
||||||
|
transition: transform 0.2s ease;
|
||||||
|
margin-left: auto;
|
||||||
|
position: relative;
|
||||||
|
top: -1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown-button.active .arrow {
|
||||||
|
transform: rotate(-135deg);
|
||||||
|
}
|
@ -5,7 +5,7 @@
|
|||||||
<title>{{ block "document-title" . }}{{ end }}</title>
|
<title>{{ block "document-title" . }}{{ end }}</title>
|
||||||
<script>if (navigator.platform === 'iPhone') document.documentElement.classList.add('ios');</script>
|
<script>if (navigator.platform === 'iPhone') document.documentElement.classList.add('ios');</script>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="color-scheme" content="dark">
|
<meta name="color-scheme" content="light">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
|
||||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||||
<meta name="mobile-web-app-capable" content="yes">
|
<meta name="mobile-web-app-capable" content="yes">
|
||||||
|
@ -8,6 +8,11 @@
|
|||||||
slug: "{{ .Page.Slug }}",
|
slug: "{{ .Page.Slug }}",
|
||||||
baseURL: "{{ .App.Config.Server.BaseURL }}",
|
baseURL: "{{ .App.Config.Server.BaseURL }}",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type ThemeCollection
|
||||||
|
*/
|
||||||
|
const userThemes = JSON.parse("{{ .Presets }}");
|
||||||
</script>
|
</script>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
||||||
@ -29,6 +34,23 @@
|
|||||||
{{ end }}
|
{{ end }}
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
||||||
|
{{ define "theme-switcher" }}
|
||||||
|
<div class="theme-dropdown">
|
||||||
|
<button class="dropdown-button">
|
||||||
|
<span>Theme</span>
|
||||||
|
<i class="arrow"></i>
|
||||||
|
</button>
|
||||||
|
<div class="dropdown-content">
|
||||||
|
<div class="theme-option" data-theme="default" data-scheme="dark">default</div>
|
||||||
|
<div class="theme-option" data-theme="light" data-scheme="light">light</div>
|
||||||
|
<div class="theme-option" data-theme="dark" data-scheme="dark">dark</div>
|
||||||
|
<div class="separator"></div>
|
||||||
|
<div class="custom-presets">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
{{ define "document-body" }}
|
{{ define "document-body" }}
|
||||||
<div class="flex flex-column body-content">
|
<div class="flex flex-column body-content">
|
||||||
{{ if not .Page.HideDesktopNavigation }}
|
{{ if not .Page.HideDesktopNavigation }}
|
||||||
@ -39,6 +61,9 @@
|
|||||||
<div class="nav flex grow">
|
<div class="nav flex grow">
|
||||||
{{ template "navigation-links" . }}
|
{{ template "navigation-links" . }}
|
||||||
</div>
|
</div>
|
||||||
|
<div class="flex items-center">
|
||||||
|
{{ template "theme-switcher" . }}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
@ -52,7 +77,12 @@
|
|||||||
<label class="mobile-navigation-label"><input type="checkbox" class="mobile-navigation-page-links-input" autocomplete="on"{{ if .Page.ExpandMobilePageNavigation }} checked{{ end }}><div class="hamburger-icon"></div></label>
|
<label class="mobile-navigation-label"><input type="checkbox" class="mobile-navigation-page-links-input" autocomplete="on"{{ if .Page.ExpandMobilePageNavigation }} checked{{ end }}><div class="hamburger-icon"></div></label>
|
||||||
</div>
|
</div>
|
||||||
<div class="mobile-navigation-page-links">
|
<div class="mobile-navigation-page-links">
|
||||||
{{ template "navigation-links" . }}
|
<div class="flex grow">
|
||||||
|
{{ template "navigation-links" . }}
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center">
|
||||||
|
{{ template "theme-switcher" . }}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -1,14 +1,31 @@
|
|||||||
<style>
|
<style>
|
||||||
:root {
|
.default {
|
||||||
{{ if .BackgroundColor }}
|
{{ if .BackgroundColor }}
|
||||||
--bgh: {{ .BackgroundColor.Hue }};
|
--bgh: {{ .BackgroundColor.Hue }};
|
||||||
--bgs: {{ .BackgroundColor.Saturation }}%;
|
--bgs: {{ .BackgroundColor.Saturation }}%;
|
||||||
--bgl: {{ .BackgroundColor.Lightness }}%;
|
--bgl: {{ .BackgroundColor.Lightness }}%;
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
||||||
{{ if ne 0.0 .ContrastMultiplier }}--cm: {{ .ContrastMultiplier }};{{ end }}
|
{{ if ne 0.0 .ContrastMultiplier }}--cm: {{ .ContrastMultiplier }};{{ end }}
|
||||||
{{ if ne 0.0 .TextSaturationMultiplier }}--tsm: {{ .TextSaturationMultiplier }};{{ end }}
|
{{ if ne 0.0 .TextSaturationMultiplier }}--tsm: {{ .TextSaturationMultiplier }};{{ end }}
|
||||||
{{ if .PrimaryColor }}--color-primary: {{ .PrimaryColor.String | safeCSS }};{{ end }}
|
{{ if .PrimaryColor }}--color-primary: {{ .PrimaryColor.String | safeCSS }};{{ end }}
|
||||||
{{ if .PositiveColor }}--color-positive: {{ .PositiveColor.String | safeCSS }};{{ end }}
|
{{ if .PositiveColor }}--color-positive: {{ .PositiveColor.String | safeCSS }};{{ end }}
|
||||||
{{ if .NegativeColor }}--color-negative: {{ .NegativeColor.String | safeCSS }};{{ end }}
|
{{ if .NegativeColor }}--color-negative: {{ .NegativeColor.String | safeCSS }};{{ end }}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{{ range $name,$theme := .Presets }}
|
||||||
|
.{{ $name }} {
|
||||||
|
{{ if .BackgroundColor }}
|
||||||
|
--bgh: {{ $theme.BackgroundColor.Hue }};
|
||||||
|
--bgs: {{ $theme.BackgroundColor.Saturation }}%;
|
||||||
|
--bgl: {{ $theme.BackgroundColor.Lightness }}%;
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
{{ if ne 0.0 $theme.ContrastMultiplier }}--cm: {{ $theme.ContrastMultiplier }};{{ end }}
|
||||||
|
{{ if ne 0.0 $theme.TextSaturationMultiplier }}--tsm: {{ $theme.TextSaturationMultiplier }};{{ end }}
|
||||||
|
{{ if $theme.PrimaryColor }}--color-primary: {{ $theme.PrimaryColor.String | safeCSS }};{{ end }}
|
||||||
|
{{ if $theme.PositiveColor }}--color-positive: {{ $theme.PositiveColor.String | safeCSS }};{{ end }}
|
||||||
|
{{ if $theme.NegativeColor }}--color-negative: {{ $theme.NegativeColor.String | safeCSS }};{{ end }}
|
||||||
|
}
|
||||||
|
{{ end }}
|
||||||
</style>
|
</style>
|
Loading…
x
Reference in New Issue
Block a user