@ -21,18 +21,20 @@ export class ConsentsoftwareCookieconsent extends LitElement {
public csWebclientInstance = new csWebclient . CsWebclient ( ) ;
public csWebclientInstance = new csWebclient . CsWebclient ( ) ;
public csWebclientRan = false ;
public csWebclientRan = false ;
// Reflects the current theme ('light' or 'dark')
@property ( { type : String , reflect : true } )
@property ( { type : String , reflect : true } )
public theme : 'light' | 'dark' = 'light' ;
public theme : 'light' | 'dark' = 'light' ;
/**
/**
* We bind `heightPixels` to a CSS variable (--cookieconsent-height) .
* Define component styles with CSS variables that adjust based on theme .
* Then we can do margin-bottom animations in CSS .
* The default variables serve as baseline for the light theme .
* Theme-specific overrides modify these for dark mode.
*/
*/
public static styles = css `
public static styles = css `
:host {
:host {
font-family: ${ shared . fontStack } ;
font-family: ${ shared . fontStack } ;
user-select: none;
user-select: none;
/* Default theme variables */
/* Default variables for Light Theme */
--text-color: #333;
--text-color: #333;
--background-color: #eeeeee;
--background-color: #eeeeee;
--accent-color: #333333;
--accent-color: #333333;
@ -41,8 +43,14 @@ export class ConsentsoftwareCookieconsent extends LitElement {
--icon-color: #4496f5;
--icon-color: #4496f5;
--link-color: #333;
--link-color: #333;
--padding-sides: 16px;
--padding-sides: 16px;
/* Additional variables for modal and info container styling */
--info-bg: rgba(0, 0, 0, 0.1);
--info-text: rgba(255, 255, 255, 0.5);
--modal-box-shadow: 0px 0px 8px rgba(0, 0, 0, 0.2);
}
}
/* Dark Theme Overrides:
When theme attribute is 'dark', override variables accordingly. */
:host([theme='dark']) {
:host([theme='dark']) {
--text-color: #fff;
--text-color: #fff;
--background-color: #111;
--background-color: #111;
@ -51,8 +59,14 @@ export class ConsentsoftwareCookieconsent extends LitElement {
--button-hover-bg: #222222;
--button-hover-bg: #222222;
--icon-color: #4496f5;
--icon-color: #4496f5;
--link-color: #fff;
--link-color: #fff;
--info-bg: rgba(0, 0, 0, 0.1);
--info-text: rgba(255, 255, 255, 0.5);
--modal-box-shadow: 0px 0px 8px rgba(255, 255, 255, 0.6);
}
}
/* Light Theme Overrides:
Explicit light theme settings, currently matching defaults.
Can be customized independently if desired. */
:host([theme='light']) {
:host([theme='light']) {
--text-color: #333;
--text-color: #333;
--background-color: #eeeeee;
--background-color: #eeeeee;
@ -61,33 +75,39 @@ export class ConsentsoftwareCookieconsent extends LitElement {
--button-hover-bg: #f2f2f2;
--button-hover-bg: #f2f2f2;
--icon-color: #4496f5;
--icon-color: #4496f5;
--link-color: #333;
--link-color: #333;
--info-bg: rgba(0, 0, 0, 0.1);
--info-text: rgba(0, 0, 0, 0.5);
--modal-box-shadow: 0px 0px 8px rgba(0, 0, 0, 0.2);
}
}
/* Overlay covering the page behind the modal */
.pageOverlay {
.pageOverlay {
position: fixed;
position: fixed;
top: 0px ;
top: 0;
bottom: 0px ;
bottom: 0;
right: 0px ;
right: 0;
left: 0px ;
left: 0;
display: grid;
display: grid;
align-items: center;
align-items: center;
justify-content: center;
justify-content: center;
z-index: 1000; /* standard z-index for fixed elements */
z-index: 1000; /* Ensures the overlay is on top of other elements */
background: rgba(255, 255, 255, 0);
background: rgba(255, 255, 255, 0);
backdrop-filter: blur(0px);
backdrop-filter: blur(0px);
transition: all 0.2s;
transition: all 0.2s;
}
}
/* Shake animation for overlay when clicked */
.pageOverlay.shake {
.pageOverlay.shake {
background: rgba(0, 0, 0, 0.5) !important;
background: rgba(0, 0, 0, 0.5) !important;
}
}
/* Modal box styling using theme variables for colors and shadows */
.modalBox {
.modalBox {
display: block;
display: block;
color: var(--text-color);
color: var(--text-color);
background: var(--background-color);
background: var(--background-color);
box-shadow: 0px 0px 8px rgba(255, 255, 255, 0.6 );
box-shadow: var(--modal-box-shadow );
position: rea ltive;
position: rela tive;
border: 1px dotted rgba(255, 255, 255, 0.1);
border: 1px dotted rgba(255, 255, 255, 0.1);
border-top: 1px solid var(--accent-color);
border-top: 1px solid var(--accent-color);
border-radius: 16px;
border-radius: 16px;
@ -95,12 +115,22 @@ export class ConsentsoftwareCookieconsent extends LitElement {
min-width: calc(100vw / 3);
min-width: calc(100vw / 3);
box-sizing: border-box;
box-sizing: border-box;
overflow: hidden;
overflow: hidden;
will-change: transform; /* ensure efficient rendering */
will-change: transform;
transition: all 0.3s;
transition: all 0.3s;
transform: scale(0.95);
transform: scale(0.95);
opacity: 0;
opacity: 0;
}
}
/* Media query for mobile devices: stack buttons vertically */
@media (max-width: 600px) {
.modalBox {
height: 100vh;
box-shadow: none;
border-radius: 0px;
}
}
/* Shake animation for modal box */
.modalBox.shake {
.modalBox.shake {
animation: shake 150ms 2 linear;
animation: shake 150ms 2 linear;
}
}
@ -117,10 +147,7 @@ export class ConsentsoftwareCookieconsent extends LitElement {
}
}
}
}
/*
/* Toggle display based on [show] attribute */
* Toggle display based on [show] attribute
* (so if show=false, the banner doesn't show at all).
*/
:host([show='false']) {
:host([show='false']) {
display: none;
display: none;
}
}
@ -151,25 +178,14 @@ export class ConsentsoftwareCookieconsent extends LitElement {
gap: 16px;
gap: 16px;
}
}
.info-container {
/* Media query for mobile devices: stack buttons vertically */
color: var(--text-color);
@media (max-width: 600px) {
text-align: center;
.button-container {
line-height: 3em ;
grid-template-columns: 1fr ;
background: rgba(0, 0, 0, 0.1);
}
border-top: 1px dotted rgba(255, 255, 255, 0.1);
font-size: 0.8em;
color: rgba(255, 255, 255, 0.5);
}
.info-container a {
text-decoration: underline;
color: var(--link-color);
transition: color 0.2s;
}
.info-container a:hover {
color: #ffffff;
}
}
/* Consent button styling using theme variables */
.consent-button {
.consent-button {
border-radius: 3px;
border-radius: 3px;
background: var(--button-bg);
background: var(--button-bg);
@ -184,10 +200,30 @@ export class ConsentsoftwareCookieconsent extends LitElement {
.consent-button:hover {
.consent-button:hover {
background: var(--button-hover-bg);
background: var(--button-hover-bg);
}
}
/* Use theme variables for info container background and text */
.info-container {
text-align: center;
line-height: 3em;
background: var(--info-bg);
border-top: 1px dotted rgba(255, 255, 255, 0.1);
font-size: 0.8em;
color: var(--info-text);
}
.info-container a {
text-decoration: underline;
color: var(--link-color);
transition: color 0.2s;
}
.info-container a:hover {
color: #ffffff;
}
` ;
` ;
constructor ( ) {
constructor ( ) {
super ( ) ;
super ( ) ;
// Initially hide the consent banner until needed
this . setAttribute ( 'show' , 'false' ) ;
this . setAttribute ( 'show' , 'false' ) ;
}
}
@ -205,36 +241,6 @@ export class ConsentsoftwareCookieconsent extends LitElement {
and choose which cookie level you are willing to accept.
and choose which cookie level you are willing to accept.
</div>
</div>
</div>
</div>
<!-- <div class="button-container">
<div
class="consent-button"
@click= ${ ( event : MouseEvent ) = >
this . handleConsentButtonClick ( event , [ 'functional' ] ) }
>
Functional cookies
</div>
<div
class="consent-button"
@click= ${ ( event : MouseEvent ) = >
this . handleConsentButtonClick ( event , [ 'functional' , 'analytics' ] ) }
>
Analytics cookies
</div>
<div
class="consent-button"
@click= ${ ( event : MouseEvent ) = >
this . handleConsentButtonClick ( event , [ 'functional' , 'analytics' , 'marketing' ] ) }
>
Marketing cookies
</div>
<div
class="consent-button"
@click= ${ ( event : MouseEvent ) = >
this . handleConsentButtonClick ( event , [ 'functional' , 'analytics' , 'marketing' , 'all' ] ) }
>
All cookies
</div>
</div> -->
<consentsoftware-mainselection></consentsoftware-mainselection>
<consentsoftware-mainselection></consentsoftware-mainselection>
<div class="button-container">
<div class="button-container">
<div
<div
@ -262,7 +268,6 @@ export class ConsentsoftwareCookieconsent extends LitElement {
<div class="info-container">
<div class="info-container">
consent management powered by
consent management powered by
<a href="https://consent.software">consent.software</a>
<a href="https://consent.software">consent.software</a>
(Open Source)
</div>
</div>
</div>
</div>
</div>
</div>
@ -271,41 +276,44 @@ export class ConsentsoftwareCookieconsent extends LitElement {
}
}
/**
/**
* C alled when the element is inser ted in to the DOM.
* Lifecycle method c alled when the element is connec ted to the DOM.
* It sets up the theme and displays the consent banner if no cookie levels are set.
*/
*/
public async connectedCallback() {
public async connectedCallback() {
super . connectedCallback ( ) ;
super . connectedCallback ( ) ;
this . updateTheme ( ) ;
this . updateTheme ( ) ; // Initialize theme based on system preference
const cookieLevel = await this . csWebclientInstance . getCookieLevels ( ) ;
const cookieLevel = await this . csWebclientInstance . getCookieLevels ( ) ;
if ( ! cookieLevel ) {
if ( ! cookieLevel ) {
// Show consent banner if cookie levels haven't been set yet
this . setAttribute ( 'show' , 'true' ) ;
this . setAttribute ( 'show' , 'true' ) ;
requestAnimationFrame ( async ( ) = > {
requestAnimationFrame ( async ( ) = > {
await this . updated ( ) ;
await this . updated ( ) ;
const pageOverlay : HTMLDivElement = this . shadowRoot ? . querySelector ( '.pageOverlay' ) ;
const pageOverlay : HTMLDivElement = this . shadowRoot ? . querySelector ( '.pageOverlay' ) ;
if ( pageOverlay ) {
if ( pageOverlay ) {
// Apply dark overlay styling when modal appears
pageOverlay . style . background = 'rgba(0,0,0, 0.5)' ;
pageOverlay . style . background = 'rgba(0,0,0, 0.5)' ;
pageOverlay . style . backdropFilter = 'blur(20px)' ;
pageOverlay . style . backdropFilter = 'blur(20px)' ;
}
}
const modalBox : HTMLDivElement = this . shadowRoot ? . querySelector ( '.modalBox' ) ;
const modalBox : HTMLDivElement = this . shadowRoot ? . querySelector ( '.modalBox' ) ;
if ( modalBox ) {
if ( modalBox ) {
// Animate modal box appearance
modalBox . style . transform = ` scale(1) ` ;
modalBox . style . transform = ` scale(1) ` ;
modalBox . style . opacity = '1' ;
modalBox . style . opacity = '1' ;
}
}
} ) ;
} ) ;
} else {
} else {
// Hide banner if cookie levels are already set
this . setAttribute ( 'show' , 'false' ) ;
this . setAttribute ( 'show' , 'false' ) ;
}
}
}
}
/**
public async firstUpdated() {
* Call ed after the first render of the component.
// Placeholder for any logic need ed after first render
* We measure the actual height of the banner and update the CSS variable.
}
*/
public async firstUpdated() { }
/**
/**
* Called whenev er t he element is updated or re-rendered .
* Called after updates. Logs bann er height and runs consent scripts if necessary .
*/
*/
public async updated() {
public async updated() {
console . log ( ` The height of the cookie banner is ${ this . shadowRoot ? . host ? . clientHeight } px ` ) ;
console . log ( ` The height of the cookie banner is ${ this . shadowRoot ? . host ? . clientHeight } px ` ) ;
@ -317,7 +325,8 @@ export class ConsentsoftwareCookieconsent extends LitElement {
}
}
/**
/**
* Sets the user’ s chosen cookie level(s) and hides the banner.
* Handles consent button clicks, sets cookie levels, and hides the banner.
* Uses theme variables for styling transitions.
*/
*/
private async handleConsentButtonClick (
private async handleConsentButtonClick (
event : MouseEvent ,
event : MouseEvent ,
@ -326,21 +335,27 @@ export class ConsentsoftwareCookieconsent extends LitElement {
console . log ( ` Set level to ${ levelsArg } ` ) ;
console . log ( ` Set level to ${ levelsArg } ` ) ;
const pageOverlay : HTMLDivElement = this . shadowRoot ? . querySelector ( '.pageOverlay' ) ;
const pageOverlay : HTMLDivElement = this . shadowRoot ? . querySelector ( '.pageOverlay' ) ;
if ( pageOverlay ) {
if ( pageOverlay ) {
// Fade out overlay effect using inline styles for transition
pageOverlay . style . background = 'rgba(255,255,255, 0)' ;
pageOverlay . style . background = 'rgba(255,255,255, 0)' ;
pageOverlay . style . backdropFilter = 'blur(0px)' ;
pageOverlay . style . backdropFilter = 'blur(0px)' ;
}
}
const modalBox : HTMLDivElement = this . shadowRoot ? . querySelector ( '.modalBox' ) ;
const modalBox : HTMLDivElement = this . shadowRoot ? . querySelector ( '.modalBox' ) ;
if ( modalBox ) {
if ( modalBox ) {
// Scale down and fade out modal box before hiding
modalBox . style . transform = ` scale(0.95) ` ;
modalBox . style . transform = ` scale(0.95) ` ;
modalBox . style . opacity = '0' ;
modalBox . style . opacity = '0' ;
}
}
// Save user consent preferences
await this . csWebclientInstance . setCookieLevels ( levelsArg ) ;
await this . csWebclientInstance . setCookieLevels ( levelsArg ) ;
await delayFor ( 300 ) ;
await delayFor ( 300 ) ;
this . setAttribute ( 'show' , 'false' ) ;
this . setAttribute ( 'show' , 'false' ) ; // Hide the consent banner
// After user selection, re-check for any required scripts to run
this . updated ( ) ; // Trigger any post-consent actions
this . updated ( ) ;
}
}
/**
* Handles clicks on the page overlay. If clicked outside the modal,
* triggers a shake animation as feedback.
*/
private async pageOverlayClick ( e : MouseEvent ) {
private async pageOverlayClick ( e : MouseEvent ) {
if ( e . target === e . currentTarget ) {
if ( e . target === e . currentTarget ) {
const pageOverlay : HTMLDivElement = this . shadowRoot ? . querySelector ( '.pageOverlay' ) ;
const pageOverlay : HTMLDivElement = this . shadowRoot ? . querySelector ( '.pageOverlay' ) ;
@ -356,12 +371,15 @@ export class ConsentsoftwareCookieconsent extends LitElement {
}
}
/**
/**
* Dynamically switches the theme between light/ dark,
* Dynamically switches the theme between light and dark.
* respecting `prefers-color-scheme` by default .
* Listens for system theme changes to update the component's theme .
*/
*/
private updateTheme() {
private updateTheme() {
// Check the initial system preference for dark mode
const prefersDark = window . matchMedia ( '(prefers-color-scheme: dark)' ) . matches ;
const prefersDark = window . matchMedia ( '(prefers-color-scheme: dark)' ) . matches ;
this . theme = prefersDark ? 'dark' : 'light' ;
this . theme = prefersDark ? 'dark' : 'light' ;
// Listen for changes in the system color scheme preference
window . matchMedia ( '(prefers-color-scheme: dark)' ) . addEventListener ( 'change' , ( e ) = > {
window . matchMedia ( '(prefers-color-scheme: dark)' ) . addEventListener ( 'change' , ( e ) = > {
this . theme = e . matches ? 'dark' : 'light' ;
this . theme = e . matches ? 'dark' : 'light' ;
} ) ;
} ) ;