<script src="/static/david/js/instantpage-3.0.0.min.js" type="module" defer></script> | <script src="/static/david/js/instantpage-3.0.0.min.js" type="module" defer></script> | ||||
<template id="theme-selector"> | <template id="theme-selector"> | ||||
<form id="chosen-color-scheme"> | |||||
<form> | |||||
<fieldset> | <fieldset> | ||||
<legend>Thème</legend> | <legend>Thème</legend> | ||||
<label> | <label> | ||||
/* This is a work in progress with Anthony https://ricaud.me */ | /* This is a work in progress with Anthony https://ricaud.me */ | ||||
// TODISCUSS: | // TODISCUSS: | ||||
// 1. give a way for the user to close that choice? | |||||
// 2. store preferences? (in a cookie or localstorage) | |||||
// 1. give a way for the user to close that chooser? | |||||
// 2. store preferences? (in a cookie or localstorage or sessionStorage) | |||||
// 3. create the template from scratch in JS? | // 3. create the template from scratch in JS? | ||||
// 4. how to deal with multiple rules by stylesheet? | |||||
const themeSelectorTemplate = document.querySelector('#theme-selector') | |||||
const clone = themeSelectorTemplate.content.cloneNode(true) | |||||
const firstChild = clone.firstElementChild | |||||
document.body.insertAdjacentElement('afterbegin', firstChild) | |||||
const form = document.querySelector('form#chosen-color-scheme') | |||||
form.addEventListener('change', (e) => { | |||||
const chosenColorScheme = e.target.value | |||||
document.body.classList.toggle('forced-dark', chosenColorScheme === 'dark') | |||||
document.body.classList.toggle( | |||||
'forced-light', | |||||
chosenColorScheme === 'light' | |||||
// 4. how to make it generic? (no need for forced-dark/light existing rules) | |||||
// Results from a Poll: https://mastodon.social/@dav/104093540923157521 | |||||
// Avoir un moyen de changer de thème d'un site à la main : | |||||
// 49% => Utile | |||||
// 23% => Pas utile | |||||
// 9% => So 2000 | |||||
// 19% => Je veux le même | |||||
// After 24 hours and 43 votes | |||||
// A bit hard to interpret, I guess people wanting it found it useful too! | |||||
function loadThemeForm(templateName) { | |||||
const themeSelectorTemplate = document.querySelector(templateName) | |||||
const clone = themeSelectorTemplate.content.cloneNode(true) | |||||
const form = document.body.insertAdjacentElement( | |||||
'afterbegin', | |||||
clone.firstElementChild | |||||
) | ) | ||||
}) | |||||
form.addEventListener('change', (e) => { | |||||
const chosenColorScheme = e.target.value | |||||
document.body.classList.toggle( | |||||
'forced-dark', | |||||
chosenColorScheme === 'dark' | |||||
) | |||||
document.body.classList.toggle( | |||||
'forced-light', | |||||
chosenColorScheme === 'light' | |||||
) | |||||
}) | |||||
} | |||||
const mediaQueryTest = '(prefers-color-scheme: dark)' | const mediaQueryTest = '(prefers-color-scheme: dark)' | ||||
window.addEventListener('load', () => { | window.addEventListener('load', () => { | ||||
// TODO: do not display the form if there is no related media CSS rule. | |||||
for (const styleSheet of [...document.styleSheets]) { | |||||
let mediaRules = [] | |||||
let ruleFound = false | |||||
for (const styleSheet of Array.from(document.styleSheets)) { | |||||
for (const cssRule of styleSheet.cssRules) { | for (const cssRule of styleSheet.cssRules) { | ||||
if (cssRule.type !== CSSRule.MEDIA_RULE) continue | if (cssRule.type !== CSSRule.MEDIA_RULE) continue | ||||
// Warning, Safari does not have/supports `conditionText`. | |||||
// WARNING: Safari does not have/supports `conditionText`. | |||||
if (cssRule.conditionText) { | if (cssRule.conditionText) { | ||||
if (cssRule.conditionText !== mediaQueryTest) continue | if (cssRule.conditionText !== mediaQueryTest) continue | ||||
} else { | } else { | ||||
if (cssRule.cssText.startsWith(mediaQueryTest)) continue | if (cssRule.cssText.startsWith(mediaQueryTest)) continue | ||||
} | } | ||||
// At that point, only a media cssRule matching our media query | |||||
// test should remain, hence the renaming. | |||||
const mediaRule = cssRule | const mediaRule = cssRule | ||||
for (const innerCssRule of mediaRule.cssRules) { | for (const innerCssRule of mediaRule.cssRules) { | ||||
styleSheet.insertRule(innerCssRule.cssText) | |||||
mediaRules.push(innerCssRule) | |||||
ruleFound = true | |||||
} | } | ||||
// TODO: what if there are more than one declaration? | |||||
break | |||||
} | } | ||||
// WARNING: do not try to insert a Rule to a styleSheet you are | |||||
// currently iterating on, otherwise the browser will be stuck | |||||
// in a infinite loop… | |||||
for (const mediaRule of mediaRules) { | |||||
styleSheet.insertRule(mediaRule.cssText) | |||||
} | |||||
mediaRules = [] | |||||
} | } | ||||
if (ruleFound) loadThemeForm('#theme-selector') | |||||
}) | }) | ||||
</script> | </script> | ||||
</body> | </body> | ||||
</html> | </html> |
</p> | </p> | ||||
</footer> | </footer> | ||||
<script src="/static/david/js/instantpage-3.0.0.min.js" type="module" defer></script> | <script src="/static/david/js/instantpage-3.0.0.min.js" type="module" defer></script> | ||||
<template id="theme-selector"> | |||||
<form id="chosen-color-scheme"> | |||||
<fieldset> | |||||
<legend>Thème</legend> | |||||
<label> | |||||
<input type="radio" value="auto" name="chosen-color-scheme" checked> Auto | |||||
</label> | |||||
<label> | |||||
<input type="radio" value="dark" name="chosen-color-scheme"> Foncé | |||||
</label> | |||||
<label> | |||||
<input type="radio" value="light" name="chosen-color-scheme"> Clair | |||||
</label> | |||||
</fieldset> | |||||
</form> | |||||
</template> | |||||
<script type="text/javascript"> | |||||
/* This is a work in progress with Anthony https://ricaud.me */ | |||||
// TODISCUSS: | |||||
// 1. give a way for the user to close that choice? | |||||
// 2. store preferences? (in a cookie or localstorage) | |||||
// 3. create the template from scratch in JS? | |||||
// 4. how to deal with multiple rules by stylesheet? | |||||
const themeSelectorTemplate = document.querySelector('#theme-selector') | |||||
const clone = themeSelectorTemplate.content.cloneNode(true) | |||||
const firstChild = clone.firstElementChild | |||||
document.body.insertAdjacentElement('afterbegin', firstChild) | |||||
const form = document.querySelector('form#chosen-color-scheme') | |||||
form.addEventListener('change', (e) => { | |||||
const chosenColorScheme = e.target.value | |||||
document.body.classList.toggle('forced-dark', chosenColorScheme === 'dark') | |||||
document.body.classList.toggle( | |||||
'forced-light', | |||||
chosenColorScheme === 'light' | |||||
) | |||||
}) | |||||
const mediaQueryTest = '(prefers-color-scheme: dark)' | |||||
window.addEventListener('load', () => { | |||||
// TODO: do not display the form if there is no related media CSS rule. | |||||
for (const styleSheet of [...document.styleSheets]) { | |||||
for (const cssRule of styleSheet.cssRules) { | |||||
if (cssRule.type !== CSSRule.MEDIA_RULE) continue | |||||
// Warning, Safari does not have/supports `conditionText`. | |||||
if (cssRule.conditionText) { | |||||
if (cssRule.conditionText !== mediaQueryTest) continue | |||||
} else { | |||||
if (cssRule.cssText.startsWith(mediaQueryTest)) continue | |||||
} | |||||
const mediaRule = cssRule | |||||
for (const innerCssRule of mediaRule.cssRules) { | |||||
styleSheet.insertRule(innerCssRule.cssText) | |||||
} | |||||
// TODO: what if there are more than one declaration? | |||||
break | |||||
} | |||||
} | |||||
}) | |||||
</script> | |||||
{% block extra_bottom %}{% endblock extra_bottom %} | |||||
</body> | </body> | ||||
</html> | </html> |
</article> | </article> | ||||
{% endblock content %} | {% endblock content %} | ||||
{% block extra_bottom %} | |||||
<template id="theme-selector"> | |||||
<form> | |||||
<fieldset> | |||||
<legend>Thème</legend> | |||||
<label> | |||||
<input type="radio" value="auto" name="chosen-color-scheme" checked> Auto | |||||
</label> | |||||
<label> | |||||
<input type="radio" value="dark" name="chosen-color-scheme"> Foncé | |||||
</label> | |||||
<label> | |||||
<input type="radio" value="light" name="chosen-color-scheme"> Clair | |||||
</label> | |||||
</fieldset> | |||||
</form> | |||||
</template> | |||||
<script type="text/javascript"> | |||||
/* This is a work in progress with Anthony https://ricaud.me */ | |||||
// TODISCUSS: | |||||
// 1. give a way for the user to close that chooser? | |||||
// 2. store preferences? (in a cookie or localstorage or sessionStorage) | |||||
// 3. create the template from scratch in JS? | |||||
// 4. how to make it generic? (no need for forced-dark/light existing rules) | |||||
// Results from a Poll: https://mastodon.social/@dav/104093540923157521 | |||||
// Avoir un moyen de changer de thème d'un site à la main : | |||||
// 49% => Utile | |||||
// 23% => Pas utile | |||||
// 9% => So 2000 | |||||
// 19% => Je veux le même | |||||
// After 24 hours and 43 votes | |||||
// A bit hard to interpret, I guess people wanting it found it useful too! | |||||
function loadThemeForm(templateName) { | |||||
const themeSelectorTemplate = document.querySelector(templateName) | |||||
const clone = themeSelectorTemplate.content.cloneNode(true) | |||||
const form = document.body.insertAdjacentElement( | |||||
'afterbegin', | |||||
clone.firstElementChild | |||||
) | |||||
form.addEventListener('change', (e) => { | |||||
const chosenColorScheme = e.target.value | |||||
document.body.classList.toggle( | |||||
'forced-dark', | |||||
chosenColorScheme === 'dark' | |||||
) | |||||
document.body.classList.toggle( | |||||
'forced-light', | |||||
chosenColorScheme === 'light' | |||||
) | |||||
}) | |||||
} | |||||
const mediaQueryTest = '(prefers-color-scheme: dark)' | |||||
window.addEventListener('load', () => { | |||||
let mediaRules = [] | |||||
let ruleFound = false | |||||
for (const styleSheet of Array.from(document.styleSheets)) { | |||||
for (const cssRule of styleSheet.cssRules) { | |||||
if (cssRule.type !== CSSRule.MEDIA_RULE) continue | |||||
// WARNING: Safari does not have/supports `conditionText`. | |||||
if (cssRule.conditionText) { | |||||
if (cssRule.conditionText !== mediaQueryTest) continue | |||||
} else { | |||||
if (cssRule.cssText.startsWith(mediaQueryTest)) continue | |||||
} | |||||
// At that point, only a media cssRule matching our media query | |||||
// test should remain, hence the renaming. | |||||
const mediaRule = cssRule | |||||
for (const innerCssRule of mediaRule.cssRules) { | |||||
mediaRules.push(innerCssRule) | |||||
ruleFound = true | |||||
} | |||||
} | |||||
// WARNING: do not try to insert a Rule to a styleSheet you are | |||||
// currently iterating on, otherwise the browser will be stuck | |||||
// in a infinite loop… | |||||
for (const mediaRule of mediaRules) { | |||||
styleSheet.insertRule(mediaRule.cssText) | |||||
} | |||||
mediaRules = [] | |||||
} | |||||
if (ruleFound) loadThemeForm('#theme-selector') | |||||
}) | |||||
</script> | |||||
{% endblock extra_bottom %} |