diff --git a/app.js b/app.js index c3710b4..1e08d27 100644 --- a/app.js +++ b/app.js @@ -67,6 +67,7 @@ const scenarios = [ 'analytics', 'embedded-video', 'payment-gateway', + 'user-preferences', 'gsi' ]; scenarios.forEach(scenario => { diff --git a/public/assets/styles/style.css b/public/assets/styles/style.css index 4f3beda..44cb07e 100644 --- a/public/assets/styles/style.css +++ b/public/assets/styles/style.css @@ -440,6 +440,91 @@ video { display: none; } +:root { + --bullet-toggle-size: 46px; + --toogle-width: 100px; + --toogle-height: 54px; + --light-mode-background: #f1c40f; + --dark-mode-background: #025BDF; + --dark-container-background: #3F3F3F; + --dark-container-text: #fff; +} + +.dark-mode-toggle { + position: relative; + display: inline-block; + width: var(--toogle-width); + height: var(--toogle-height); +} + +.dark-mode-toggle input { + opacity: 0; + width: 0; + height: 0; +} + +.dark-mode-toggle label { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: #EEE; + border-radius: 30px; + cursor: pointer; + transition: all 0.2s ease-in-out; +} + +.dark .dark-mode-toggle label { + background-color: #fff; +} + +.dark-mode-toggle label .bullet { + content: ""; + position: absolute; + top: 50%; + left: 5%; + transform: translate(0, -50%); + width: var(--bullet-toggle-size); + aspect-ratio: 1; + background-color: var(--light-mode-background); + border-radius: 50%; + transition: background-color 0.2s ease-in-out; + display: flex; + align-items: center; + justify-content: center; +} + +.dark-mode-toggle input:checked + label .bullet { + background-color: var(--dark-mode-background); + left: 95%; + transform: translate(-100%, -50%); +} + +.sun, + .moon { + position: relative; + z-index: 100; + fill: #fff; +} + +.moon { + display: none; +} + +.dark-mode-toggle input:checked + label .moon { + display: block; +} + +.dark-mode-toggle input:checked + label .sun { + display: none; +} + +.dark .page-card { + background-color: var(--dark-container-background); + color: var(--dark-container-text); +} + *, ::before, ::after { --tw-border-spacing-x: 0; --tw-border-spacing-y: 0; @@ -734,6 +819,10 @@ video { max-width: 32rem; } +.transform { + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} + .appearance-none { -webkit-appearance: none; -moz-appearance: none; diff --git a/src/common/index.ejs b/src/common/index.ejs index fd9a54e..39e8f89 100644 --- a/src/common/index.ejs +++ b/src/common/index.ejs @@ -14,6 +14,7 @@ <%= renderCard('Analytics Tracking', '🔎', '/analytics','Testing the tracking cross-site') %> <%= renderCard('Embedded Content', '📽️', '/embedded-video','Sharing data across sites with embedded content') %> <%= renderCard('E-Commerce', '🛒', '/ecommerce','Storage a shopping cart across sites via iframe') %> + <%= renderCard('User preferences', '🎨', '/user-preferences','Using cookies to set user preferences cross-domain') %> <%= renderCard('Single Sign-On', '🔐', '/single-sign-on','Using third-party providers to creating a login session') %> <%= renderCard('Payment Gateway', '💳', '/payment-gateway','Payment flow simulation with credit card') %> <%= renderCard('CHIPS', '🍪', '/chips','Using CHIPS to solve tracking issues across third-party applications') %> diff --git a/src/common/internal-page/header.ejs b/src/common/internal-page/header.ejs index 3f5e97e..3f5c739 100644 --- a/src/common/internal-page/header.ejs +++ b/src/common/internal-page/header.ejs @@ -10,4 +10,4 @@

<%= title %>

<% } %> -
\ No newline at end of file +
diff --git a/src/scenarios/user-preferences/index.ejs b/src/scenarios/user-preferences/index.ejs new file mode 100644 index 0000000..c6fc9c4 --- /dev/null +++ b/src/scenarios/user-preferences/index.ejs @@ -0,0 +1,20 @@ +<%- include(commonPath + '/header.ejs') %> +
+ <%- include(commonPath + '/internal-page/header.ejs') %> +

Here you can set you prefered theme.

+ +
+ + +
+ <%- include(commonPath + '/internal-page/footer.ejs') %> +
+ + + +<%- include(commonPath + '/footer.ejs') %> diff --git a/src/scenarios/user-preferences/routes.js b/src/scenarios/user-preferences/routes.js new file mode 100644 index 0000000..607efc1 --- /dev/null +++ b/src/scenarios/user-preferences/routes.js @@ -0,0 +1,40 @@ +const express = require('express'); +const path = require('path'); +const router = express.Router(); + +router.get('/', (req, res) => { + res.render(path.join(__dirname,'index'), { + title: 'User Preferences' + }); +}); + +router.get( '/get-user-preference', ( req, res ) => { + const currentTheme = req.cookies.theme || 'light'; + res.json( { theme: currentTheme }); +}); + +router.post( '/set-user-preference', ( req, res ) => { + const { theme } = req.body; + + if (!theme) { + res.status(400).send({ message: 'Invalid request' }); + + } + + res.cookie('theme', theme, { + domain: res.locals.domainC, + maxAge: 30 * 24 * 60 * 60 * 1000, // 30 days + httpOnly: true, + sameSite: "none", + secure: true + }); + res.status(200).send({ message: 'Success', theme : theme}); +}); + +// Serve the user-preference.js file to the site +router.get('/user-preference.js', (req, res) => { + res.set('Content-Type', 'application/javascript'); + res.render(path.join(__dirname,'user-preference')); +}); + +module.exports = router; diff --git a/src/scenarios/user-preferences/user-preference.ejs b/src/scenarios/user-preferences/user-preference.ejs new file mode 100644 index 0000000..7fec842 --- /dev/null +++ b/src/scenarios/user-preferences/user-preference.ejs @@ -0,0 +1,39 @@ +document.addEventListener('DOMContentLoaded', () => { + const baseURL = '<%= protocol %>://<%= domainC %><% if (isPortPresent) { %>:<%= port %><% } %>/user-preferences'; + const pageContainer = document.getElementById('theme-container'); + const themeSwitcher = document.getElementById('dark-mode-switch'); + + function updateUserPreference() { + fetch(`${baseURL}/get-user-preference`) + .then(response => { + if (!response.ok) { + throw new Error('Network response was not ok'); + } + return response.json(); + }) + .then(data => { + const theme = data.theme; + console.log(data.theme) + pageContainer.className = theme; + themeSwitcher.checked = theme === 'dark'; + }) + .catch(error => { + console.error('There has been a problem with your fetch operation:', error); + }); + } + + function toggleTheme() { + fetch( `${baseURL}/set-user-preference`, { + method: 'POST', + credentials: 'include', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ theme: themeSwitcher?.checked ? 'dark' : 'light' }) + }) + .then(response => response.json()) + .then(data => { + pageContainer.className = data.theme; + }); + } + window.toggleTheme = toggleTheme; + updateUserPreference(); +}); diff --git a/src/styles/input.css b/src/styles/input.css index bd6213e..23fc465 100644 --- a/src/styles/input.css +++ b/src/styles/input.css @@ -1,3 +1,92 @@ @tailwind base; @tailwind components; -@tailwind utilities; \ No newline at end of file +@tailwind utilities; + +@layer base { + + :root { + --bullet-toggle-size: 46px; + --toogle-width: 100px; + --toogle-height: 54px; + --light-mode-background: #f1c40f; + --dark-mode-background: #025BDF; + --dark-container-background: #3F3F3F; + --dark-container-text: #fff; + } + + .dark-mode-toggle { + position: relative; + display: inline-block; + width: var(--toogle-width); + height: var(--toogle-height); + } + + .dark-mode-toggle input { + opacity: 0; + width: 0; + height: 0; + } + + .dark-mode-toggle label { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: #EEE; + border-radius: 30px; + cursor: pointer; + transition: all 0.2s ease-in-out; + } + + .dark .dark-mode-toggle label { + background-color: #fff; + } + + .dark-mode-toggle label .bullet { + content: ""; + position: absolute; + top: 50%; + left: 5%; + transform: translate(0, -50%); + width: var(--bullet-toggle-size); + aspect-ratio: 1; + background-color: var(--light-mode-background); + border-radius: 50%; + transition: background-color 0.2s ease-in-out; + display: flex; + align-items: center; + justify-content: center; + } + + .dark-mode-toggle input:checked + label .bullet { + background-color: var(--dark-mode-background); + left: 95%; + transform: translate(-100%, -50%); + } + + .sun, + .moon { + position: relative; + z-index: 100; + fill: #fff; + } + + .moon { + display: none; + } + + .dark-mode-toggle input:checked + label .moon { + display: block; + } + + .dark-mode-toggle input:checked + label .sun { + display: none; + } + + .dark .page-card { + background-color: var(--dark-container-background); + color: var(--dark-container-text); + } + +} \ No newline at end of file