variant: allow page browsing with custom theme #188

This commit is contained in:
Sören Weber 2022-02-21 23:11:04 +01:00
parent f2b09e0980
commit 78abe914e0
No known key found for this signature in database
GPG key ID: 07D17FF580AE7589
7 changed files with 204 additions and 93 deletions

View file

@ -168,7 +168,7 @@ Then, set the `themeVariant` value with the name of your custom theme file. That
### Multiple variants ### Multiple variants
You can also set multiple variants. In this case, the first variant is the default choosen on first view and a theme switch will be shown in the menu footer. You can also set multiple variants. In this case, the first variant is the default choosen on first view and a variant switch will be shown in the menu footer.
```toml ```toml
[params] [params]

View file

@ -13,16 +13,19 @@ The arrowed lines reflect how colors are inherited thru different parts of the t
To better understand this select the `neon` variant and modify the differnet heading colors. There, colors for the heading `h2`, `h3` and `h4` are explicitly set. `h5` is not set and inherits its value from `h4`. `h6` is also not set and inherits its value from `h5`. To better understand this select the `neon` variant and modify the differnet heading colors. There, colors for the heading `h2`, `h3` and `h4` are explicitly set. `h5` is not set and inherits its value from `h4`. `h6` is also not set and inherits its value from `h5`.
Once you've changed a color, the theme selector will show a "My custom variant" entry and your changes are stored. You can change pages and even close the browser without losing your changes.
Once you are satisfied, you can download the new variants file and install it in your site. Once you are satisfied, you can download the new variants file and install it in your site.
{{% notice note %}} {{% notice note %}}
You need to define at least one `themeVariant` in your `config.toml` to modify and download the styles.
This only works in modern browsers. This only works in modern browsers.
{{% /notice %}} {{% /notice %}}
## Variant generator ## Variant generator
<a class="vardownload btn btn-default">Download color variant</a>
<a class="varreset btn btn-default">Reset variant</a>
<div id="vargenerator" class="mermaid" style="background-color: var(--INTERNAL-MAIN-TEXT-color);" align="center">Graph</div> <div id="vargenerator" class="mermaid" style="background-color: var(--INTERNAL-MAIN-TEXT-color);" align="center">Graph</div>
<a class="vardownload btn btn-default">Download color variant</a> <a class="vardownload btn btn-default">Download color variant</a>

View file

@ -24,7 +24,7 @@ This document shows you what's new in the latest release. For a detailed list of
This change will only affect your installation if you've not set the `themeVariant` parameter in your `config.toml`. [If you still want to use the Learn color variant]({{%relref "basics/customization/#learn-variant" %}}), you have to explicitly set `themeVariant="learn"` in your `config.toml`. This change will only affect your installation if you've not set the `themeVariant` parameter in your `config.toml`. [If you still want to use the Learn color variant]({{%relref "basics/customization/#learn-variant" %}}), you have to explicitly set `themeVariant="learn"` in your `config.toml`.
Note, that this will also affect your site if viewed with Internet Explorer 11 and can not be reconfigured as it does not support CSS variables. Note, that this will also affect your site if viewed with Internet Explorer 11 but in this case it can not be reconfigured as Internet Explorer does not support CSS variables.
- **Change**: Due to a bug, that we couldn't fix in a general manner for color variants, we decided to remove `--MENU-SEARCH-BOX-ICONS-color`. - **Change**: Due to a bug, that we couldn't fix in a general manner for color variants, we decided to remove `--MENU-SEARCH-BOX-ICONS-color`.
@ -36,9 +36,9 @@ This document shows you what's new in the latest release. For a detailed list of
- **New**: To make the creation of new variants easier for you, we've added a new interactive [theme variant generator]({{%relref "basics/generator" %}}). - **New**: To make the creation of new variants easier for you, we've added a new interactive [theme variant generator]({{%relref "basics/generator" %}}).
- **New**: You can now configure multiple color variants in your `config.toml`. In this case, the first variant is the default chosen on first view and a theme switch will be shown in the menu footer. See the [documentation]({{%relref "basics/customization/#multiple-variants" %}}) for configuration. - **New**: You can now configure multiple color variants in your `config.toml`. In this case, the first variant is the default chosen on first view and a variant switch will be shown in the menu footer. See the [documentation]({{%relref "basics/customization/#multiple-variants" %}}) for configuration.
Note, that the new theme switch will not work with Internet Explorer 11 as it does not support CSS variables. Note, that the new variant switch will not work with Internet Explorer 11 as it does not support CSS variables.
## 2.9.0 ## 2.9.0

View file

@ -52,26 +52,22 @@
</ul> </ul>
</div> </div>
{{- end }} {{- end }}
<div class="footermargin"></div> {{- $siteLanguages := .Site.Languages }}
{{- $showlangswitch := and .Site.IsMultiLingual (not .Site.Params.disableLanguageSwitchingButton) }} {{- $showlangswitch := and .Site.IsMultiLingual (not .Site.Params.disableLanguageSwitchingButton) (gt (int (len $siteLanguages)) 1) }}
{{- $themevariants := slice | append .Site.Params.themeVariant }} {{- $themevariants := slice | append (.Site.Params.themeVariant | default "relearn-light" ) }}
{{- $showthemeswitch := gt (int (len $themevariants)) 1 }} {{- $showvariantswitch := gt (int (len $themevariants)) 1 }}
{{- $footer := partial "menu-footer.html" . }} {{- $footer := partial "menu-footer.html" . }}
{{- $showfooter := not (eq 0 (int (len ($footer | plainify)))) }} {{- $showfooter := not (eq 0 (int (len ($footer | plainify)))) }}
{{- if or $showlangswitch $showvisitedlinks $showfooter }} <div class="footermargin footerLangSwitch footerVariantSwitch footerVisitedLinks footerFooter{{if $showlangswitch}} showLangSwitch{{end}}{{if $showvariantswitch}} showVariantSwitch{{end}}{{if $showvisitedlinks}} showVisitedLinks{{end}}{{if $showfooter}} showFooter{{end}}"></div>
<hr class="default-animation"/> <hr class="default-animation footerLangSwitch footerVariantSwitch footerVisitedLinks footerFooter{{if $showlangswitch}} showLangSwitch{{end}}{{if $showvariantswitch}} showVariantSwitch{{end}}{{if $showvisitedlinks}} showVisitedLinks{{end}}{{if $showfooter}} showFooter{{end}}"/>
{{- end }} <div id="prefooter" class="footerLangSwitch footerVariantSwitch footerVisitedLinks{{if $showlangswitch}} showLangSwitch{{end}}{{if $showvariantswitch}} showVariantSwitch{{end}}{{if $showvisitedlinks}} showVisitedLinks{{end}}">
{{- if or $showlangswitch $showthemeswitch $showvisitedlinks }}
<div id="prefooter">
<ul> <ul>
{{- if $showlangswitch }} <li id="select-language-container" class="footerLangSwitch{{if $showlangswitch}} showLangSwitch{{end}}">
<li id="select-language-container">
<a class="padding select-container"> <a class="padding select-container">
<i class="fas fa-language fa-fw"></i> <i class="fas fa-language fa-fw"></i>
<span>&nbsp;</span> <span>&nbsp;</span>
<div class="select-style"> <div class="select-style">
<select id="select-language" onchange="location = baseUri + this.value;"> <select id="select-language" onchange="location = baseUri + this.value;">
{{- $siteLanguages := .Site.Languages }}
{{- $pageLang := .Page.Lang }} {{- $pageLang := .Page.Lang }}
{{- range .Page.AllTranslations }} {{- range .Page.AllTranslations }}
{{- $translation := . }} {{- $translation := . }}
@ -90,20 +86,18 @@
<div class="select-clear"></div> <div class="select-clear"></div>
</a> </a>
</li> </li>
{{- end }} <li id="select-variant-container" class="footerVariantSwitch{{if $showvariantswitch}} showVariantSwitch{{end}}">
{{- if $showthemeswitch }}
<li id="select-variant-container">
<a class="padding select-container"> <a class="padding select-container">
<i class="fas fa-paint-brush fa-fw"></i> <i class="fas fa-paint-brush fa-fw"></i>
<span>&nbsp;</span> <span>&nbsp;</span>
<div class="select-style"> <div class="select-style">
<select id="select-variant" onchange="variants.changeVariant( this.value );"> <select id="select-variant" onchange="variants.changeVariant( this.value );">
{{- $firsttheme := true }} {{- $firstvariant := true }}
{{- range $themevariants }} {{- range $themevariants }}
{{- $themevariant := . }} {{- $themevariant := . }}
{{- $variantname := replaceRE "[-_]+" " " $themevariant }} {{- $variantname := replaceRE "[-_]+" " " $themevariant }}
{{- if $firsttheme }} {{- if $firstvariant }}
{{- $firsttheme = false }} {{- $firstvariant = false }}
<option id="{{ $themevariant }}" value="{{ $themevariant }}" selected>{{ $variantname | title }}</option> <option id="{{ $themevariant }}" value="{{ $themevariant }}" selected>{{ $variantname | title }}</option>
{{- else }} {{- else }}
<option id="{{ $themevariant }}" value="{{ $themevariant }}">{{ $variantname | title }}</option> <option id="{{ $themevariant }}" value="{{ $themevariant }}">{{ $variantname | title }}</option>
@ -113,20 +107,14 @@
</div> </div>
<div class="select-clear"></div> <div class="select-clear"></div>
</a> </a>
<script>variants.markSelectedVariant( variants.getVariant() );</script> <script>variants.markSelectedVariant();</script>
</li> </li>
{{- end }} <li class="footerVisitedLinks{{if $showvisitedlinks}} showVisitedLinks{{end}}"><a class="padding" href="#" data-clear-history-toggle=""><i class="fas fa-history fa-fw"></i> {{ T "Clear-History" }}</a></li>
{{- if $showvisitedlinks }}
<li><a class="padding" href="#" data-clear-history-toggle=""><i class="fas fa-history fa-fw"></i> {{ T "Clear-History" }}</a></li>
{{- end }}
</ul> </ul>
</div> </div>
{{- end }} <div id="footer" class="footerFooter{{if $showfooter}} showFooter{{end}}">
{{- if $showfooter }}
<div id="footer">
{{- $footer }} {{- $footer }}
</div> </div>
{{- end }}
</div> </div>
</nav> </nav>
{{- define "section-tree-nav" }} {{- define "section-tree-nav" }}

View file

@ -5,7 +5,7 @@
<link href="{{"css/perfect-scrollbar.min.css" | relURL}}{{ if $assetBusting }}?{{ now.Unix }}{{ end }}" rel="stylesheet"> <link href="{{"css/perfect-scrollbar.min.css" | relURL}}{{ if $assetBusting }}?{{ now.Unix }}{{ end }}" rel="stylesheet">
<link href="{{"css/auto-complete.css" | relURL}}{{ if $assetBusting }}?{{ now.Unix }}{{ end }}" rel="stylesheet"> <link href="{{"css/auto-complete.css" | relURL}}{{ if $assetBusting }}?{{ now.Unix }}{{ end }}" rel="stylesheet">
<link href="{{"css/theme.css" | relURL}}{{ if $assetBusting }}?{{ now.Unix }}{{ end }}" rel="stylesheet"> <link href="{{"css/theme.css" | relURL}}{{ if $assetBusting }}?{{ now.Unix }}{{ end }}" rel="stylesheet">
{{- $themevariants := slice | append .Site.Params.themeVariant }} {{- $themevariants := slice | append (.Site.Params.themeVariant | default "relearn-light" ) }}
{{- with index $themevariants 0 }} {{- with index $themevariants 0 }}
<link id="variant-style" href="{{(printf "css/theme-%s.css" .) | relURL}}{{ if $assetBusting }}?{{ now.Unix }}{{ end }}" rel="stylesheet"> <link id="variant-style" href="{{(printf "css/theme-%s.css" .) | relURL}}{{ if $assetBusting }}?{{ now.Unix }}{{ end }}" rel="stylesheet">
{{- end }} {{- end }}

View file

@ -1452,3 +1452,17 @@ h6 a {
display: none; display: none;
} }
} }
.footerLangSwitch,
.footerVariantSwitch,
.footerVisitedLinks,
.footerFooter {
display: none;
}
.showLangSwitch,
.showVariantSwitch,
.showVisitedLinks,
.showFooter {
display: initial;
}

View file

@ -3,7 +3,7 @@
var variants = { var variants = {
variant: '', variant: '',
variants: [], variants: [],
customvariant: 'my-variant', customvariantname: 'my-custom-variant',
init: function( variants ){ init: function( variants ){
this.variants = variants; this.variants = variants;
@ -11,20 +11,11 @@ var variants = {
this.changeVariant( variant ); this.changeVariant( variant );
document.addEventListener( 'readystatechange', function(){ document.addEventListener( 'readystatechange', function(){
if( document.readyState == 'interactive' ){ if( document.readyState == 'interactive' ){
this.markSelectedVariant( this.getVariant() ); this.markSelectedVariant();
} }
}.bind( this ) ); }.bind( this ) );
}, },
parseVariantnameFromFilename: function ( s ){
if( !s || !s.match ){
return '';
}
var matches = s.match(/^.*\/?theme-([^\/]*?)\.css.*$/);
var variant = matches && matches.length == 2 ? matches[ 1 ] : '';
return variant;
},
getVariant: function(){ getVariant: function(){
return this.variant; return this.variant;
}, },
@ -34,12 +25,14 @@ var variants = {
window.localStorage.setItem( 'variant', variant ); window.localStorage.setItem( 'variant', variant );
}, },
markSelectedVariant: function( variant ){ markSelectedVariant: function(){
var variant = this.getVariant();
var select = document.querySelector( '#select-variant' ); var select = document.querySelector( '#select-variant' );
if( !select ){ if( !select ){
return; return;
} }
if( select.value != variant ){ this.addCustomVariantOption();
if( variant && select.value != variant ){
select.value = variant; select.value = variant;
} }
// remove selection, because if some uses an arrow navigation" // remove selection, because if some uses an arrow navigation"
@ -50,59 +43,153 @@ var variants = {
} }
}, },
generateVariantPath( old_path ){ generateVariantPath( variant, old_path ){
var variant = this.getVariant();
var new_path = old_path.replace( /^(.*\/theme-).*?(\.css.*)$/, '$1' + variant + '$2' ); var new_path = old_path.replace( /^(.*\/theme-).*?(\.css.*)$/, '$1' + variant + '$2' );
return new_path; return new_path;
}, },
changeVariant: function( variant ){ addCustomVariantOption: function(){
if( this.variants.indexOf( variant ) < 0 ){ var variantbase = window.localStorage.getItem( 'customvariantbase' );
variant = this.variants.length ? this.variants[ 0 ] : ''; if( this.variants.indexOf( variantbase ) < 0 ){
variantbase = '';
} }
this.setVariant( variant ); if( !window.localStorage.getItem( 'customvariant' ) ){
if( !variant ){ variantbase = '';
}
if( !variantbase ){
return; return;
} }
var link = document.querySelector( '#variant-style' ); var select = document.querySelector( '#select-variant' );
if( !link ){ if( !select ){
return; return;
} }
var old_path = link.getAttribute( 'href' ); var option = document.querySelector( '#' + this.customvariantname );
var new_path = this.generateVariantPath( old_path ); if( !option ){
if( old_path != new_path ){ option = document.createElement( 'option' );
link.setAttribute( 'href', new_path ); option.id = this.customvariantname;
this.markSelectedVariant( variant ); option.value = this.customvariantname;
option.text = this.customvariantname.replace( /-/g, ' ' ).replace(/\w\S*/g, (w) => (w.replace(/^\w/g, (c) => c.toUpperCase())));
select.appendChild( option );
document.querySelectorAll( '.footerVariantSwitch' ).forEach( function( e ){
e.classList.add( 'showVariantSwitch' );
});
} }
}, },
removeCustomVariantOption: function(){
var option = document.querySelector( '#' + this.customvariantname );
if( option ){
option.remove();
}
if( this.variants.length <= 1 ){
document.querySelectorAll( '.footerVariantSwitch' ).forEach( function( e ){
e.classList.remove( 'showVariantSwitch' );
});
}
},
saveCustomVariant: function(){
if( this.getVariant() != this.customvariantname ){
window.localStorage.setItem( 'customvariantbase', this.getVariant() );
}
window.localStorage.setItem( 'customvariant', this.generateStylesheet() );
this.setVariant( this.customvariantname );
this.markSelectedVariant();
},
loadCustomVariant: function(){
var stylesheet = window.localStorage.getItem( 'customvariant' );
// temp styles to document
var head = document.querySelector( 'head' );
var style = document.createElement( 'style' );
style.id = 'custom-variant-style';
style.appendChild( document.createTextNode( stylesheet ) );
head.appendChild( style );
var interval_id = setInterval( function(){
if( this.findLoadedStylesheet( 'variant-style' ) ){
clearInterval( interval_id );
// save the styles to the current variant stylesheet
this.variantvariables.forEach( function( e ){
this.changeColor( e.name, true );
}.bind( this ) );
// remove temp styles
style.remove();
this.saveCustomVariant();
}
}.bind( this ), 25 );
},
resetVariant: function(){ resetVariant: function(){
var variantbase = window.localStorage.getItem( 'customvariantbase' );
if( variantbase && confirm( 'You have made changes to your custom variant. Are you sure you want to reset all changes?' ) ){
this.removeCustomVariantOption();
this.changeVariant( variantbase );
window.localStorage.removeItem( 'customvariantbase' );
window.localStorage.removeItem( 'customvariant' );
}
},
switchStylesheet: function( variant, without_check ){
var link = document.querySelector( '#variant-style' ); var link = document.querySelector( '#variant-style' );
if( !link ){ if( !link ){
return; return;
} }
var old_path = link.getAttribute( 'href' ); var old_path = link.getAttribute( 'href' );
var new_path = this.generateVariantPath( old_path ); var new_path = this.generateVariantPath( variant, old_path );
link.setAttribute( 'href', new_path ); link.setAttribute( 'href', new_path );
}, },
changeVariant: function( variant ){
if( variant == this.customvariantname ){
var variantbase = window.localStorage.getItem( 'customvariantbase' );
if( this.variants.indexOf( variantbase ) < 0 ){
variant = '';
}
if( !window.localStorage.getItem( 'customvariant' ) ){
variant = '';
}
this.setVariant( variant );
if( !variant ){
return;
}
this.switchStylesheet( variantbase );
this.loadCustomVariant();
}
else{
if( this.variants.indexOf( variant ) < 0 ){
variant = this.variants.length ? this.variants[ 0 ] : '';
}
this.setVariant( variant );
if( !variant ){
return;
}
this.switchStylesheet( variant );
this.markSelectedVariant();
}
},
generator: function( vargenerator, vardownload, varreset ){ generator: function( vargenerator, vardownload, varreset ){
var graphDefinition = this.generateGraph(); var graphDefinition = this.generateGraph();
var element = document.querySelector( vargenerator ); var graphs = document.querySelectorAll( vargenerator );
element.innerHTML = graphDefinition; graphs.forEach( function( e ){ e.innerHTML = graphDefinition; });
var interval_id = setInterval( function(){ var interval_id = setInterval( function(){
if( document.querySelectorAll( vargenerator + '.mermaid > svg' ).length ){ if( document.querySelectorAll( vargenerator + '.mermaid > svg' ).length ){
clearInterval( interval_id ); clearInterval( interval_id );
this.styleGraph(); this.styleGraph();
} }
}.bind( this ), 100 ); }.bind( this ), 25 );
var download = document.querySelector( vardownload ); var downloads = document.querySelectorAll( vardownload );
download.addEventListener('click', this.getStylesheet.bind( this ) ); downloads.forEach( function( e ){ e.addEventListener('click', this.getStylesheet.bind( this )); }.bind( this ) );
var reset = document.querySelector( varreset ); var resets = document.querySelectorAll( varreset );
reset.addEventListener('click', this.resetVariant.bind( this ) ); resets.forEach( function( e ){ e.addEventListener('click', this.resetVariant.bind( this )); }.bind( this ) );
}, },
download: function(data, mimetype, filename){ download: function(data, mimetype, filename){
@ -115,7 +202,7 @@ var variants = {
}, },
getStylesheet: function(){ getStylesheet: function(){
this.download( this.generateStylesheet(), 'text/css', 'theme-' + this.customvariant + '.css' ); this.download( this.generateStylesheet(), 'text/css', 'theme-' + this.customvariantname + '.css' );
}, },
adjustCSSRules: function(selector, props, sheets){ adjustCSSRules: function(selector, props, sheets){
@ -164,28 +251,33 @@ var variants = {
return this.normalizeColor( getComputedStyle( document.documentElement ).getPropertyValue( '--INTERNAL-'+c ) ); return this.normalizeColor( getComputedStyle( document.documentElement ).getPropertyValue( '--INTERNAL-'+c ) );
}, },
changeColor: function( c ){ findLoadedStylesheet: function( id ){
var style = null; var style = null;
var variant = this.variant;
for( var n = 0; n < document.styleSheets.length; ++n ){ for( var n = 0; n < document.styleSheets.length; ++n ){
if( variant = this.parseVariantnameFromFilename( document.styleSheets[n].href ) ){ if( document.styleSheets[n].ownerNode.id == id ){
var s = document.styleSheets[n]; var s = document.styleSheets[n];
for( var m = 0; m < s.rules.length; ++m ){ for( var m = 0; m < s.rules.length; ++m ){
if( s.rules[m].selectorText == ':root' ){ if( s.rules[m].selectorText == ':root' ){
style = s.rules[m].style; style = s.rules[m].style;
break; break;
}
} }
} break;
break;
} }
} }
if( !style ){ return style;
alert( 'Theme stylesheet for variant "' + variant + '" not set or found' ); },
return;
changeColor: function( c, without_prompt ){
without_prompt = without_prompt || false;
var read_style = this.findLoadedStylesheet( 'custom-variant-style' );
var write_style = this.findLoadedStylesheet( 'variant-style' );
if( !read_style ){
read_style = write_style;
} }
var e = this.findColor( c ); var e = this.findColor( c );
var p = this.normalizeColor( style.getPropertyValue( '--'+c ) ).replace( '--INTERNAL-', '--' ); var p = this.normalizeColor( read_style.getPropertyValue( '--'+c ) ).replace( '--INTERNAL-', '--' );
var f = this.getColorValue( e.fallback ); var f = this.getColorValue( e.fallback );
var v = this.getColorValue( e.name ); var v = this.getColorValue( e.name );
@ -196,21 +288,35 @@ var variants = {
v = p; v = p;
} }
var t = c + '\n\n' + e.tooltip + '\n'; var n = '';
if( e.fallback ){ if( without_prompt ){
t += '\nInherits value "' + f + '" from ' + e.fallback + ' if not set\n'; n = v;
} }
if( e.default ){ else{
t += '\nDefaults to value "' + this.normalizeColor(e.default) + '" if not set\n'; var t = c + '\n\n' + e.tooltip + '\n';
if( e.fallback ){
t += '\nInherits value "' + f + '" from ' + e.fallback + ' if not set\n';
}
if( e.default ){
t += '\nDefaults to value "' + this.normalizeColor(e.default) + '" if not set\n';
}
n = prompt( t, v );
} }
var n = prompt( t, v );
if( n ){ if( n ){
n = this.normalizeColor( n ).replace( '--INTERNAL-', '--' ).replace( '--', '--INTERNAL-' ); n = this.normalizeColor( n ).replace( '--INTERNAL-', '--' ).replace( '--', '--INTERNAL-' );
style.setProperty( '--'+c, n ); if( without_prompt || n != v ){
write_style.setProperty( '--'+c, n );
}
if( !without_prompt ){
this.saveCustomVariant();
}
} }
else if( n !== null){ else if( n !== null){
style.removeProperty( '--'+c ); write_style.removeProperty( '--'+c );
if( !without_prompt ){
this.saveCustomVariant();
}
} }
}, },
@ -239,7 +345,7 @@ var variants = {
generateStylesheet: function(){ generateStylesheet: function(){
var style = var style =
'/* ' + this.customvariant + ' */\n' + '/* ' + this.customvariantname + ' */\n' +
':root {\n' + ':root {\n' +
this.variantvariables.sort( function( l, r ){ return l.name.localeCompare(r.name); } ).reduce( function( a, e ){ return a + this.generateColorVariable( e ); }.bind( this ), '' ) + this.variantvariables.sort( function( l, r ){ return l.name.localeCompare(r.name); } ).reduce( function( a, e ){ return a + this.generateColorVariable( e ); }.bind( this ), '' ) +
'}\n'; '}\n';