mirror of
https://github.com/phranck/TUIkit.git
synced 2026-06-20 09:54:37 +00:00
Adds realistic CRT monitor scanline sweep effect to terminal: **Visual Effect:** - Sharp bottom edge (cathode ray current position) - Trailing phosphor glow upward (150px trail) - Gradient: transparent → 0.02 → 0.04 → 0.08 → 0.1 - Sharp edge: 0.08 opacity, no blur - Trail: blur(3px) for smooth phosphor decay **Animation:** - 15s linear infinite - Moves from top to bottom within terminal area only - Minimal pause between cycles - Only active when terminal is powered on **Implementation:** - Container (150px) with gradient + sharp 2px bottom edge - Uses transform: translateY() for smooth animation - CSS @keyframes crt-scanline-move - Positioned within CRT glow layer (z-index 3) Inspired by real CRT electron beam behavior where the cathode ray sweeps the screen with phosphor persistence trailing behind.
345 lines
7.3 KiB
CSS
345 lines
7.3 KiB
CSS
@import "tailwindcss";
|
|
|
|
@font-face {
|
|
font-family: "WarText";
|
|
src: url("/fonts/WarText.ttf") format("truetype");
|
|
font-weight: normal;
|
|
font-style: normal;
|
|
font-display: swap;
|
|
}
|
|
|
|
/* Themes: Green → Amber → Red → Violet → Blue → White */
|
|
|
|
/* Theme: Green (default) */
|
|
:root,
|
|
[data-theme="green"] {
|
|
--background: #060a07;
|
|
--foreground: #33ff33;
|
|
--accent: #66ff66;
|
|
--accent-secondary: #00cc00;
|
|
--accent-glow: 102, 255, 102;
|
|
--accent-glow-secondary: 0, 204, 0;
|
|
--accent-glow-tertiary: 31, 143, 31;
|
|
--headline-color: #33ff33;
|
|
--headline-glow: 51, 255, 51;
|
|
--muted: #1f8f1f;
|
|
--border: #2d5a2d;
|
|
--container-body: #0e271c;
|
|
--container-cap: #0a1b13;
|
|
}
|
|
|
|
/* Theme: Amber */
|
|
[data-theme="amber"] {
|
|
--background: #0a0706;
|
|
--foreground: #ffaa00;
|
|
--accent: #ffcc33;
|
|
--accent-secondary: #cc9900;
|
|
--accent-glow: 255, 204, 51;
|
|
--accent-glow-secondary: 204, 153, 0;
|
|
--accent-glow-tertiary: 143, 102, 0;
|
|
--headline-color: #ffaa00;
|
|
--headline-glow: 255, 170, 0;
|
|
--muted: #8f6600;
|
|
--border: #5a4a2d;
|
|
--container-body: #251710;
|
|
--container-cap: #1e110e;
|
|
}
|
|
|
|
/* Theme: Red */
|
|
[data-theme="red"] {
|
|
--background: #0a0606;
|
|
--foreground: #ff4444;
|
|
--accent: #ff6666;
|
|
--accent-secondary: #cc4444;
|
|
--accent-glow: 255, 68, 68;
|
|
--accent-glow-secondary: 204, 51, 51;
|
|
--accent-glow-tertiary: 143, 34, 34;
|
|
--headline-color: #ff4444;
|
|
--headline-glow: 255, 68, 68;
|
|
--muted: #8f2222;
|
|
--border: #5a2d2d;
|
|
--container-body: #281112;
|
|
--container-cap: #1e0f10;
|
|
}
|
|
|
|
/* Theme: Violet */
|
|
[data-theme="violet"] {
|
|
--background: #08060a;
|
|
--foreground: #bb77ff;
|
|
--accent: #cc99ff;
|
|
--accent-secondary: #9944dd;
|
|
--accent-glow: 204, 153, 255;
|
|
--accent-glow-secondary: 153, 68, 221;
|
|
--accent-glow-tertiary: 119, 68, 170;
|
|
--headline-color: #bb77ff;
|
|
--headline-glow: 187, 119, 255;
|
|
--muted: #7744aa;
|
|
--border: #4a2d5a;
|
|
--container-body: #1a1028;
|
|
--container-cap: #120c1e;
|
|
}
|
|
|
|
/* Theme: Blue (VFD) */
|
|
[data-theme="blue"] {
|
|
--background: #060708;
|
|
--foreground: #00aaff;
|
|
--accent: #33bbff;
|
|
--accent-secondary: #0099dd;
|
|
--accent-glow: 51, 187, 255;
|
|
--accent-glow-secondary: 0, 153, 221;
|
|
--accent-glow-tertiary: 0, 102, 153;
|
|
--headline-color: #00aaff;
|
|
--headline-glow: 0, 170, 255;
|
|
--muted: #006699;
|
|
--border: #2d4a5a;
|
|
--container-body: #0e1825;
|
|
--container-cap: #0a121c;
|
|
}
|
|
|
|
/* Theme: White */
|
|
[data-theme="white"] {
|
|
--background: #06070a;
|
|
--foreground: #e8e8e8;
|
|
--accent: #ffffff;
|
|
--accent-secondary: #c0c0c0;
|
|
--accent-glow: 255, 255, 255;
|
|
--accent-glow-secondary: 192, 192, 192;
|
|
--accent-glow-tertiary: 120, 120, 120;
|
|
--headline-color: #e8e8e8;
|
|
--headline-glow: 232, 232, 232;
|
|
--muted: #787878;
|
|
--border: #484848;
|
|
--container-body: #111a2a;
|
|
--container-cap: #0d131d;
|
|
}
|
|
|
|
@theme inline {
|
|
--color-background: var(--background);
|
|
--color-foreground: var(--foreground);
|
|
--color-accent: var(--accent);
|
|
--color-accent-secondary: var(--accent-secondary);
|
|
--color-muted: var(--muted);
|
|
--color-border: var(--border);
|
|
--color-container-body: var(--container-body);
|
|
--color-container-cap: var(--container-cap);
|
|
--font-sans: var(--font-nunito);
|
|
--font-mono: var(--font-geist-mono);
|
|
}
|
|
|
|
body {
|
|
background: var(--background);
|
|
color: var(--foreground);
|
|
font-family: var(--font-nunito), system-ui, sans-serif;
|
|
font-size: 1.375rem;
|
|
line-height: 1.75;
|
|
}
|
|
|
|
/* Smooth scroll */
|
|
html {
|
|
scroll-behavior: smooth;
|
|
}
|
|
|
|
/* Code block styling */
|
|
code {
|
|
font-family: var(--font-geist-mono), ui-monospace, monospace;
|
|
}
|
|
|
|
/* Cloud animation keyframes — slow, massive wandering paths */
|
|
@keyframes cloud-drift {
|
|
0% {
|
|
transform: translate(0, 0) scale(1);
|
|
opacity: 0.5;
|
|
}
|
|
10% {
|
|
transform: translate(900px, -500px) scale(1.15);
|
|
opacity: 0.6;
|
|
}
|
|
22% {
|
|
transform: translate(-600px, 800px) scale(0.85);
|
|
opacity: 0.4;
|
|
}
|
|
38% {
|
|
transform: translate(1100px, 300px) scale(1.1);
|
|
opacity: 0.55;
|
|
}
|
|
52% {
|
|
transform: translate(-900px, -700px) scale(0.9);
|
|
opacity: 0.45;
|
|
}
|
|
68% {
|
|
transform: translate(500px, -1000px) scale(1.08);
|
|
opacity: 0.5;
|
|
}
|
|
82% {
|
|
transform: translate(-400px, 600px) scale(0.92);
|
|
opacity: 0.48;
|
|
}
|
|
100% {
|
|
transform: translate(0, 0) scale(1);
|
|
opacity: 0.5;
|
|
}
|
|
}
|
|
|
|
@keyframes cloud-drift-reverse {
|
|
0% {
|
|
transform: translate(0, 0) scale(1);
|
|
opacity: 0.4;
|
|
}
|
|
12% {
|
|
transform: translate(-1000px, 600px) scale(1.12);
|
|
opacity: 0.55;
|
|
}
|
|
25% {
|
|
transform: translate(700px, -900px) scale(0.88);
|
|
opacity: 0.35;
|
|
}
|
|
40% {
|
|
transform: translate(-500px, -800px) scale(1.08);
|
|
opacity: 0.5;
|
|
}
|
|
55% {
|
|
transform: translate(1100px, 400px) scale(0.92);
|
|
opacity: 0.42;
|
|
}
|
|
70% {
|
|
transform: translate(-800px, 700px) scale(1.06);
|
|
opacity: 0.48;
|
|
}
|
|
85% {
|
|
transform: translate(400px, -500px) scale(0.95);
|
|
opacity: 0.44;
|
|
}
|
|
100% {
|
|
transform: translate(0, 0) scale(1);
|
|
opacity: 0.4;
|
|
}
|
|
}
|
|
|
|
/* Spinner light pulse — fade in, hold with subtle flicker, fade out */
|
|
@keyframes spinner-pulse {
|
|
0% {
|
|
opacity: 0;
|
|
transform: scale(0.5);
|
|
}
|
|
12% {
|
|
opacity: var(--peak-opacity, 0.5);
|
|
transform: scale(1);
|
|
}
|
|
30% {
|
|
opacity: calc(var(--peak-opacity, 0.5) * 0.6);
|
|
}
|
|
45% {
|
|
opacity: var(--peak-opacity, 0.5);
|
|
}
|
|
60% {
|
|
opacity: calc(var(--peak-opacity, 0.5) * 0.7);
|
|
}
|
|
75% {
|
|
opacity: var(--peak-opacity, 0.5);
|
|
}
|
|
88% {
|
|
opacity: calc(var(--peak-opacity, 0.5) * 0.4);
|
|
transform: scale(0.8);
|
|
}
|
|
100% {
|
|
opacity: 0;
|
|
transform: scale(0.3);
|
|
}
|
|
}
|
|
|
|
/* Spinner light inward drift — flies toward center, shrinks as if receding into the scene.
|
|
Uses --drift-x and --drift-y in px set inline by SpinnerLights component. */
|
|
@keyframes spinner-pulse-inward {
|
|
0% {
|
|
opacity: 0;
|
|
translate: 0px 0px;
|
|
scale: 1;
|
|
}
|
|
10% {
|
|
opacity: var(--peak-opacity, 0.5);
|
|
scale: 0.95;
|
|
}
|
|
30% {
|
|
opacity: calc(var(--peak-opacity, 0.5) * 0.7);
|
|
scale: 0.8;
|
|
}
|
|
50% {
|
|
opacity: var(--peak-opacity, 0.5);
|
|
scale: 0.6;
|
|
}
|
|
70% {
|
|
opacity: calc(var(--peak-opacity, 0.5) * 0.6);
|
|
scale: 0.4;
|
|
}
|
|
90% {
|
|
opacity: calc(var(--peak-opacity, 0.5) * 0.3);
|
|
scale: 0.2;
|
|
}
|
|
100% {
|
|
opacity: 0;
|
|
translate: var(--drift-x) var(--drift-y);
|
|
scale: 0.1;
|
|
}
|
|
}
|
|
|
|
/* Headline cursor blink — classic terminal block cursor on/off */
|
|
@keyframes cursor-blink {
|
|
0%,
|
|
50% {
|
|
opacity: 1;
|
|
}
|
|
51%,
|
|
100% {
|
|
opacity: 0;
|
|
}
|
|
}
|
|
|
|
@utility animate-cursor-blink {
|
|
animation: cursor-blink 1s step-end infinite;
|
|
}
|
|
|
|
/* CRT scanline - moving element from top to bottom within container only */
|
|
@keyframes crt-scanline-move {
|
|
0% {
|
|
transform: translateY(-150px);
|
|
}
|
|
100% {
|
|
transform: translateY(calc(100% + 150px));
|
|
}
|
|
}
|
|
|
|
@keyframes cloud-drift-slow {
|
|
0% {
|
|
transform: translate(0, 0) scale(1);
|
|
opacity: 0.35;
|
|
}
|
|
14% {
|
|
transform: translate(800px, 900px) scale(1.1);
|
|
opacity: 0.5;
|
|
}
|
|
28% {
|
|
transform: translate(-1000px, -400px) scale(0.88);
|
|
opacity: 0.3;
|
|
}
|
|
42% {
|
|
transform: translate(600px, -1100px) scale(1.06);
|
|
opacity: 0.45;
|
|
}
|
|
57% {
|
|
transform: translate(-700px, 500px) scale(0.92);
|
|
opacity: 0.38;
|
|
}
|
|
72% {
|
|
transform: translate(1000px, -300px) scale(1.04);
|
|
opacity: 0.42;
|
|
}
|
|
86% {
|
|
transform: translate(-500px, 800px) scale(0.96);
|
|
opacity: 0.4;
|
|
}
|
|
100% {
|
|
transform: translate(0, 0) scale(1);
|
|
opacity: 0.35;
|
|
}
|
|
}
|