Documentation
Documentation
<script
src="https://your-project.inth.app/c15t.headless.js"
defer
></script>
<style>
.cookie-bar {
position: fixed;
inset: auto 0 0;
z-index: 50;
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 0.75rem 1.5rem;
padding: 0.875rem 1.5rem;
background: #14213d;
color: #f8f9fb;
font:
0.9375rem/1.5 system-ui,
sans-serif;
box-shadow: 0 -4px 16px rgb(0 0 0 / 0.15);
}
.cookie-bar__text {
flex: 1 1 20rem;
margin: 0;
}
.cookie-bar__actions {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
}
.cookie-bar button {
padding: 0.5rem 1rem;
border: 1px solid #f8f9fb;
border-radius: 999px;
background: transparent;
color: inherit;
font: inherit;
cursor: pointer;
}
.cookie-bar .cookie-bar__button {
background: #f8f9fb;
color: #14213d;
font-weight: 600;
}
.cookie-bar .cookie-bar__link {
border-color: transparent;
text-decoration: underline;
text-underline-offset: 3px;
}
.cookie-bar button:focus-visible {
outline: 2px solid #fca311;
outline-offset: 2px;
}
/* On narrow screens, Reject and Accept share a row at equal width. */
@media (max-width: 30rem) {
.cookie-bar__actions {
width: 100%;
}
.cookie-bar__actions button {
flex: 1 1 40%;
}
.cookie-bar__actions .cookie-bar__link {
flex-basis: 100%;
}
}
/* The headless build has no dialog, so preferences open inside the bar. */
.cookie-bar[hidden],
.cookie-bar__prefs[hidden] {
display: none;
}
.cookie-bar__prefs {
display: flex;
flex-wrap: wrap;
gap: 0.5rem 1.25rem;
flex-basis: 100%;
margin: 0;
padding: 0;
border: 0;
}
.cookie-bar__prefs label {
display: flex;
align-items: center;
gap: 0.375rem;
}
</style>
<section
id="cookie-bar"
class="cookie-bar"
aria-label="Cookie consent"
hidden
>
<p class="cookie-bar__text">
We use cookies to measure traffic and improve this site. Choose which
ones can run.
</p>
<fieldset
class="cookie-bar__prefs"
hidden
>
<legend class="cookie-bar__text">Allow these cookies:</legend>
<label
><input
type="checkbox"
name="functionality"
/>
Functionality</label
>
<label
><input
type="checkbox"
name="measurement"
/>
Measurement</label
>
<label
><input
type="checkbox"
name="experience"
/>
Experience</label
>
<label
><input
type="checkbox"
name="marketing"
/>
Marketing</label
>
</fieldset>
<div class="cookie-bar__actions">
<!-- c15t wires data-c15t-action buttons itself; they need no code. -->
<button
type="button"
class="cookie-bar__link"
data-c15t-action="customize"
>
Preferences
</button>
<button
type="button"
class="cookie-bar__button"
data-c15t-action="dismiss"
data-notice-only
hidden
>
Got it
</button>
<button
type="button"
class="cookie-bar__button"
data-c15t-action="reject"
data-choice-only
>
Reject all
</button>
<button
type="button"
class="cookie-bar__button"
data-c15t-action="accept"
data-choice-only
>
Accept all
</button>
<button
type="button"
class="cookie-bar__button"
data-save
hidden
>
Save
</button>
</div>
</section>
<script>
const bar = document.getElementById('cookie-bar');
const prefs = bar.querySelector('.cookie-bar__prefs');
const saveButton = bar.querySelector('[data-save]');
const switches = prefs.querySelectorAll('input');
const choiceButtons = bar.querySelectorAll('[data-choice-only]');
const noticeButtons = bar.querySelectorAll('[data-notice-only]');
// The tag is deferred, so queue calls until window.c15t is the real API.
window.c15t = window.c15t || [];
const queue = (call) => {
if (Array.isArray(window.c15t)) {
window.c15t.push(call);
} else {
window.c15t[call[0]](...call.slice(1));
}
};
// c15t says which surface is due; the page decides what it looks like.
// 'banner' shows the bar, 'dialog' opens the switches, 'none' hides it.
queue([
'on',
'ui',
(surface) => {
const { kind } = c15t.getSnapshot().promptRequirement;
const editing = surface === 'dialog';
// A notice gets one acknowledgement button instead of Accept and Reject.
for (const button of choiceButtons) {
button.hidden = kind === 'notice' && !editing;
}
for (const button of noticeButtons) {
button.hidden = kind !== 'notice' || editing;
}
for (const input of switches) {
input.checked = c15t.has(input.name);
}
prefs.hidden = !editing;
saveButton.hidden = !editing;
bar.hidden =
surface === 'none' || (surface === 'banner' && kind === 'none');
},
]);
// Save records exactly what the switches show.
saveButton.addEventListener('click', () => {
const choice = {};
for (const input of switches) {
choice[input.name] = input.checked;
}
c15t.save(choice);
});
</script>