Files
boocode/apps/web/src/styles/themes/effects/boocode-classic-fx.css
indifferentketchup f42c673881 feat: futuristic theme ladder + stacked landing banner
Add three opt-in dark themes (BooCode+, BooCode Classic, BooCode
Override) plus an in-place Ember polish, on a class-scoped effects
engine: matrix rain, a neon grid field, and frosted glass, all gated
by a localStorage "Animated background" toggle and prefers-reduced-
motion. Extend the server theme_id whitelist so the new ids persist,
and replace the Home landing wordmark with the stacked mascot +
wordmark banner.
2026-06-03 14:16:59 +00:00

269 lines
11 KiB
CSS

/* BooCode Classic — effects layer. Faithful revival of /opt/boolab
data-mode='boocode' terminal-HUD chrome.
Scoping contract (immutable):
- EVERY rule is scoped under `.theme-boocode-classic` so the other 22 themes
are byte-for-byte unaffected.
- EVERY continuous keyframe animation is *additionally* scoped under
`html.bc-anim-on.theme-boocode-classic`. ThemeFx sets `bc-anim-on` only
when the Animated-background toggle is ON *and* prefers-reduced-motion is
not set — so toggling either off removes the class and freezes all motion.
@keyframes names are global (CSS can't scope the at-rule itself); they are
`bc-`-prefixed and only *referenced* under the gated selector, so no
animation runs unless the gate is on.
The matrix-rain canvas lives in components/fx/MatrixRain.tsx (mounted by
ThemeFx). This sheet adds: an Orbitron display wordmark + blinking caret, an
app-wide scanline sweep, orange card hover glow + lift, terminal-frame chrome
on the rails, and the ported boolab `.bc-*` / `.boocode-*` design-system
classes (dormant until a HUD component consumes them). */
.theme-boocode-classic {
--bc-orange: #f97316;
--bc-amber: #fbbf24;
--bc-rust: #c2410c;
--bc-glow-orange: 0 0 24px color-mix(in srgb, var(--bc-orange) 32%, transparent);
--bc-glow-soft: 0 0 40px color-mix(in srgb, var(--bc-orange) 14%, transparent);
}
/* ── Orbitron display wordmark ────────────────────────────────────────────
Applied to the "BooCode" text heading via the additive `boocode-display`
class (the only text wordmark — the sidebar wordmark is an <img>, which a
font can't restyle). Orbitron 800 is JS-imported in main.tsx. */
.theme-boocode-classic .boocode-display {
font-family: 'Orbitron', var(--font-sans);
font-weight: 800;
letter-spacing: 0.06em;
color: var(--bc-orange);
text-shadow: 0 0 14px color-mix(in srgb, var(--bc-orange) 45%, transparent);
}
/* Terminal cursor after the wordmark. Always rendered (a solid block when
motion is off — boolab's reduced-motion behaviour); blinks only when gated. */
.theme-boocode-classic .boocode-display::after {
content: '▮';
margin-left: 0.12em;
color: var(--bc-amber);
opacity: 1;
}
html.bc-anim-on.theme-boocode-classic .boocode-display::after {
animation: bc-caret-blink 1s steps(1, end) infinite;
}
/* ── App-wide scanline sweep ──────────────────────────────────────────────
A soft warm band that drifts down the viewport on a calm 8s cycle.
`position: fixed` keeps it viewport-locked (never creates document overflow);
mix-blend-mode: screen only lightens, so text underneath stays legible.
Defined ONLY under the gate → vanishes entirely when motion is off (the warm
palette + static rain-off state is the reduced-motion fallback).
`.h-dvh.bg-background` is unique to the AppShell root (App.tsx). */
html.bc-anim-on.theme-boocode-classic .h-dvh.bg-background::after {
content: '';
position: fixed;
inset: 0;
z-index: 2;
pointer-events: none;
background: linear-gradient(
180deg,
transparent 46%,
color-mix(in srgb, var(--bc-orange) 9%, transparent) 50%,
transparent 54%
);
mix-blend-mode: screen;
animation: bc-scanline 8s linear infinite;
will-change: transform;
}
/* ── Card hover glow + lift ───────────────────────────────────────────────
The shadcn Card primitive (`data-slot="card"`). The orange rim + drop glow
apply on hover under any condition (hover feedback is acceptable under
reduced motion); the transition timing and the 1px lift are gated so motion
off = instant, no travel. */
.theme-boocode-classic [data-slot="card"]:hover {
border-color: color-mix(in srgb, var(--bc-orange) 55%, transparent);
box-shadow:
0 0 0 1px color-mix(in srgb, var(--bc-orange) 22%, transparent),
0 6px 18px -8px color-mix(in srgb, var(--bc-orange) 40%, transparent);
}
html.bc-anim-on.theme-boocode-classic [data-slot="card"] {
transition: border-color 160ms ease, box-shadow 160ms ease, transform 160ms ease;
}
html.bc-anim-on.theme-boocode-classic [data-slot="card"]:hover {
transform: translateY(-1px);
}
/* ── Terminal-frame chrome on the rails ───────────────────────────────────
Monospace + a hairline orange edge-light on the sidebar (vertical list that
already truncates — low overflow risk). Pane top-bars are intentionally left
alone: they are crowded control rows where a wider mono font risks wrapping. */
.theme-boocode-classic .bg-sidebar {
font-family: var(--font-mono);
box-shadow: inset -1px 0 0 0 color-mix(in srgb, var(--bc-orange) 8%, transparent);
}
/* Floating surfaces (menus / dialogs / popovers) are intentionally NOT given an
inset box-shadow here — that would clobber shadcn's `shadow-md` elevation
(higher specificity) and flatten menus over the rain. The warm `--popover` /
`--border` tokens already theme them. */
/* ──────────────────────────────────────────────────────────────────────────
Ported boolab design-system classes. These are faithful copies of the
/opt/boolab `.bc-*` / `.boocode-*` chrome. No component in this repo renders
these class names yet (boolab's RepoStatusBar / HUD components don't exist
here), so they are DORMANT — shipped so the Classic design system is complete
and any future HUD element (prompt line, status pill, kbd hint, breadcrumb)
lights up automatically. All scoped to the theme; animations gated.
────────────────────────────────────────────────────────────────────────── */
/* Terminal prompt line: `$ boocode @host: path (branch)` */
.theme-boocode-classic .bc-prompt-line {
display: flex;
align-items: center;
gap: 0.5rem;
flex-wrap: wrap;
row-gap: 0.25rem;
font-family: var(--font-mono);
font-size: 0.8125rem;
color: var(--muted-foreground);
padding: 0.5rem 0.75rem;
background: var(--popover);
border-bottom: 1px solid var(--border);
}
.theme-boocode-classic .bc-prompt-host { color: var(--bc-orange); }
.theme-boocode-classic .bc-prompt-branch { color: var(--bc-orange); opacity: 0.8; }
.theme-boocode-classic .bc-prompt-dollar {
color: var(--bc-orange);
text-shadow: 0 0 6px var(--bc-orange);
margin-right: 0.25rem;
}
/* Standalone blinking block caret. */
.theme-boocode-classic .bc-caret {
display: inline-block;
width: 0.6em;
height: 1em;
background: currentColor;
vertical-align: text-bottom;
margin-left: 0.1em;
}
html.bc-anim-on.theme-boocode-classic .bc-caret {
animation: bc-caret-blink 1s steps(1, end) infinite;
}
/* IDLE / SYNCING / ERROR status pills. */
.theme-boocode-classic .bc-status-pill {
display: inline-flex;
align-items: center;
gap: 0.25rem;
padding: 0.1rem 0.5rem;
border-radius: 999px;
border: 1px solid var(--border);
background: var(--card);
font-size: 0.6875rem;
text-transform: uppercase;
letter-spacing: 0.12em;
font-family: var(--font-mono);
}
.theme-boocode-classic .bc-status-idle { color: #7ae07a; border-color: rgba(122, 224, 122, 0.4); }
.theme-boocode-classic .bc-status-syncing {
color: var(--bc-orange);
border-color: color-mix(in srgb, var(--bc-orange) 60%, transparent);
}
.theme-boocode-classic .bc-status-error { color: #ff6b6b; border-color: rgba(255, 107, 107, 0.5); }
.theme-boocode-classic .bc-status-syncing::before {
content: '▮';
color: var(--bc-orange);
}
html.bc-anim-on.theme-boocode-classic .bc-status-syncing::before {
animation: bc-caret-blink 1s steps(1, end) infinite;
}
/* Keyboard hint chip. */
.theme-boocode-classic .bc-key-hint {
display: inline-flex;
align-items: center;
justify-content: center;
min-width: 1.25rem;
padding: 0 0.3rem;
height: 1.1rem;
border: 1px solid var(--border);
border-radius: 0.25rem;
font-family: var(--font-mono);
font-size: 0.625rem;
color: var(--muted-foreground);
background: var(--popover);
}
/* Uppercase orange breadcrumb / overline. */
.theme-boocode-classic .boocode-breadcrumb {
font-family: 'Orbitron', var(--font-sans);
font-size: 0.6875rem;
letter-spacing: 0.22em;
text-transform: uppercase;
color: var(--bc-orange);
text-shadow: 0 0 8px color-mix(in srgb, var(--bc-orange) 40%, transparent);
}
/* Inset terminal-frame border. */
.theme-boocode-classic .boocode-terminal-frame {
border: 1px solid var(--border);
box-shadow:
inset 0 0 0 1px color-mix(in srgb, var(--bc-orange) 5%, transparent),
0 0 0 1px #0a0604;
}
/* Card hover glow + lift utility (boolab `.bc-card`), for non-shadcn surfaces. */
.theme-boocode-classic .bc-card {
position: relative;
border: 1px solid color-mix(in srgb, var(--bc-orange) 22%, transparent);
background: var(--card);
padding: 1rem;
border-radius: 0.375rem;
overflow: hidden;
}
.theme-boocode-classic .bc-card:hover {
border-color: color-mix(in srgb, var(--bc-orange) 55%, transparent);
box-shadow:
0 0 0 1px color-mix(in srgb, var(--bc-orange) 22%, transparent),
0 6px 18px -8px color-mix(in srgb, var(--bc-orange) 40%, transparent);
}
html.bc-anim-on.theme-boocode-classic .bc-card {
transition: border-color 160ms ease, box-shadow 160ms ease, transform 160ms ease;
}
html.bc-anim-on.theme-boocode-classic .bc-card:hover {
transform: translateY(-1px);
}
/* ── Keyframes (global names, referenced only under the gate above) ───────── */
@keyframes bc-caret-blink {
0%, 49% { opacity: 1; }
50%, 100% { opacity: 0; }
}
@keyframes bc-scanline {
0% { transform: translateY(-100%); }
100% { transform: translateY(100%); }
}
/* ── Reduced-motion belt-and-suspenders ───────────────────────────────────
bc-anim-on already excludes reduced-motion (ThemeFx), but if the class ever
lingered, this hard-stops every animation and the lift transform. */
@media (prefers-reduced-motion: reduce) {
.theme-boocode-classic .boocode-display::after,
.theme-boocode-classic .bc-caret,
.theme-boocode-classic .bc-status-syncing::before {
animation: none;
opacity: 1;
}
.theme-boocode-classic .h-dvh.bg-background::after {
animation: none;
display: none;
}
.theme-boocode-classic [data-slot="card"],
.theme-boocode-classic .bc-card {
transition: none;
}
.theme-boocode-classic [data-slot="card"]:hover,
.theme-boocode-classic .bc-card:hover {
transform: none;
}
}