review code of gdpr banner

This commit is contained in:
Massimo Maggioni 2022-02-18 22:46:09 +01:00
parent 081040572a
commit e89e2d3750
17 changed files with 231 additions and 125 deletions

View File

@ -22,3 +22,17 @@ body {
scrollbar-color: var(--scrollbar-thumb) transparent; scrollbar-color: var(--scrollbar-thumb) transparent;
} }
/**/ /**/
/* scrollbar styles for Chromium */
::-webkit-scrollbar {
height: auto;
}
::-webkit-scrollbar-thumb {
background-color: var(--scrollbar-thumb);
}
::-webkit-scrollbar-track {
background-color: transparent;
}
/**/

View File

@ -123,7 +123,6 @@
} }
.article-page.has-toc { .article-page.has-toc {
.left-sidebar { .left-sidebar {
display: none; display: none;
} }
@ -395,6 +394,41 @@
} }
} }
.highlight {
background-color: var(--pre-background-color);
padding: var(--card-padding);
position: relative;
&:hover {
.copyCodeButton {
opacity: 1;
}
}
pre {
margin: initial;
padding: 0;
margin: 0;
width: auto;
}
}
.copyCodeButton {
position: absolute;
top: calc(var(--card-padding));
right: calc(var(--card-padding));
background: var(--card-background);
border: none;
box-shadow: var(--shadow-l2);
border-radius: var(--tag-border-radius);
padding: 8px 16px;
color: var(--card-text-color-main);
cursor: pointer;
font-size: 14px;
opacity: 0;
transition: opacity 0.3s ease;
}
.table-wrapper { .table-wrapper {
padding: 0 var(--card-padding); padding: 0 var(--card-padding);
overflow-x: auto; overflow-x: auto;
@ -449,6 +483,7 @@
/// Negative margins /// Negative margins
blockquote, blockquote,
figure, figure,
.highlight,
pre, pre,
.gallery, .gallery,
.video-wrapper, .video-wrapper,
@ -458,30 +493,4 @@
margin-right: calc((var(--card-padding)) * -1); margin-right: calc((var(--card-padding)) * -1);
width: calc(100% + var(--card-padding) * 2); width: calc(100% + var(--card-padding) * 2);
} }
.highlight {
position: relative;
&:hover {
.copyCodeButton {
opacity: 1;
}
}
}
.copyCodeButton {
position: absolute;
top: calc(var(--card-padding));
right: 0;
background: var(--card-background);
border: none;
box-shadow: var(--shadow-l2);
border-radius: var(--tag-border-radius);
padding: 8px 16px;
color: var(--card-text-color-main);
cursor: pointer;
font-size: 14px;
opacity: 0;
transition: opacity 0.3s ease;
}
} }

View File

@ -1,19 +1,6 @@
$defaultTagBackgrounds: #8ea885, #df7988, #0177b8, #ffb900, #6b69d6; $defaultTagBackgrounds: #8ea885, #df7988, #0177b8, #ffb900, #6b69d6;
$defaultTagColors: #fff, #fff, #fff, #fff, #fff; $defaultTagColors: #fff, #fff, #fff, #fff, #fff;
[data-scheme="light"] {
--pre-text-color: #272822;
--pre-background-color: #fafafa;
@import "partials/highlight/light.scss";
}
[data-scheme="dark"] {
color-scheme: dark;
--pre-text-color: #f8f8f2;
--pre-background-color: #272822;
@import "partials/highlight/dark.scss";
}
/* /*
* Global style * Global style
*/ */
@ -46,7 +33,7 @@ $defaultTagColors: #fff, #fff, #fff, #fff, #fff;
--accent-color-darker: #bdc3c7; --accent-color-darker: #bdc3c7;
--accent-color-text: #000; --accent-color-text: #000;
--body-text-color: rgba(255, 255, 255, 0.7); --body-text-color: rgba(255, 255, 255, 0.7);
--scrollbar-thumb: #424242; --scrollbar-thumb: hsl(0, 0%, 40%);
--scrollbar-track: var(--body-background); --scrollbar-track: var(--body-background);
} }
} }
@ -160,3 +147,15 @@ $defaultTagColors: #fff, #fff, #fff, #fff, #fff;
--shadow-l4: 0px 24px 32px rgba(0, 0, 0, 0.04), 0px 16px 24px rgba(0, 0, 0, 0.04), 0px 4px 8px rgba(0, 0, 0, 0.04), --shadow-l4: 0px 24px 32px rgba(0, 0, 0, 0.04), 0px 16px 24px rgba(0, 0, 0, 0.04), 0px 4px 8px rgba(0, 0, 0, 0.04),
0px 0px 1px rgba(0, 0, 0, 0.04); 0px 0px 1px rgba(0, 0, 0, 0.04);
} }
[data-scheme="light"] {
--pre-text-color: #272822;
--pre-background-color: #fafafa;
@import "partials/highlight/light.scss";
}
[data-scheme="dark"] {
--pre-text-color: #f8f8f2;
--pre-background-color: #272822;
@import "partials/highlight/dark.scss";
}

View File

@ -57,6 +57,60 @@ class StackGallery {
} }
public static createGallery(container: HTMLElement) { public static createGallery(container: HTMLElement) {
/// The process of wrapping image with figure tag is done using JavaScript instead of only Hugo markdown render hook
/// because it can not detect whether image is being wrapped by a link or not
/// and it lead to a invalid HTML construction (<a><figure><img></figure></a>)
const images = container.querySelectorAll('img');
for (const img of Array.from(images)) {
/// Images are wrapped with figure tag if the paragraph has only images without texts
/// This is done to allow inline images within paragraphs
const paragraph = img.closest('p');
if (!paragraph || !container.contains(paragraph)) continue;
if (paragraph.textContent.trim() == '') {
/// Once we insert figcaption, this check no longer works
/// So we add a class to paragraph to mark it
paragraph.classList.add('no-text');
}
let isNewLineImage = paragraph.classList.contains('no-text');
if (!isNewLineImage) continue;
const hasLink = img.parentElement.tagName == 'A';
let el: HTMLElement = img;
/// Wrap image with figure tag, with flex-grow and flex-basis values extracted from img's data attributes
const figure = document.createElement('figure');
figure.style.setProperty('flex-grow', img.getAttribute('data-flex-grow') || '1');
figure.style.setProperty('flex-basis', img.getAttribute('data-flex-basis') || '0');
if (hasLink) {
/// Wrap <a> if it exists
el = img.parentElement;
}
el.parentElement.insertBefore(figure, el);
figure.appendChild(el);
/// Add figcaption if it exists
if (img.hasAttribute('alt')) {
const figcaption = document.createElement('figcaption');
figcaption.innerText = img.getAttribute('alt');
figure.appendChild(figcaption);
}
/// Wrap img tag with <a> tag if image was not wrapped by <a> tag
if (!hasLink) {
figure.className = 'gallery-image';
const a = document.createElement('a');
a.href = img.src;
a.setAttribute('target', '_blank');
img.parentNode.insertBefore(a, img);
a.appendChild(img);
}
}
const figuresEl = container.querySelectorAll('figure.gallery-image'); const figuresEl = container.querySelectorAll('figure.gallery-image');
let currentGallery = []; let currentGallery = [];

View File

@ -62,20 +62,21 @@ let Stack = {
/** /**
* Add copy button to code block * Add copy button to code block
*/ */
const codeBlocks = document.querySelectorAll('.article-content > div.highlight'); const highlights = document.querySelectorAll('.article-content div.highlight');
const copyText = `Copy`, const copyText = `Copy`,
copiedText = `Copied!`; copiedText = `Copied!`;
codeBlocks.forEach(codeBlock => {
highlights.forEach(highlight => {
const copyButton = document.createElement('button'); const copyButton = document.createElement('button');
copyButton.innerHTML = copyText; copyButton.innerHTML = copyText;
copyButton.classList.add('copyCodeButton'); copyButton.classList.add('copyCodeButton');
codeBlock.appendChild(copyButton); highlight.appendChild(copyButton);
const pre = codeBlock.getElementsByTagName('pre'); const codeBlock = highlight.querySelector('code[data-lang]');
const code = pre[0].textContent; if (!codeBlock) return;
copyButton.addEventListener('click', () => { copyButton.addEventListener('click', () => {
navigator.clipboard.writeText(code) navigator.clipboard.writeText(codeBlock.textContent)
.then(() => { .then(() => {
copyButton.textContent = copiedText; copyButton.textContent = copiedText;

View File

@ -1,7 +1,7 @@
plugins: plugins:
- name: gtag - name: gtag
enabled: true enabled: true
functional: false optional: true
title: Google Analytics title: Google Analytics
description: This code gives us insight into the number of people that visit our website, where they are from and what they are clicking on. We follow the guidelines of the Italian Government. description: This code gives us insight into the number of people that visit our website, where they are from and what they are clicking on. We follow the guidelines of the Italian Government.
html_src: gdpr/gtag.html html_src: gdpr/gtag.html

View File

@ -4,6 +4,8 @@ theme: hugo-theme-stack
paginate: 5 paginate: 5
title: Example Site title: Example Site
# Change it to your Disqus shortname before using # Change it to your Disqus shortname before using
disqusShortname: hugo-theme-stack disqusShortname: hugo-theme-stack
@ -29,9 +31,10 @@ params:
rssFullContent: true rssFullContent: true
favicon: favicon:
GDPRbanner: # mode: nocookie, functional, consent
enabled: true GDPR:
onlyfunctional: false enable: true
mode: consent
footer: footer:
since: 2020 since: 2020

View File

@ -162,3 +162,7 @@ X<sup>n</sup> + Y<sup>n</sup> = Z<sup>n</sup>
Press <kbd><kbd>CTRL</kbd>+<kbd>ALT</kbd>+<kbd>Delete</kbd></kbd> to end the session. Press <kbd><kbd>CTRL</kbd>+<kbd>ALT</kbd>+<kbd>Delete</kbd></kbd> to end the session.
Most <mark>salamanders</mark> are nocturnal, and hunt for insects, worms, and other small creatures. Most <mark>salamanders</mark> are nocturnal, and hunt for insects, worms, and other small creatures.
## Hyperlinked image
[![Google](https://www.google.com/images/branding/googlelogo/1x/googlelogo_light_color_272x92dp.png)](https://google.com)

View File

@ -36,3 +36,7 @@ Hugo ships with several [Built-in Shortcodes](https://gohugo.io/content-manageme
## bilibilibi Shortcode ## bilibilibi Shortcode
{{< bilibili av498363026 >}} {{< bilibili av498363026 >}}
## Gist Shortcode
{{< gist spf13 7896402 >}}

2
go.mod
View File

@ -1,3 +1,3 @@
module github.com/CaiJimmy/hugo-theme-stack module github.com/CaiJimmy/hugo-theme-stack/v3
go 1.12 go 1.12

View File

@ -63,8 +63,8 @@ search:
other: "#PAGES_COUNT pages (#TIME_SECONDS seconds)" other: "#PAGES_COUNT pages (#TIME_SECONDS seconds)"
footer: footer:
GDPRsettings: GDPR:
modify: Manage GDPR settings consent: Manage GDPR settings
functional: This website use only functional cookies functional: This website use only functional cookies
nocookie: This website doesn't use any cookie and doesn't collect any personal data nocookie: This website doesn't use any cookie and doesn't collect any personal data

View File

@ -62,8 +62,8 @@ search:
other: "#PAGES_COUNT pagine (#TIME_SECONDS secondi)" other: "#PAGES_COUNT pagine (#TIME_SECONDS secondi)"
footer: footer:
GDPRsettings: GDPR:
other: Gestisci il consenso GDPR consent: Gestisci il consenso GDPR
functional: Questo sito web utilizza solo cookie funzionali functional: Questo sito web utilizza solo cookie funzionali
nocookie: Questo sito web non usa cookie e non raccoglie dati personali nocookie: Questo sito web non usa cookie e non raccoglie dati personali

View File

@ -25,22 +25,17 @@
{{- end -}} {{- end -}}
{{- end -}} {{- end -}}
<figure <img src="{{ $Permalink }}"
{{ if $galleryImage }}
class="gallery-image"
style="
flex-grow: {{ div (mul $image.Width 100) $image.Height }};
flex-basis: {{ div (mul $image.Width 240) $image.Height }}px"
{{ end }}>
<a href="{{ $Permalink }}" {{ if $galleryImage }}data-size="{{ $image.Width }}x{{ $image.Height }}"{{ end }}>
<img src="{{ $Permalink }}"
{{ with $Width }}width="{{ . }}"{{ end }} {{ with $Width }}width="{{ . }}"{{ end }}
{{ with $Height }}height="{{ . }}"{{ end }} {{ with $Height }}height="{{ . }}"{{ end }}
{{ with $Srcset }}srcset="{{ . }}"{{ end }} {{ with $Srcset }}srcset="{{ . }}"{{ end }}
loading="lazy" loading="lazy"
{{ with $alt }}alt="{{ . }}"{{ end }}>
</a>
{{ with $alt }} {{ with $alt }}
<figcaption>{{ . | markdownify }}</figcaption> alt="{{ . }}"
{{ end }} {{ end }}
</figure> {{ if $galleryImage }}
class="gallery-image"
data-flex-grow="{{ div (mul $image.Width 100) $image.Height }}"
data-flex-basis="{{ div (mul $image.Width 240) $image.Height }}px"
{{ end }}
>

View File

@ -1,3 +1,3 @@
{{ if and .Site.Params.GDPRbanner.enabled (not .Site.Params.GDPRbanner.onlyfunctional) }} {{ if and .Site.Params.GDPR.enable (not (eq .Site.Params.GDPR.mode "nocookie")) }}
{{ partial "gdpr/banner.html" . }} {{ partial "gdpr/banner.html" . }}
{{ end }} {{ end }}

View File

@ -1,6 +1,5 @@
{{- $ThemeVersion := "3.7.0" -}} {{- $ThemeVersion := "3.8.0" -}}
<footer class="site-footer"> <footer class="site-footer">
<section class="copyright"> <section class="copyright">
&copy; &copy;
{{ if and (.Site.Params.footer.since) (ne .Site.Params.footer.since (int (now.Format "2006"))) }} {{ if and (.Site.Params.footer.since) (ne .Site.Params.footer.since (int (now.Format "2006"))) }}
@ -9,17 +8,19 @@
{{ now.Format "2006" }} {{ .Site.Title }} {{ now.Format "2006" }} {{ .Site.Title }}
</section> </section>
{{ if .Site.Params.GDPR.enable }}
<section class="GDPRbanner"> <section class="GDPRbanner">
{{ if .Site.Params.GDPRbanner.enabled }} {{ if (eq .Site.Params.GDPR.mode "consent") }}
{{ if not .Site.Params.GDPRbanner.onlyfunctional }} <a class="manage-consent" href="#manage-consent">{{ T "footer.GDPR.consent" }}</a>
<a class="manage-consent" href="#manage-consent">{{ T "footer.GDPRsettings.modify" }}</a>
{{ else }} {{ else }}
{{ T "footer.GDPRsettings.functional" }} {{ if (eq .Site.Params.GDPR.mode "functional") }}
{{ T "footer.GDPR.functional" }}
{{ else }}
{{ T "footer.GDPR.nocookie" }}
{{ end }} {{ end }}
{{ else }}
{{ T "footer.GDPRsettings.nocookie" }}
{{ end }} {{ end }}
</section> </section>
{{ end }}
<section class="powerby"> <section class="powerby">
{{ with .Site.Params.footer.customText }} {{ with .Site.Params.footer.customText }}

View File

@ -1,5 +1,24 @@
<!-- GDPR banner visual style --> <!-- if all enabled plugins are not optional instead use mode = functionl in config file-->
<style> {{ range $index, $plugin := .Site.Data.gdpr.plugins }}
{{ if and $plugin.enabled (not $plugin.optional) }}
{{- errorf "All enabled plugins are not optional, use mode = functional in config file" -}}
{{ end }}
{{ end }}
<!-- import and execute scripts because all are functional -->
{{ if eq .Site.Params.GDPR.mode "functional" }}
{{ range $index, $plugin := .Site.Data.gdpr.plugins }}
{{ if $plugin.enabled }}
{{ partial $plugin.html_src }}
<script type="text/javascript" src="/js/{{ $plugin.js_src }}"></script>
{{ end }}
{{ end }}
{{ end }}
<!-- import and execute scripts based on user consent -->
{{ if eq .Site.Params.GDPR.mode "consent" }}
<!-- GDPR banner visual style -->
<style>
#consent-notice {padding: 1rem 1rem; display: none; text-align: center; position: fixed; bottom: 0; width: calc(100% - 2rem); background: #222; color: rgba(255,255,255,0.8);} #consent-notice {padding: 1rem 1rem; display: none; text-align: center; position: fixed; bottom: 0; width: calc(100% - 2rem); background: #222; color: rgba(255,255,255,0.8);}
#consent-notice span {margin-right: 1rem;} #consent-notice span {margin-right: 1rem;}
#consent-notice button {cursor: pointer; display: inline-block; width: auto;} #consent-notice button {cursor: pointer; display: inline-block; width: auto;}
@ -26,31 +45,23 @@
#consent-notice span {display: block; padding-top: 3px; margin-bottom: 1.5rem;} #consent-notice span {display: block; padding-top: 3px; margin-bottom: 1.5rem;}
#consent-notice button.btn {position: relative; bottom: 4px;} #consent-notice button.btn {position: relative; bottom: 4px;}
} }
</style> </style>
<!-- import scripts: full if functional, partially if depends on user consent --> <!-- preferences dialog -->
{{ range $index, $plugin := .Site.Data.gdpr.plugins }} <div id="consent-notice">
{{ if $plugin.enabled }} <span>We would like to use <a class="manage-consent" href="#manage-consent">third party code</a> to improve the functionality of this website.</span>
{{ "<!-- " }} {{ $plugin.name }} {{ "start -->" }} <button class="btn manage-consent">Manage preferences</button>
{{ partial $plugin.html_src }} <button class="btn deny-consent">Deny</button>
{{ if $plugin.functional }} <button class="btn approve-consent">Allow</button>
<script type="text/javascript" src="/js/{{ $plugin.js_src }}"></script> </div>
{{ end }} <div id="consent-overlay">
{{ "<!-- " }} {{ $plugin.name }} {{ "end -->" }}
{{ end }}
{{ end }}
<!-- preferences dialog -->
<div id="consent-notice"><span>We would like to use <a class="manage-consent" href="#manage-consent">third party code</a> to improve the functionality of this website.</span><button class="btn manage-consent">Manage preferences</button><button class="btn deny-consent">Deny</button><button class="btn approve-consent">Allow</button></div>
<div id="consent-overlay">
<div> <div>
{{ range $index, $plugin := .Site.Data.gdpr.plugins }} {{ range $index, $plugin := .Site.Data.gdpr.plugins }}
{{ if $plugin.enabled }} {{ if $plugin.enabled }}
<div> <div>
<input type="checkbox" id="item{{ $index }}" value="1" name="item{{ $index }}" {{ if $plugin.functional }}checked disabled{{ end }} /> <input type="checkbox" id="item{{ $index }}" value="1" name="item{{ $index }}" {{ if not $plugin.optional }}checked disabled{{ end }} />
<label for="item{{ $index }}"> <label for="item{{ $index }}">
<h3>{{ $plugin.title }}{{ if $plugin.functional}} (functional){{ end }}</h3> <h3>{{ $plugin.title }}{{ if not $plugin.optional}} (functional){{ end }}</h3>
<p>{{ $plugin.description }}</p> <p>{{ $plugin.description }}</p>
</label> </label>
</div> </div>
@ -61,13 +72,23 @@
<button class="btn approve-consent">Allow all</button> <button class="btn approve-consent">Allow all</button>
</div> </div>
</div> </div>
</div> </div>
<!-- execute user consent scripts --> <!-- import scripts library with no really action -->
<script> {{ range $index, $plugin := .Site.Data.gdpr.plugins }}
{{ if $plugin.enabled }}
{{ partial $plugin.html_src }}
{{ if not $plugin.optional }}
<script type="text/javascript" src="/js/{{ $plugin.js_src }}"></script>
{{ end }}
{{ end }}
{{ end }}
<!-- really execute scripts based on user consent -->
<script>
const scripts = []; const scripts = [];
{{ range $index, $plugin := .Site.Data.gdpr.plugins }} {{ range $index, $plugin := .Site.Data.gdpr.plugins }}
{{ if and $plugin.enabled (not $plugin.functional) }} {{ if and $plugin.enabled ($plugin.optional) }}
scripts[{{ $index }}] = "/js/{{ $plugin.js_src }}"; scripts[{{ $index }}] = "/js/{{ $plugin.js_src }}";
{{ end }} {{ end }}
{{ end }} {{ end }}
@ -183,4 +204,5 @@
this.classList.toggle('active'); this.classList.toggle('active');
} }
}); });
</script> </script>
{{ end }}

View File

@ -21,7 +21,7 @@
<link rel="shortcut icon" href="{{ . }}" /> <link rel="shortcut icon" href="{{ . }}" />
{{ end }} {{ end }}
{{ if not .Site.Params.GDPRbanner.enabled }} {{ if not .Site.Params.GDPR.enable }}
{{- template "_internal/google_analytics.html" . -}} {{- template "_internal/google_analytics.html" . -}}
{{ end }} {{ end }}
{{- partial "head/custom.html" . -}} {{- partial "head/custom.html" . -}}