Improve UI of themed banner
Signed-off-by: Émile Ré <emile@getprobo.com>
This commit is contained in:
@@ -23,7 +23,6 @@ export const THEMED_STYLES = `
|
||||
--_shadow: var(--probo-shadow, 0 4px 24px rgba(0, 0, 0, 0.12));
|
||||
--_accent: var(--probo-accent, #1a1a1a);
|
||||
--_accent-text: var(--probo-accent-text, #ffffff);
|
||||
--_overlay: var(--probo-overlay, rgba(0, 0, 0, 0.4));
|
||||
--_z-index: var(--probo-z-index, 2147483646);
|
||||
--_btn-radius: var(--probo-btn-radius, 8px);
|
||||
--_font-size: var(--probo-font-size, 14px);
|
||||
@@ -40,26 +39,71 @@ export const THEMED_STYLES = `
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.overlay {
|
||||
.floating {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
background: var(--_overlay);
|
||||
z-index: var(--_z-index);
|
||||
padding: 24px;
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
justify-content: center;
|
||||
padding: 16px;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.floating[data-position="bottom-left"] {
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.floating[data-position="bottom-right"] {
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.floating[data-position="bottom-center"] {
|
||||
bottom: 0;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
|
||||
.floating[data-position="top-left"] {
|
||||
top: 0;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.floating[data-position="top-right"] {
|
||||
top: 0;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.floating[data-position="top-center"] {
|
||||
top: 0;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
|
||||
.card {
|
||||
background: var(--_bg);
|
||||
border-radius: var(--_radius);
|
||||
box-shadow: var(--_shadow);
|
||||
max-width: 520px;
|
||||
width: 100%;
|
||||
padding: 24px;
|
||||
max-height: 85vh;
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
probo-banner .card {
|
||||
max-width: 450px;
|
||||
}
|
||||
|
||||
probo-preference-panel .card {
|
||||
max-width: 520px;
|
||||
max-height: 75vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
probo-preference-panel probo-category-list {
|
||||
overflow-y: auto;
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.title {
|
||||
@@ -87,10 +131,10 @@ export const THEMED_STYLES = `
|
||||
.btn {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
padding: 10px 16px;
|
||||
padding: 8px 10px;
|
||||
border-radius: var(--_btn-radius);
|
||||
border: 1px solid var(--_border);
|
||||
background: var(--_bg);
|
||||
background: color-mix(in srgb, var(--_text) 8%, var(--_bg));
|
||||
color: var(--_text);
|
||||
font-family: var(--_font);
|
||||
font-size: var(--_font-size);
|
||||
@@ -101,7 +145,14 @@ export const THEMED_STYLES = `
|
||||
}
|
||||
|
||||
.btn:hover {
|
||||
border-color: var(--_accent);
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.btn-link {
|
||||
background: transparent;
|
||||
border: none;
|
||||
color: var(--_accent);
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
@@ -110,50 +161,53 @@ export const THEMED_STYLES = `
|
||||
border-color: var(--_accent);
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
.panel-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 16px;
|
||||
padding: 24px;
|
||||
border-bottom: 1px solid var(--_border);
|
||||
}
|
||||
|
||||
.panel-back {
|
||||
.panel-close {
|
||||
background: none;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
padding: 4px;
|
||||
color: var(--_text);
|
||||
font-family: var(--_font);
|
||||
color: var(--_text-secondary);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.panel-back:hover {
|
||||
color: var(--_accent);
|
||||
.panel-close:hover {
|
||||
color: var(--_text);
|
||||
}
|
||||
|
||||
probo-category-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
probo-preference-panel .buttons {
|
||||
border-top: 1px solid var(--_border);
|
||||
padding: 20px 24px 24px;
|
||||
}
|
||||
|
||||
probo-category {
|
||||
display: block;
|
||||
border: 1px solid var(--_border);
|
||||
border-radius: 8px;
|
||||
padding: 12px 16px;
|
||||
border-bottom: 1px solid var(--_border);
|
||||
padding: 12px 40px 12px 60px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
probo-category:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.category-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
}
|
||||
@@ -181,9 +235,11 @@ export const THEMED_STYLES = `
|
||||
|
||||
.toggle {
|
||||
position: relative;
|
||||
width: 44px;
|
||||
height: 24px;
|
||||
display: inline-block;
|
||||
width: 34px;
|
||||
height: 18px;
|
||||
flex-shrink: 0;
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
.toggle input {
|
||||
@@ -197,7 +253,7 @@ export const THEMED_STYLES = `
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: var(--_border);
|
||||
border-radius: 12px;
|
||||
border-radius: 9px;
|
||||
cursor: pointer;
|
||||
transition: background 0.2s;
|
||||
}
|
||||
@@ -207,8 +263,8 @@ export const THEMED_STYLES = `
|
||||
position: absolute;
|
||||
top: 2px;
|
||||
left: 2px;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
background: white;
|
||||
border-radius: 50%;
|
||||
transition: transform 0.2s;
|
||||
@@ -220,7 +276,7 @@ export const THEMED_STYLES = `
|
||||
}
|
||||
|
||||
.toggle input:checked + .toggle-track::after {
|
||||
transform: translateX(20px);
|
||||
transform: translateX(16px);
|
||||
}
|
||||
|
||||
.toggle input:disabled + .toggle-track {
|
||||
@@ -228,41 +284,71 @@ export const THEMED_STYLES = `
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.cookie-toggle {
|
||||
position: absolute;
|
||||
left: 36px;
|
||||
top: 14px;
|
||||
background: none;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
padding: 2px;
|
||||
margin: 0;
|
||||
color: var(--_text-secondary);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.cookie-toggle:hover {
|
||||
color: var(--_accent);
|
||||
}
|
||||
|
||||
.cookie-toggle svg {
|
||||
transition: transform 0.2s;
|
||||
transform: rotate(-90deg);
|
||||
}
|
||||
|
||||
.cookie-toggle.open svg {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
|
||||
probo-cookie-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
margin-top: 8px;
|
||||
padding-top: 8px;
|
||||
border-top: 1px solid var(--_border);
|
||||
margin-top: 10px;
|
||||
background: color-mix(in srgb, var(--_text) 4%, var(--_bg));
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.cookie-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: baseline;
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
padding: 10px 12px;
|
||||
font-size: calc(var(--_font-size) - 2px);
|
||||
gap: 8px;
|
||||
border-bottom: 1px solid var(--_border);
|
||||
}
|
||||
|
||||
.cookie-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.cookie-name {
|
||||
font-weight: 500;
|
||||
font-family: monospace;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.cookie-duration {
|
||||
.cookie-detail {
|
||||
color: var(--_text-secondary);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.cookie-label {
|
||||
font-weight: 500;
|
||||
color: var(--_text);
|
||||
}
|
||||
|
||||
[hidden] {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
@media (min-width: 640px) {
|
||||
.overlay {
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
@@ -17,7 +17,8 @@ import type { ProboCookieBannerRoot } from "../components/cookie-banner-root";
|
||||
import type { BannerConfig } from "../client";
|
||||
import { THEMED_STYLES } from "./styles";
|
||||
|
||||
const BACK_ARROW = `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="15 18 9 12 15 6"/></svg>`;
|
||||
const CLOSE_ICON = `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>`;
|
||||
const CHEVRON_DOWN = `<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="6 9 12 15 18 9"/></svg>`;
|
||||
|
||||
export class ProboThemedBanner extends HTMLElement {
|
||||
private shadow: ShadowRoot;
|
||||
@@ -48,33 +49,35 @@ export class ProboThemedBanner extends HTMLElement {
|
||||
<style>${THEMED_STYLES}</style>
|
||||
<probo-cookie-banner-root banner-id="${this.esc(bannerId)}" base-url="${this.esc(baseUrl)}">
|
||||
<probo-banner>
|
||||
<div class="overlay">
|
||||
<div class="floating" data-position="${this.esc(position)}">
|
||||
<div class="card" role="dialog" aria-label="Cookie consent">
|
||||
<p class="title">Cookie Preferences</p>
|
||||
<p class="description" data-description>
|
||||
We use cookies to improve your experience and analyze site traffic.
|
||||
</p>
|
||||
<div class="buttons">
|
||||
<probo-reject-button><button class="btn">Reject all</button></probo-reject-button>
|
||||
<probo-customize-button><button class="btn">Customize</button></probo-customize-button>
|
||||
<probo-accept-button><button class="btn btn-primary">Accept all</button></probo-accept-button>
|
||||
<probo-reject-button><button class="btn">Reject all</button></probo-reject-button>
|
||||
<probo-customize-button><button class="btn btn-link">Customize</button></probo-customize-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</probo-banner>
|
||||
|
||||
<probo-preference-panel>
|
||||
<div class="overlay">
|
||||
<div class="floating" data-position="${this.esc(position)}">
|
||||
<div class="card" role="dialog" aria-label="Cookie preferences">
|
||||
<div class="panel-header">
|
||||
<button class="panel-back" data-action="back">
|
||||
${BACK_ARROW} Back
|
||||
</button>
|
||||
<p class="title" style="margin:0">Preferences</p>
|
||||
<div style="width:60px"></div>
|
||||
<button class="panel-close" data-action="back" aria-label="Close">
|
||||
${CLOSE_ICON}
|
||||
</button>
|
||||
</div>
|
||||
<probo-category-list>
|
||||
<template>
|
||||
<button class="cookie-toggle" data-action="toggle-cookies">
|
||||
${CHEVRON_DOWN}
|
||||
</button>
|
||||
<div class="category-header">
|
||||
<div class="category-info">
|
||||
<div class="category-name" data-slot="name"></div>
|
||||
@@ -87,11 +90,12 @@ export class ProboThemedBanner extends HTMLElement {
|
||||
</label>
|
||||
</probo-category-toggle>
|
||||
</div>
|
||||
<probo-cookie-list>
|
||||
<probo-cookie-list hidden>
|
||||
<template>
|
||||
<div class="cookie-item">
|
||||
<span class="cookie-name" data-slot="name"></span>
|
||||
<span class="cookie-duration" data-slot="duration"></span>
|
||||
<span class="cookie-detail"><span class="cookie-label">Description:</span> <span data-slot="description"></span></span>
|
||||
<span class="cookie-detail"><span class="cookie-label">Duration:</span> <span data-slot="duration"></span></span>
|
||||
</div>
|
||||
</template>
|
||||
</probo-cookie-list>
|
||||
@@ -120,6 +124,22 @@ export class ProboThemedBanner extends HTMLElement {
|
||||
this.shadow.querySelector("[data-action=back]")?.addEventListener("click", () => {
|
||||
root.setState("banner");
|
||||
});
|
||||
|
||||
this.shadow.addEventListener("click", (e: Event) => {
|
||||
const btn = (e.target as Element).closest?.("[data-action=toggle-cookies]") as HTMLElement | null;
|
||||
if (!btn) return;
|
||||
const category = btn.closest("probo-category");
|
||||
const cookieList = category?.querySelector("probo-cookie-list") as HTMLElement | null;
|
||||
if (!cookieList) return;
|
||||
const open = cookieList.hasAttribute("hidden");
|
||||
if (open) {
|
||||
cookieList.removeAttribute("hidden");
|
||||
btn.classList.add("open");
|
||||
} else {
|
||||
cookieList.setAttribute("hidden", "");
|
||||
btn.classList.remove("open");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private updateDescription(config: BannerConfig): void {
|
||||
|
||||
Reference in New Issue
Block a user