Files
Odoo-Modules/fusion_clock/static/src/scss/nfc_kiosk.scss

594 lines
21 KiB
SCSS
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// NFC Clock Kiosk — premium glass + animated mesh, always-dark.
//
// CRITICAL: All styles in this file are scoped under `:has(#nfc_kiosk_root)`
// to prevent leaking into other frontend pages. The previous version applied
// `html,body { overflow:hidden; height:100vh }` and `header,footer{display:none}`
// globally, which broke website scrolling and chrome on every frontend page.
//
// The single CSS custom property `--nfc-h` (hue, 0360) is set by JS after
// extracting the dominant color from the company logo. All colors interpolate
// from that hue via HSL, so the entire palette adapts to the customer brand.
// ─────────────────────────────────────────────────────────────────────
// Defaults (overridden by JS once logo dominant-hue is extracted)
// ─────────────────────────────────────────────────────────────────────
:root {
--nfc-h: 220; // fallback aurora-blue hue
--nfc-bg: #0b0d10;
--nfc-text: #ffffff;
--nfc-text-muted: #9ba3ad;
--nfc-success: #18a957;
--nfc-error: #d9374e;
}
// ─────────────────────────────────────────────────────────────────────
// Page-level styling — ONLY when the kiosk is on the page
// ─────────────────────────────────────────────────────────────────────
html:has(#nfc_kiosk_root) {
overflow: hidden;
height: 100%;
body {
overflow: hidden;
height: 100%;
margin: 0;
padding: 0;
background: var(--nfc-bg) !important;
color: var(--nfc-text);
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
}
// Hide site chrome on the kiosk page only
.o_main_navbar, header, footer, .o_header_standard, .o_footer { display: none !important; }
}
// ─────────────────────────────────────────────────────────────────────
// Kiosk root container with animated mesh gradient background
// ─────────────────────────────────────────────────────────────────────
.nfc-kiosk {
position: fixed;
inset: 0;
width: 100vw;
height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 2rem;
box-sizing: border-box;
user-select: none;
-webkit-tap-highlight-color: transparent;
overflow: hidden;
background: var(--nfc-bg);
// Animated mesh gradient (drifts behind everything)
&::before {
content: "";
position: absolute;
inset: -15%;
background:
radial-gradient(circle at 20% 30%, hsla(var(--nfc-h), 75%, 40%, 0.55) 0%, transparent 45%),
radial-gradient(circle at 80% 20%, hsla(calc(var(--nfc-h) + 40), 65%, 35%, 0.50) 0%, transparent 50%),
radial-gradient(circle at 70% 75%, hsla(calc(var(--nfc-h) - 25), 70%, 35%, 0.45) 0%, transparent 55%),
radial-gradient(circle at 15% 85%, hsla(calc(var(--nfc-h) + 80), 60%, 30%, 0.40) 0%, transparent 50%);
filter: blur(60px) saturate(140%);
animation: nfc-mesh-drift 28s ease-in-out infinite alternate;
z-index: 0;
will-change: transform;
}
// Subtle vignette on top so edges don't feel washed out
&::after {
content: "";
position: absolute;
inset: 0;
background: radial-gradient(ellipse at center, transparent 50%, rgba(0,0,0,0.45) 100%);
z-index: 1;
pointer-events: none;
}
> * { position: relative; z-index: 2; }
}
@keyframes nfc-mesh-drift {
0% { transform: translate(0%, 0%) rotate(0deg) scale(1); }
50% { transform: translate(3%, -2%) rotate(2deg) scale(1.05); }
100% { transform: translate(-3%, 3%) rotate(-1deg) scale(0.98); }
}
// ─────────────────────────────────────────────────────────────────────
// Header chrome — logo, time, date, location, settings
// ─────────────────────────────────────────────────────────────────────
// Logo centered at the top, on a subtle frosted-glass pill — just enough lift
// to keep dark logos readable on the dark gradient.
.nfc-kiosk__logo {
position: absolute;
top: 1.25rem;
left: 50%;
transform: translateX(-50%);
max-height: 52px;
max-width: 220px;
object-fit: contain;
background: rgba(255, 255, 255, 0.20);
backdrop-filter: blur(24px) saturate(180%);
-webkit-backdrop-filter: blur(24px) saturate(180%);
padding: 0.5rem 0.85rem;
border-radius: 0.85rem;
border: 1px solid rgba(255, 255, 255, 0.18);
box-shadow:
0 6px 24px rgba(0, 0, 0, 0.3),
inset 0 1px 0 rgba(255, 255, 255, 0.25);
box-sizing: content-box;
animation: nfc-logo-in 1.2s cubic-bezier(0.16, 1, 0.3, 1) both;
}
@keyframes nfc-logo-in {
from { opacity: 0; transform: translateX(-50%) translateY(-12px) scale(0.94); }
to { opacity: 1; transform: translateX(-50%) translateY(0) scale(1); }
}
.nfc-kiosk__company {
position: absolute;
top: 5.75rem;
left: 1.5rem;
font-size: 0.85rem;
color: var(--nfc-text-muted);
letter-spacing: 0.05em;
text-transform: uppercase;
}
// When a logo is present, the company-name text is redundant — hide it.
.nfc-kiosk__logo ~ .nfc-kiosk__company { display: none; }
// Clock + date stack vertically below the logo, all centered.
.nfc-kiosk__time {
position: absolute;
top: 6.25rem; // sits below the logo (logo bottom ≈ 90px)
left: 50%;
transform: translateX(-50%);
font-size: 2.5rem;
font-weight: 300;
color: var(--nfc-text);
font-variant-numeric: tabular-nums;
letter-spacing: -0.02em;
text-shadow: 0 2px 12px rgba(0,0,0,0.4);
display: flex;
align-items: baseline;
gap: 0.45rem;
white-space: nowrap;
.ampm {
font-size: 1rem;
font-weight: 500;
color: var(--nfc-text-muted);
letter-spacing: 0.08em;
}
}
.nfc-kiosk__date {
position: absolute;
top: 9.75rem; // sits below the time
left: 50%;
transform: translateX(-50%);
font-size: 0.85rem;
color: var(--nfc-text-muted);
letter-spacing: 0.05em;
text-transform: uppercase;
}
.nfc-kiosk__location {
position: absolute;
bottom: 1.5rem;
left: 1.5rem;
font-size: 0.85rem;
color: var(--nfc-text-muted);
background: rgba(255,255,255,0.04);
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
border: 1px solid rgba(255,255,255,0.08);
padding: 0.5rem 1rem;
border-radius: 999px;
}
.nfc-kiosk__settings {
position: absolute;
bottom: 1.5rem;
right: 1.5rem;
width: 2.75rem;
height: 2.75rem;
border-radius: 50%;
background: rgba(255,255,255,0.04);
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
color: var(--nfc-text-muted);
border: 1px solid rgba(255,255,255,0.08);
cursor: pointer;
font-size: 1.2rem;
display: flex;
align-items: center;
justify-content: center;
transition: color 200ms ease, border-color 200ms ease, transform 200ms ease;
&:hover, &:active {
color: var(--nfc-text);
border-color: rgba(255,255,255,0.2);
transform: scale(1.05);
}
}
// ─────────────────────────────────────────────────────────────────────
// Reusable glass panel
// ─────────────────────────────────────────────────────────────────────
%nfc-glass {
background: rgba(255,255,255,0.05);
backdrop-filter: blur(24px) saturate(160%);
-webkit-backdrop-filter: blur(24px) saturate(160%);
border: 1px solid rgba(255,255,255,0.1);
box-shadow:
0 20px 60px rgba(0,0,0,0.5),
inset 0 1px 0 rgba(255,255,255,0.08);
border-radius: 1.5rem;
}
// ─────────────────────────────────────────────────────────────────────
// State container — base fade-in for whatever child renders
// ─────────────────────────────────────────────────────────────────────
#nfc_state_container > * {
animation: nfc-state-in 400ms cubic-bezier(0.16, 1, 0.3, 1);
}
@keyframes nfc-state-in {
from { opacity: 0; transform: scale(0.96) translateY(10px); }
to { opacity: 1; transform: scale(1) translateY(0); }
}
// ─────────────────────────────────────────────────────────────────────
// IDLE — large NFC icon + prompt
// ─────────────────────────────────────────────────────────────────────
.nfc-kiosk__idle {
text-align: center;
display: flex;
flex-direction: column;
align-items: center;
gap: 2rem;
}
.nfc-kiosk__icon-svg {
width: 14rem;
height: 14rem;
overflow: visible; // let waves expand past viewBox without clipping
color: hsl(var(--nfc-h), 80%, 65%);
filter: drop-shadow(0 0 30px hsla(var(--nfc-h), 80%, 55%, 0.6));
.nfc-chip {
animation: nfc-chip-pulse 2.5s ease-in-out infinite;
transform-origin: center;
}
.nfc-wave {
transform-origin: center;
transform-box: fill-box; // scale around the wave's own center, not viewBox origin
opacity: 0;
animation: nfc-wave-emit 2.5s ease-out infinite;
}
.nfc-wave-1 { animation-delay: 0s; }
.nfc-wave-2 { animation-delay: 0.6s; }
.nfc-wave-3 { animation-delay: 1.2s; }
}
@keyframes nfc-chip-pulse {
0%, 100% { transform: scale(1); }
50% { transform: scale(1.05); }
}
@keyframes nfc-wave-emit {
0% { transform: scale(0.6); opacity: 0; }
25% { opacity: 0.85; }
80% { opacity: 0.25; }
100% { transform: scale(1.35); opacity: 0; }
}
.nfc-kiosk__prompt {
font-size: 2.25rem;
font-weight: 300;
letter-spacing: -0.01em;
color: var(--nfc-text);
text-shadow: 0 2px 20px rgba(0,0,0,0.4);
}
// ─────────────────────────────────────────────────────────────────────
// PROCESSING — pulsing dots
// ─────────────────────────────────────────────────────────────────────
.nfc-kiosk__processing {
@extend %nfc-glass;
padding: 2.5rem 3.5rem;
text-align: center;
font-size: 1.5rem;
color: var(--nfc-text);
display: inline-flex;
align-items: center;
gap: 1rem;
.dots {
display: inline-flex;
gap: 0.4rem;
span {
width: 0.6rem;
height: 0.6rem;
border-radius: 50%;
background: hsl(var(--nfc-h), 80%, 65%);
animation: nfc-dot-bounce 1.2s ease-in-out infinite;
&:nth-child(2) { animation-delay: 0.15s; }
&:nth-child(3) { animation-delay: 0.3s; }
}
}
}
@keyframes nfc-dot-bounce {
0%, 80%, 100% { transform: scale(0.7); opacity: 0.5; }
40% { transform: scale(1.0); opacity: 1.0; }
}
// ─────────────────────────────────────────────────────────────────────
// RESULT — glass card with avatar + name + action
// ─────────────────────────────────────────────────────────────────────
.nfc-kiosk__result {
@extend %nfc-glass;
width: 80vw;
max-width: 720px;
padding: 2.5rem 3rem;
display: flex;
align-items: center;
gap: 2rem;
position: relative;
&--success {
border-color: rgba(24,169,87,0.55);
box-shadow:
0 20px 60px rgba(0,0,0,0.5),
0 0 80px rgba(24,169,87,0.35),
inset 0 1px 0 rgba(255,255,255,0.1);
animation: nfc-success-burst 700ms cubic-bezier(0.16, 1, 0.3, 1);
}
&--error {
border-color: rgba(217,55,78,0.55);
box-shadow:
0 20px 60px rgba(0,0,0,0.5),
0 0 60px rgba(217,55,78,0.3),
inset 0 1px 0 rgba(255,255,255,0.1);
animation: nfc-shake 350ms ease-in-out, nfc-state-in 400ms cubic-bezier(0.16, 1, 0.3, 1);
}
}
@keyframes nfc-success-burst {
0% {
opacity: 0;
transform: scale(0.88);
box-shadow:
0 20px 60px rgba(0,0,0,0.5),
0 0 0 rgba(24,169,87,0),
inset 0 1px 0 rgba(255,255,255,0.1);
}
50% {
box-shadow:
0 20px 60px rgba(0,0,0,0.5),
0 0 140px rgba(24,169,87,0.7),
inset 0 1px 0 rgba(255,255,255,0.1);
}
100% {
opacity: 1;
transform: scale(1);
box-shadow:
0 20px 60px rgba(0,0,0,0.5),
0 0 80px rgba(24,169,87,0.35),
inset 0 1px 0 rgba(255,255,255,0.1);
}
}
@keyframes nfc-shake {
0%, 100% { transform: translateX(0); }
20% { transform: translateX(-10px); }
40% { transform: translateX(10px); }
60% { transform: translateX(-6px); }
80% { transform: translateX(6px); }
}
.nfc-kiosk__avatar {
width: 7rem;
height: 7rem;
border-radius: 50%;
background-size: cover;
background-position: center;
background-color: rgba(255,255,255,0.15);
flex-shrink: 0;
border: 2px solid rgba(255,255,255,0.2);
box-shadow: 0 8px 24px rgba(0,0,0,0.4);
animation: nfc-avatar-in 600ms cubic-bezier(0.34, 1.56, 0.64, 1) both;
}
@keyframes nfc-avatar-in {
from { opacity: 0; transform: scale(0.4); }
to { opacity: 1; transform: scale(1); }
}
.nfc-kiosk__result-text {
flex: 1;
.name { font-size: 2.25rem; font-weight: 600; letter-spacing: -0.02em; }
.action { font-size: 1.5rem; margin-top: 0.5rem; opacity: 0.95; font-weight: 400; }
.hours { font-size: 1.05rem; opacity: 0.75; margin-top: 0.5rem; }
}
// ─────────────────────────────────────────────────────────────────────
// SETUP wizard
// ─────────────────────────────────────────────────────────────────────
.nfc-kiosk__setup {
@extend %nfc-glass;
text-align: center;
max-width: 600px;
padding: 3.5rem 3rem;
h2 { font-size: 2rem; margin-bottom: 1rem; font-weight: 300; letter-spacing: -0.01em; }
p { color: var(--nfc-text-muted); margin-bottom: 2rem; line-height: 1.5; }
button {
font-size: 1.25rem;
padding: 1rem 2.5rem;
background: hsl(var(--nfc-h), 80%, 55%);
color: white;
border: none;
border-radius: 999px;
cursor: pointer;
font-weight: 500;
box-shadow: 0 8px 32px hsla(var(--nfc-h), 80%, 50%, 0.4);
transition: transform 200ms ease, box-shadow 200ms ease;
&:hover, &:active {
transform: translateY(-2px);
box-shadow: 0 12px 36px hsla(var(--nfc-h), 80%, 50%, 0.5);
}
}
}
// ─────────────────────────────────────────────────────────────────────
// ENROLL Mode overlay — glass panel with numpad / search / tap-prompt
// ─────────────────────────────────────────────────────────────────────
.nfc-kiosk__enroll-overlay {
position: fixed;
inset: 0;
background: rgba(0,0,0,0.7);
backdrop-filter: blur(8px);
-webkit-backdrop-filter: blur(8px);
z-index: 1000;
display: flex;
align-items: center;
justify-content: center;
padding: 2rem;
animation: nfc-overlay-in 250ms ease-out;
}
@keyframes nfc-overlay-in {
from { opacity: 0; }
to { opacity: 1; }
}
.nfc-kiosk__enroll-panel {
@extend %nfc-glass;
padding: 2.5rem;
width: 80vw;
max-width: 720px;
h2 {
font-size: 1.5rem;
margin: 0 0 1.5rem;
font-weight: 400;
color: var(--nfc-text);
}
.numpad {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 0.75rem;
margin: 1rem 0;
button {
font-size: 2rem;
padding: 1.5rem 0;
background: rgba(255,255,255,0.05);
color: var(--nfc-text);
border: 1px solid rgba(255,255,255,0.1);
border-radius: 0.75rem;
cursor: pointer;
transition: background 150ms ease, transform 100ms ease;
font-weight: 300;
&:hover { background: rgba(255,255,255,0.1); }
&:active { background: rgba(255,255,255,0.15); transform: scale(0.96); }
}
}
.pin-display {
font-size: 2.5rem;
letter-spacing: 0.5rem;
text-align: center;
margin: 1rem 0 1.5rem;
font-variant-numeric: tabular-nums;
min-height: 3rem;
color: hsl(var(--nfc-h), 80%, 70%);
}
.employee-search {
width: 100%;
padding: 1rem 1.25rem;
font-size: 1.15rem;
background: rgba(255,255,255,0.05);
color: var(--nfc-text);
border: 1px solid rgba(255,255,255,0.1);
border-radius: 0.75rem;
margin-bottom: 1rem;
box-sizing: border-box;
outline: none;
&:focus { border-color: hsl(var(--nfc-h), 80%, 55%); }
&::placeholder { color: var(--nfc-text-muted); }
}
.employee-list {
max-height: 40vh;
overflow-y: auto;
.employee-row {
padding: 0.85rem 1rem;
border-bottom: 1px solid rgba(255,255,255,0.05);
cursor: pointer;
font-size: 1.05rem;
border-radius: 0.5rem;
transition: background 150ms ease;
&:hover, &:active { background: rgba(255,255,255,0.06); }
}
}
.actions {
display: flex;
gap: 1rem;
justify-content: flex-end;
margin-top: 1.5rem;
button {
font-size: 1rem;
padding: 0.85rem 1.75rem;
border-radius: 999px;
cursor: pointer;
border: none;
font-weight: 500;
transition: transform 150ms ease, opacity 150ms ease;
&:hover, &:active { transform: scale(0.97); }
}
.cancel { background: rgba(255,255,255,0.08); color: var(--nfc-text); }
.confirm {
background: hsl(var(--nfc-h), 80%, 55%);
color: white;
box-shadow: 0 4px 20px hsla(var(--nfc-h), 80%, 50%, 0.4);
}
}
}
// ─────────────────────────────────────────────────────────────────────
// Reduced-motion fallback — respect users who prefer no animation
// ─────────────────────────────────────────────────────────────────────
@media (prefers-reduced-motion: reduce) {
.nfc-kiosk::before { animation: none; }
.nfc-kiosk__icon-svg .nfc-wave,
.nfc-kiosk__icon-svg .nfc-chip,
#nfc_state_container > *,
.nfc-kiosk__logo,
.nfc-kiosk__result--success,
.nfc-kiosk__result--error,
.nfc-kiosk__avatar { animation: none; }
}