@ -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 : # 4496 f5 ;
-- icon - color : # 4496 f5 ;
-- 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 0 px 8 px rgba ( 0 , 0 , 0 , 0.2 ) ;
}
}
/ * D a r k T h e m e O v e r r i d e s :
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 : # 4496 f5 ;
-- icon - color : # 4496 f5 ;
-- 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 0 px 8 px rgba ( 255 , 255 , 255 , 0.6 ) ;
}
}
/ * L i g h t T h e m e O v e r r i d e s :
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 : # 4496 f5 ;
-- icon - color : # 4496 f5 ;
-- 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 0 px 8 px rgba ( 0 , 0 , 0 , 0.2 ) ;
}
}
/* Overlay covering the page behind the modal */
. pageOverlay {
. pageOverlay {
position : fixed ;
position : fixed ;
top : 0 px ;
top : 0 ;
bottom : 0 px ;
bottom : 0 ;
right : 0 px ;
right : 0 ;
left : 0 px ;
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 ( 0 px ) ;
backdrop - filter : blur ( 0 px ) ;
transition : all 0.2 s ;
transition : all 0.2 s ;
}
}
/* 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 0 px 8 px rgba ( 255 , 255 , 255 , 0.6 ) ;
box - shadow : var( -- modal - box - shadow ) ;
position : re a ltive;
position : re la 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 ( 100 vw / 3 ) ;
min - width : calc ( 100 vw / 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.3 s ;
transition : all 0.3 s ;
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 150 ms 2 linear ;
animation : shake 150 ms 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.2 s ;
}
. 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.2 s ;
}
. 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 > < / c o n s e n t s o f t w a r e - m a i n s e l e c t i o n >
< consentsoftware - mainselection > < / c o n s e n t s o f t w a r e - m a i n s e l e c t i o n >
< 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 {
}
}
/ * *
/ * *
* Called when the element is inserted into the DOM .
* Lifecycle method called when the element is connected 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() {
* Called after the first render of the component .
// Placeholder for any logic needed after first render
* We measure the actual height of the banner and update the CSS variable .
}
* /
public async firstUpdated() { }
/ * *
/ * *
* Called whenever the element is updated or re - rendered .
* Called after updates . Logs banner 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' ;
} ) ;
} ) ;