Files
boocode/apps/web/src/styles/themes/effects/boocode-plus-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

206 lines
10 KiB
CSS
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.
/* BooCode+ — effects layer. Tasteful sci-fi: calm, premium, Linear-grade.
EVERY rule is scoped under `.theme-boocode-plus.dark` so (a) the other 22
themes are byte-for-byte unaffected and (b) the light variant stays a clean
flat slate — dark is the priority, so the glass/gradient/glow ship dark-only.
No canvas, no animation loop, no scanlines. Just: a faint STATIC ambient
gradient on the app background, frosted glass on CHROME ONLY (rails, menus,
dialogs, popovers, the composer, pane top-bars), a restrained indigo glow on
the primary action, and spring-eased transitions on chrome state changes.
iOS / quality guardrails honored:
- backdrop-blur capped at 812px; no nested blur-on-blur (the dialog
overlay's own backdrop-blur is neutralized so only the dialog CONTENT
frosts — one layer).
- opaque fallback: every glass rule lives inside @supports(backdrop-filter),
so a browser without it keeps the plain opaque Tailwind utility
(bg-sidebar/bg-popover/bg-card = solid token). prefers-reduced-transparency
forces the same opaque path.
- spring via cubic-bezier (NOT the linear() function — Safari <17.2 gap),
confined to box-shadow / transform / color / filter — never layout props.
- chat messages, tool-call cards and code blocks (bg-muted/30·/20, bg-card
bubbles) are deliberately NOT targeted — glass never sits behind reading
content; the ambient gradient behind it stays faint & opaque-based (AA).
Glass targets are stable, verified hooks: bg-sidebar (both rails), bg-popover
(all menus/dialogs/popovers/sheets), the composer's unique
focus-within:ring-primary/15 box, [data-slot=button][data-variant=default]
(the primary action), and the compound .border-b.bg-muted/30·/20 (pane
top-bars — distinct from message bubbles, which use .border.rounded-lg). */
.theme-boocode-plus.dark {
/* Spring curves. --bcp-spring overshoots a touch (press/glow bloom);
--bcp-ease is a smooth Linear-style decel for color/opacity. */
--bcp-spring: cubic-bezier(0.34, 1.42, 0.5, 1);
--bcp-ease: cubic-bezier(0.32, 0.72, 0, 1);
--bcp-blur: 10px;
}
/* ── Static ambient gradient ─────────────────────────────────────────────
Painted on the app-shell root (.h-dvh is unique to App.tsx). Opaque base
(var(--background)) + three faint indigo radials = depth without cost. The
MessageList scroll area is transparent, so the gradient reads faintly behind
chat — but it's static and opaque-based: the brightest stop (indigo @14% over
#0f1117) still gives ~10:1 for #e3e6f1 body text. The glass rails blur it for
a premium parallax-of-light feel. No background-attachment:fixed (iOS jank);
.h-dvh doesn't scroll, so the field is already viewport-stable. */
.theme-boocode-plus.dark .h-dvh {
background-color: var(--background);
background-image:
radial-gradient(1200px 760px at 8% -12%,
color-mix(in oklab, #5e6ad2 14%, transparent), transparent 58%),
radial-gradient(1000px 680px at 102% 4%,
color-mix(in oklab, #5a73d8 10%, transparent), transparent 54%),
radial-gradient(1100px 900px at 50% 128%,
color-mix(in oklab, #2c3270 16%, transparent), transparent 60%);
background-repeat: no-repeat;
}
/* ── Frosted glass on chrome ─────────────────────────────────────────────
Progressive enhancement: only inside @supports does the surface go
translucent + blur. Without backdrop-filter support the rules vanish and the
plain opaque utility (solid token bg) shows — that IS the opaque fallback.
Each background has an rgba() line first (covers the rare browser that has
backdrop-filter but not color-mix, e.g. Safari 15) then the color-mix line. */
@supports ((-webkit-backdrop-filter: blur(1px)) or (backdrop-filter: blur(1px))) {
/* Side rails (ProjectSidebar + RightRail). Blur the ambient gradient behind
them → the signature glass plane. Kept at 80% so the dense nav text stays
crisp; the blur + saturate sells the effect, not heavy transparency. */
.theme-boocode-plus.dark .bg-sidebar {
background-color: rgba(19, 21, 29, 0.80);
background-color: color-mix(in oklab, var(--sidebar) 80%, transparent);
-webkit-backdrop-filter: blur(var(--bcp-blur)) saturate(140%);
backdrop-filter: blur(var(--bcp-blur)) saturate(140%);
}
/* Every floating surface: dialogs, dropdown / context / sub menus, the
@-mention & slash pickers, the mobile bottom sheet, the message-actions
menu. All carry bg-popover and all float over content → one clean blur. */
.theme-boocode-plus.dark .bg-popover {
background-color: rgba(26, 29, 46, 0.84);
background-color: color-mix(in oklab, var(--popover) 84%, transparent);
-webkit-backdrop-filter: blur(12px) saturate(150%);
backdrop-filter: blur(12px) saturate(150%);
}
/* The composer message box (ChatInput) — unique focus-within ring hook.
Frosts the tail of the conversation as it scrolls beneath. 82% keeps the
textarea text readable even over a bright code block underneath. */
.theme-boocode-plus.dark .focus-within\:ring-primary\/15 {
background-color: rgba(26, 29, 46, 0.82);
background-color: color-mix(in oklab, var(--card) 82%, transparent);
-webkit-backdrop-filter: blur(var(--bcp-blur)) saturate(140%);
backdrop-filter: blur(var(--bcp-blur)) saturate(140%);
}
/* Pane top-bars (Coder/Workspace/terminal-hotkey/artifact headers). The
compound .border-b.bg-muted/N selector excludes chat bubbles & tool cards
(those use .border.rounded-lg). Lightest blur (8px) + an inset top hairline
for the edge-lit premium feel. */
.theme-boocode-plus.dark .border-b.bg-muted\/30,
.theme-boocode-plus.dark .border-b.bg-muted\/20 {
background-color: rgba(28, 32, 47, 0.62);
background-color: color-mix(in oklab, var(--muted) 62%, transparent);
-webkit-backdrop-filter: blur(8px) saturate(130%);
backdrop-filter: blur(8px) saturate(130%);
box-shadow: inset 0 1px 0 0 color-mix(in oklab, #aab2ff 8%, transparent);
}
}
/* Dialog scrim: deepen the dim (the stock bg-black/10 is too light to anchor a
frosted modal) and kill its own backdrop-blur so the only blur layer is the
dialog CONTENT above — no nested blur-on-blur. Unconditional: when blur is
unsupported the stock overlay had no blur anyway, and the deeper scrim is
harmless. */
.theme-boocode-plus.dark [data-slot="dialog-overlay"] {
background-color: rgba(7, 8, 14, 0.55);
-webkit-backdrop-filter: none;
backdrop-filter: none;
}
/* ── Restrained indigo glow on the primary action ────────────────────────
The default-variant Button only (Send, primary confirm). Resting: a hairline
indigo rim + soft drop glow. Hover: the glow blooms with the spring curve.
Press: a composited squash. box-shadow/transform/filter only — no reflow. */
.theme-boocode-plus.dark [data-slot="button"][data-variant="default"] {
box-shadow:
0 0 0 1px color-mix(in oklab, #5e6ad2 38%, transparent),
0 4px 16px -6px color-mix(in oklab, #5e6ad2 50%, transparent);
transition:
box-shadow 0.28s var(--bcp-spring),
transform 0.2s var(--bcp-spring),
filter 0.2s var(--bcp-ease),
background-color 0.18s var(--bcp-ease);
}
.theme-boocode-plus.dark [data-slot="button"][data-variant="default"]:not([disabled]):hover {
box-shadow:
0 0 0 1px color-mix(in oklab, #5e6ad2 60%, transparent),
0 8px 26px -6px color-mix(in oklab, #5e6ad2 68%, transparent);
filter: brightness(1.06);
}
.theme-boocode-plus.dark [data-slot="button"][data-variant="default"]:not([disabled]):active {
transform: translateY(0.5px) scale(0.985);
}
/* ── Spring-eased state on the rest of the chrome ─────────────────────────
Smooth (non-overshoot) color/bg transitions on menu items and the composer
focus, so hovers and focus feel intentional, not instant. Color props only. */
.theme-boocode-plus.dark [data-slot="dropdown-menu-item"],
.theme-boocode-plus.dark [data-slot="context-menu-item"],
.theme-boocode-plus.dark [data-slot="dropdown-menu-sub-trigger"],
.theme-boocode-plus.dark [data-slot="context-menu-sub-trigger"] {
transition:
background-color 0.16s var(--bcp-ease),
color 0.16s var(--bcp-ease);
}
.theme-boocode-plus.dark .focus-within\:ring-primary\/15 {
transition:
border-color 0.22s var(--bcp-ease),
box-shadow 0.22s var(--bcp-spring),
background-color 0.22s var(--bcp-ease);
}
/* ── Accessibility fallbacks ──────────────────────────────────────────────
Reduced transparency: drop every glass surface to its solid token bg and
remove all blur. Declared after the @supports block so it wins. */
@media (prefers-reduced-transparency: reduce) {
.theme-boocode-plus.dark .bg-sidebar {
background-color: var(--sidebar);
-webkit-backdrop-filter: none;
backdrop-filter: none;
}
.theme-boocode-plus.dark .bg-popover {
background-color: var(--popover);
-webkit-backdrop-filter: none;
backdrop-filter: none;
}
.theme-boocode-plus.dark .focus-within\:ring-primary\/15 {
background-color: var(--card);
-webkit-backdrop-filter: none;
backdrop-filter: none;
}
.theme-boocode-plus.dark .border-b.bg-muted\/30,
.theme-boocode-plus.dark .border-b.bg-muted\/20 {
background-color: var(--muted);
-webkit-backdrop-filter: none;
backdrop-filter: none;
box-shadow: none;
}
}
/* Reduced motion: no spring/transition, no press transform. The ambient
gradient is static, so it (correctly) stays. */
@media (prefers-reduced-motion: reduce) {
.theme-boocode-plus.dark [data-slot="button"][data-variant="default"],
.theme-boocode-plus.dark [data-slot="dropdown-menu-item"],
.theme-boocode-plus.dark [data-slot="context-menu-item"],
.theme-boocode-plus.dark [data-slot="dropdown-menu-sub-trigger"],
.theme-boocode-plus.dark [data-slot="context-menu-sub-trigger"],
.theme-boocode-plus.dark .focus-within\:ring-primary\/15 {
transition: none;
}
.theme-boocode-plus.dark [data-slot="button"][data-variant="default"]:not([disabled]):active {
transform: none;
}
}