2026-04-04 10:49:10 -04:00
|
|
|
/* =========================================================
|
|
|
|
|
REGULAR SCANLINES SETTINGS
|
|
|
|
|
========================================================= */
|
2025-05-29 17:03:50 -05:00
|
|
|
|
|
|
|
|
$scan-width: 1px;
|
|
|
|
|
$scan-crt: false;
|
|
|
|
|
$scan-fps: 20;
|
2026-04-04 10:49:10 -04:00
|
|
|
$scan-color: rgba(#000, 0.30);
|
2025-05-29 17:03:50 -05:00
|
|
|
$scan-z-index: 2147483648;
|
|
|
|
|
|
|
|
|
|
$scan-moving-line: true;
|
2026-04-04 10:49:10 -04:00
|
|
|
$scan-opacity: 0.75;
|
|
|
|
|
|
|
|
|
|
/* =========================================================
|
|
|
|
|
CRT / S-VIDEO EFFECT SETTINGS
|
|
|
|
|
========================================================= */
|
|
|
|
|
|
|
|
|
|
// whole-screen softness
|
2026-04-11 18:23:00 -04:00
|
|
|
$crt-soft-blur: 0.6px;
|
2026-04-04 10:49:10 -04:00
|
|
|
|
|
|
|
|
// mild brightening / contrast to keep blur from looking muddy
|
2026-04-11 18:23:00 -04:00
|
|
|
$crt-contrast: 1.02;
|
|
|
|
|
$crt-saturation: 1.12;
|
|
|
|
|
$crt-brightness: 1.00;
|
2026-04-04 10:49:10 -04:00
|
|
|
|
|
|
|
|
// fake horizontal chroma bleed
|
2026-04-11 18:23:00 -04:00
|
|
|
$crt-r-shift: -1px;
|
|
|
|
|
$crt-b-shift: 1px;
|
|
|
|
|
$crt-bleed-blur: 1.8px;
|
|
|
|
|
$crt-rgb-opacity: 0.05;
|
2026-04-04 10:49:10 -04:00
|
|
|
|
|
|
|
|
// subtle tube edge darkening
|
|
|
|
|
$crt-vignette-opacity: 0.16;
|
|
|
|
|
|
|
|
|
|
// optional tiny bloom
|
2026-04-11 18:23:00 -04:00
|
|
|
$crt-glow-opacity: 0.08;
|
2026-04-04 10:49:10 -04:00
|
|
|
|
|
|
|
|
/* =========================================================
|
|
|
|
|
MIXINS
|
|
|
|
|
========================================================= */
|
|
|
|
|
|
|
|
|
|
@mixin scan-crt($enabled) {
|
|
|
|
|
@if $enabled == true {
|
|
|
|
|
animation: scanlines 1s steps($scan-fps) infinite;
|
|
|
|
|
} @else {
|
|
|
|
|
animation: none;
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-05-29 17:03:50 -05:00
|
|
|
|
2026-04-04 10:49:10 -04:00
|
|
|
@mixin scan-moving($enabled) {
|
|
|
|
|
@if $enabled == true {
|
|
|
|
|
animation: scanline 6s linear infinite;
|
|
|
|
|
} @else {
|
|
|
|
|
animation: none;
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-05-29 17:03:50 -05:00
|
|
|
|
2026-04-04 10:49:10 -04:00
|
|
|
/* =========================================================
|
|
|
|
|
APPLY TO THE REAL APP CONTAINER
|
|
|
|
|
========================================================= */
|
2025-05-29 17:03:50 -05:00
|
|
|
|
2026-04-04 10:49:10 -04:00
|
|
|
/*
|
|
|
|
|
You can add class="scanlines" to #divTwcMain or #container.
|
|
|
|
|
Example:
|
|
|
|
|
<div id="divTwcMain" class="scanlines">
|
|
|
|
|
*/
|
2025-05-29 17:03:50 -05:00
|
|
|
|
2026-04-04 10:49:10 -04:00
|
|
|
.scanlines {
|
|
|
|
|
position: relative;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
isolation: isolate;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
This is the actual rendered weather area in your HTML.
|
|
|
|
|
Applying the softness here affects the maps/text/icons themselves.
|
|
|
|
|
*/
|
|
|
|
|
#container {
|
|
|
|
|
position: relative;
|
|
|
|
|
z-index: 1;
|
|
|
|
|
transform: translateZ(0);
|
|
|
|
|
will-change: filter;
|
|
|
|
|
|
|
|
|
|
filter:
|
|
|
|
|
blur($crt-soft-blur)
|
|
|
|
|
saturate($crt-saturation)
|
|
|
|
|
contrast($crt-contrast)
|
|
|
|
|
brightness($crt-brightness);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
Red fringe overlay
|
|
|
|
|
*/
|
|
|
|
|
#container::before,
|
|
|
|
|
#container::after {
|
|
|
|
|
content: '';
|
|
|
|
|
position: absolute;
|
|
|
|
|
inset: 0;
|
|
|
|
|
pointer-events: none;
|
|
|
|
|
z-index: 3;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#container::before {
|
|
|
|
|
background:
|
|
|
|
|
linear-gradient(
|
|
|
|
|
to right,
|
|
|
|
|
rgba(255, 0, 0, $crt-rgb-opacity) 0%,
|
|
|
|
|
rgba(255, 0, 0, 0.01) 15%,
|
|
|
|
|
rgba(255, 0, 0, 0.00) 50%,
|
|
|
|
|
rgba(255, 0, 0, 0.01) 85%,
|
|
|
|
|
rgba(255, 0, 0, $crt-rgb-opacity) 100%
|
|
|
|
|
);
|
|
|
|
|
transform: translateX($crt-r-shift);
|
|
|
|
|
filter: blur($crt-bleed-blur);
|
|
|
|
|
mix-blend-mode: screen;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
Blue fringe overlay
|
|
|
|
|
*/
|
|
|
|
|
#container::after {
|
|
|
|
|
background:
|
|
|
|
|
linear-gradient(
|
|
|
|
|
to right,
|
|
|
|
|
rgba(0, 140, 255, $crt-rgb-opacity) 0%,
|
|
|
|
|
rgba(0, 140, 255, 0.01) 15%,
|
|
|
|
|
rgba(0, 140, 255, 0.00) 50%,
|
|
|
|
|
rgba(0, 140, 255, 0.01) 85%,
|
|
|
|
|
rgba(0, 140, 255, $crt-rgb-opacity) 100%
|
|
|
|
|
);
|
|
|
|
|
transform: translateX($crt-b-shift);
|
|
|
|
|
filter: blur($crt-bleed-blur);
|
|
|
|
|
mix-blend-mode: screen;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
Moving scanline
|
|
|
|
|
*/
|
|
|
|
|
&:before,
|
|
|
|
|
&:after {
|
|
|
|
|
display: block;
|
|
|
|
|
pointer-events: none;
|
|
|
|
|
content: '';
|
|
|
|
|
position: absolute;
|
|
|
|
|
left: 0;
|
|
|
|
|
right: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
&:before {
|
|
|
|
|
height: var(--scanline-thickness, $scan-width);
|
|
|
|
|
z-index: $scan-z-index + 2;
|
|
|
|
|
background: $scan-color;
|
|
|
|
|
opacity: $scan-opacity;
|
|
|
|
|
@include scan-moving($scan-moving-line);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
Regular scanline mask
|
|
|
|
|
*/
|
|
|
|
|
&:after {
|
|
|
|
|
top: 0;
|
|
|
|
|
bottom: 0;
|
|
|
|
|
z-index: $scan-z-index;
|
|
|
|
|
background: repeating-linear-gradient(
|
|
|
|
|
to bottom,
|
|
|
|
|
transparent 0,
|
|
|
|
|
transparent var(--scanline-thickness, $scan-width),
|
|
|
|
|
$scan-color var(--scanline-thickness, $scan-width),
|
|
|
|
|
$scan-color calc(var(--scanline-thickness, $scan-width) * 2)
|
|
|
|
|
);
|
|
|
|
|
@include scan-crt($scan-crt);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
Vignette layer
|
|
|
|
|
Added as an inset shadow so you don't need extra HTML
|
|
|
|
|
*/
|
|
|
|
|
box-shadow:
|
|
|
|
|
inset 0 0 80px rgba(0, 0, 0, $crt-vignette-opacity),
|
|
|
|
|
inset 0 0 18px rgba(255, 255, 255, $crt-glow-opacity);
|
2025-05-29 17:03:50 -05:00
|
|
|
}
|
|
|
|
|
|
2026-04-04 10:49:10 -04:00
|
|
|
/* =========================================================
|
|
|
|
|
OPTIONAL: only affect active weather panels, not menus
|
|
|
|
|
========================================================= */
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
If the controls / bottom nav get too blurry, move the blur
|
|
|
|
|
from #container to the weather slides only:
|
|
|
|
|
*/
|
|
|
|
|
.scanlines.crt-panels-only {
|
|
|
|
|
#container {
|
|
|
|
|
filter: none;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.weather-display {
|
|
|
|
|
filter:
|
|
|
|
|
blur($crt-soft-blur)
|
|
|
|
|
saturate($crt-saturation)
|
|
|
|
|
contrast($crt-contrast)
|
|
|
|
|
brightness($crt-brightness);
|
|
|
|
|
|
|
|
|
|
transform: translateZ(0);
|
|
|
|
|
}
|
2025-05-29 17:03:50 -05:00
|
|
|
}
|
|
|
|
|
|
2026-04-04 10:49:10 -04:00
|
|
|
/* =========================================================
|
|
|
|
|
OPTIONAL: make text slightly glow like old TV phosphors
|
|
|
|
|
========================================================= */
|
2025-05-29 17:03:50 -05:00
|
|
|
|
|
|
|
|
.scanlines {
|
2026-04-04 10:49:10 -04:00
|
|
|
.header,
|
|
|
|
|
.main,
|
|
|
|
|
.scroll,
|
|
|
|
|
.date-time,
|
|
|
|
|
.city,
|
|
|
|
|
.temp,
|
|
|
|
|
.condition,
|
|
|
|
|
.location,
|
|
|
|
|
.label,
|
|
|
|
|
.value,
|
|
|
|
|
.title {
|
|
|
|
|
text-shadow:
|
|
|
|
|
0 0 1px rgba(255, 255, 255, 0.18),
|
|
|
|
|
0 0 2px rgba(255, 255, 255, 0.06);
|
|
|
|
|
}
|
2025-05-29 17:03:50 -05:00
|
|
|
}
|
|
|
|
|
|
2026-04-04 10:49:10 -04:00
|
|
|
/* =========================================================
|
|
|
|
|
ANIMATIONS
|
|
|
|
|
========================================================= */
|
|
|
|
|
|
2025-05-29 17:03:50 -05:00
|
|
|
@keyframes scanline {
|
2026-04-04 10:49:10 -04:00
|
|
|
0% {
|
|
|
|
|
transform: translate3d(0, 200000%, 0);
|
|
|
|
|
}
|
2025-05-29 17:03:50 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@keyframes scanlines {
|
2026-04-04 10:49:10 -04:00
|
|
|
0% {
|
|
|
|
|
background-position: 0 50%;
|
|
|
|
|
}
|
2025-06-24 22:38:25 -04:00
|
|
|
}
|