Skip to content

Commit

Permalink
Add CSS classes for consent sections
Browse files Browse the repository at this point in the history
- refactor global object references
- update sandbox html
- Add CSS section to README
  • Loading branch information
replete committed Jun 23, 2024
1 parent 19cef7e commit 5ca4022
Show file tree
Hide file tree
Showing 7 changed files with 125 additions and 99 deletions.
15 changes: 12 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

#### [View demo](https://replete.github.io/biscuitman)

I didn't like sending 100KB+ for a simple cookie consent solution so I wrote this. It's currently **3.1kB/br or 3.6kB/gz**, including CSS. It's designed to be as small as possible with an adequate featureset for basic website cookie consent.
I didn't like sending 100KB+ for a simple cookie consent solution so I wrote this. It's currently **3.3kB/br and 3.8kB/gz**, including CSS. It's designed to be as small as possible with an adequate featureset for basic website cookie consent.

- Stores consent in `localStorage`, exposes in `window.Consent` and through custom events fired on `document`
- Handles consent granulated by custom sections (e.g. essential, performance, analytics...)
Expand All @@ -18,12 +18,15 @@ I didn't like sending 100KB+ for a simple cookie consent solution so I wrote thi
- Mobile-first
- Browser support: >= 2% browserlist (No IE support, but its not impossible)
- Written with latest CSS / JS features and targetted to >= 2% using browserlist
- show more functionality for long disclaimer text
- link for privacy policy in any string
- 'show more' functionality for long disclaimer text
- include optional link in any text
- CSS classes on `<html>` element for consents

![screenshot of main UI](media/ui.webp)

## How to use
[View demo](https://replete.github.io/biscuitman) for a more detailed example

```html
<!--
1. Prepare script tags
Expand Down Expand Up @@ -143,6 +146,12 @@ html:not(:has(.bm-hide))::after {
- `bmOpen()` – Opens My Consent Settings modal
- example usage: `<a href="javascript:bmOpen();"> Update my consent settings</a>`

## CSS

- `biscuitman` class is used for the main UI
- consents are added to `<html>` with the convention of `bm-{sectionName}` for granted and `bm-no-{sectionName}` for ungranted
- `bm-hide` is used on the UI container

## Events

The easiest way to see how events work is to view the `console.debug()` calls in the [demo](https://replete.github.io/biscuitman)
Expand Down
50 changes: 35 additions & 15 deletions biscuitman.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
((d, w, Object, bm)=>{
((d, w, Object, h, bm)=>{
const defaults = {
key: 'myconsent',
global: 'Consent',
Expand Down Expand Up @@ -76,7 +76,7 @@
}
</p>
${o.sections.map(section => {
let hasConsent = w[o.global][section]
let hasConsent = getConsents()[section]
let isEssential = section === 'essential'
let isDisabled = isEssential ? 'disabled' : ''
let isChecked = isEssential ? 'checked' : ''
Expand Down Expand Up @@ -116,7 +116,17 @@
d.body.appendChild(ui)
}

const displayUI = (show) => ui.classList[show ? 'remove' : 'add']('bm-hide')
const displayUI = (show) => ui.classList.toggle('bm-hide', !show)

const applyCssClasses = () => {
const { consentTime, ...consents} = getConsents()
if (!consentTime) h.className = h.className.replace(/\bbm-[^\s]+(\s+|$)/g, '').trim();

for (let [name, granted] of Object.entries(consents)) {
h.classList.toggle(`bm-${name}`, granted)
h.classList.toggle(`bm-no-${name}`, !granted)
}
}

function buttonHandler(e) {
let id = e.target.dataset.id
Expand Down Expand Up @@ -155,7 +165,14 @@

/* Data */

function readConsents() {
const setConsents = consents => {
w[o.global] = consents
applyCssClasses()
}

const getConsents = () => w[o.global]

function loadConsents() {
try {
return JSON.parse(localStorage.getItem(o.key))
} catch (err) {
Expand All @@ -171,8 +188,7 @@
d.cookie.split('; ').map(cookie => cookie.split('='))
)


const { consentTime, ...consents } = readConsents() || o.sections.slice(1).reduce((consents, section) => {
const { consentTime, ...consents } = loadConsents() || o.sections.slice(1).reduce((consents, section) => {
consents[section] = false;
return { consentTime: undefined, ...consents }
}, {})
Expand Down Expand Up @@ -204,18 +220,21 @@

function saveConsents(value) {
const willReadValues = value === undefined
w[o.global].consentTime = +new Date()
let consents = {
consentTime: +new Date()
}
o.sections.forEach(section => {
if (section === 'essential') return false
let sectionElement = ui.querySelector(`[data-s=${section}]`)
let sectionConsent = willReadValues
? sectionElement.checked
: value
w[o.global][section] = sectionConsent
consents[section] = sectionConsent
if (!willReadValues) sectionElement.checked = value
})
localStorage.setItem(o.key, JSON.stringify(w[o.global]))
dispatch('save', {data: w[o.global]})
setConsents(consents)
localStorage.setItem(o.key, JSON.stringify(consents))
dispatch('save', {data: consents})
clearStorages()
insertScripts()
dialog.close()
Expand All @@ -225,7 +244,7 @@
function insertScripts() {
const scripts = d.querySelectorAll('script[data-consent]')
scripts.forEach(script => {
if (!w[o.global][script.dataset.consent]) return false
if (!getConsents()[script.dataset.consent]) return false

const newScript = d.createElement('script')
for (let { name, value } of script.attributes) {
Expand All @@ -250,7 +269,7 @@

/* Start */

w[o.global] = readConsents() || {}
setConsents(loadConsents() || {})

// Optional Non-EU auto-consent
const tz = Intl.DateTimeFormat().resolvedOptions().timeZone
Expand All @@ -275,14 +294,15 @@
// Helper methods
// <a onclick="bmInvalidate()" href="javascript:void(0)">Delete Consent Preferences</a>
w.bmInvalidate = () => {
dispatch('invalidate', {data: readConsents()})
dispatch('invalidate', {data: getConsents()})
saveConsents(false)
setConsents({})
localStorage.removeItem(o.key)
displayUI(true)
}
// <a onclick="bmUpdate()" href="javascript:void(0)">Update Consent Preferences</a>
w.bmUpdate = () => {
dispatch('update', {data: readConsents()})
dispatch('update', {data: getConsents()})
openModal()
}
})(document, window, Object, 'biscuitman')
})(document, window, Object, document.documentElement, 'biscuitman')
2 changes: 1 addition & 1 deletion dist/biscuitman.min.css

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 5ca4022

Please sign in to comment.