diff --git a/packages/console/app/src/component/light-rays.tsx b/packages/console/app/src/component/light-rays.tsx index 6c17410733..36b47a4777 100644 --- a/packages/console/app/src/component/light-rays.tsx +++ b/packages/console/app/src/component/light-rays.tsx @@ -49,9 +49,16 @@ export const defaultConfig: LightRaysConfig = { opacity: 0.35, } +export interface LightRaysAnimationState { + time: number + intensity: number + pulseValue: number +} + interface LightRaysProps { config: Accessor class?: string + onAnimationFrame?: (state: LightRaysAnimationState) => void } const hexToRgb = (hex: string): [number, number, number] => { @@ -141,7 +148,6 @@ const WGSL_SHADER = ` @vertex fn vertexMain(@builtin(vertex_index) vertexIndex: u32) -> VertexOutput { - // Full-screen triangle var positions = array, 3>( vec2(-1.0, -1.0), vec2(3.0, -1.0), @@ -523,7 +529,8 @@ export default function LightRays(props: LightRaysProps) { } const currentConfig = props.config() - uniformDataRef.iTime = t * 0.001 + const timeSeconds = t * 0.001 + uniformDataRef.iTime = timeSeconds if (currentConfig.followMouse && currentConfig.mouseInfluence > 0.0) { const smoothing = 0.92 @@ -534,6 +541,24 @@ export default function LightRays(props: LightRaysProps) { uniformDataRef.mousePos = [smoothMouseRef.x, smoothMouseRef.y] } + if (props.onAnimationFrame) { + const pulseCenter = (currentConfig.pulsatingMin + currentConfig.pulsatingMax) * 0.5 + const pulseAmplitude = (currentConfig.pulsatingMax - currentConfig.pulsatingMin) * 0.5 + const pulseValue = currentConfig.pulsating + ? pulseCenter + pulseAmplitude * Math.sin(timeSeconds * currentConfig.raysSpeed * 3.0) + : 1.0 + + const baseIntensity1 = 0.45 + 0.15 * Math.sin(timeSeconds * currentConfig.raysSpeed * 1.5) + const baseIntensity2 = 0.3 + 0.2 * Math.cos(timeSeconds * currentConfig.raysSpeed * 1.1) + const intensity = (baseIntensity1 + baseIntensity2) * pulseValue + + props.onAnimationFrame({ + time: timeSeconds, + intensity, + pulseValue, + }) + } + try { const uniformData = createUniformBufferCorrected(uniformDataRef) deviceRef.queue.writeBuffer(uniformBufferRef, 0, uniformData.buffer) diff --git a/packages/console/app/src/routes/black.css b/packages/console/app/src/routes/black.css index e02b17a8b4..a0cd5712b3 100644 --- a/packages/console/app/src/routes/black.css +++ b/packages/console/app/src/routes/black.css @@ -185,7 +185,7 @@ h1 { color: rgba(255, 255, 255, 0.92); - font-size: 18px; + font-size: 16px; font-style: normal; font-weight: 400; line-height: 1.45; @@ -194,11 +194,15 @@ @media (min-width: 768px) { font-size: 20px; } + + @media (max-width: 480px) { + font-size: 14px; + } } p { color: rgba(255, 255, 255, 0.59); - font-size: 18px; + font-size: 16px; font-style: normal; font-weight: 400; line-height: 1.45; @@ -207,36 +211,46 @@ @media (min-width: 768px) { font-size: 20px; } + + @media (max-width: 480px) { + font-size: 14px; + } } } [data-slot="hero-black"] { margin-top: 40px; padding: 0 20px; + position: relative; @media (min-width: 768px) { margin-top: 60px; } svg { - --hero-black-fill-from: hsl(0 0% 100%); - --hero-black-fill-to: hsl(0 0% 100% / 0%); - --hero-black-stroke-from: hsl(0 0% 100% / 60%); - --hero-black-stroke-to: hsl(0 0% 100% / 0%); - width: 100%; max-width: 590px; height: auto; - filter: drop-shadow(0 0 20px rgba(255, 255, 255, 0.1)); + overflow: visible; + filter: drop-shadow(0 0 20px rgba(255, 255, 255, calc(0.1 + var(--hero-black-glow-intensity, 0) * 0.15))) + drop-shadow(0 -5px 30px rgba(255, 255, 255, calc(var(--hero-black-glow-intensity, 0) * 0.2))); mask-image: linear-gradient(to bottom, black, transparent); stroke-width: 1.5; - [data-slot="black-fill"] { + [data-slot="black-base"] { fill: url(#hero-black-fill-gradient); + stroke: url(#hero-black-stroke-gradient); } - [data-slot="black-stroke"] { - fill: url(#hero-black-stroke-gradient); + [data-slot="black-glow"] { + fill: url(#hero-black-top-glow); + pointer-events: none; + } + + [data-slot="black-shimmer"] { + fill: url(#hero-black-shimmer-gradient); + pointer-events: none; + mix-blend-mode: overlay; } } } @@ -348,7 +362,8 @@ gap: 12px; padding: 24px; border: 1px solid rgba(255, 255, 255, 0.17); - background: black; + background-color: rgba(0, 0, 0, 0.75); + backdrop-filter: blur(4px); background-clip: padding-box; border-radius: 4px; text-decoration: none; @@ -356,6 +371,10 @@ cursor: pointer; text-align: left; + @media (max-width: 480px) { + padding: 16px; + } + &:hover:not(:active) { border-color: rgba(255, 255, 255, 0.35); } @@ -400,6 +419,15 @@ width: 100%; max-width: 660px; margin: 0 auto; + position: relative; + background-color: rgba(0, 0, 0, 0.75); + backdrop-filter: blur(4px); + z-index: 1; + + @media (max-width: 480px) { + margin: 0 20px; + width: calc(100% - 40px); + } } [data-slot="selected-card"] { @@ -458,7 +486,6 @@ line-height: 1.5; padding-left: 16px; position: relative; - white-space: nowrap; &::before { content: "▪"; @@ -466,6 +493,10 @@ left: 0; color: rgba(255, 255, 255, 0.39); } + + @media (max-width: 768px) { + font-size: 12px; + } } } diff --git a/packages/console/app/src/routes/black.tsx b/packages/console/app/src/routes/black.tsx index 59d7b13b45..36c9d1eaf0 100644 --- a/packages/console/app/src/routes/black.tsx +++ b/packages/console/app/src/routes/black.tsx @@ -3,7 +3,7 @@ import { Title, Meta, Link } from "@solidjs/meta" import { createMemo, createSignal } from "solid-js" import { github } from "~/lib/github" import { config } from "~/config" -import LightRays, { defaultConfig, type LightRaysConfig } from "~/component/light-rays" +import LightRays, { defaultConfig, type LightRaysConfig, type LightRaysAnimationState } from "~/component/light-rays" import "./black.css" export default function BlackLayout(props: RouteSectionProps) { @@ -18,6 +18,47 @@ export default function BlackLayout(props: RouteSectionProps) { ) const [lightRaysConfig, setLightRaysConfig] = createSignal(defaultConfig) + const [rayAnimationState, setRayAnimationState] = createSignal({ + time: 0, + intensity: 0.5, + pulseValue: 1, + }) + + const svgLightingValues = createMemo(() => { + const state = rayAnimationState() + const t = state.time + + const wave1 = Math.sin(t * 1.5) * 0.5 + 0.5 + const wave2 = Math.sin(t * 2.3 + 1.2) * 0.5 + 0.5 + const wave3 = Math.sin(t * 0.8 + 2.5) * 0.5 + 0.5 + + const shimmerPos = Math.sin(t * 0.7) * 0.5 + 0.5 + const glowIntensity = state.intensity * state.pulseValue * 0.35 + const fillOpacity = 0.1 + wave1 * 0.08 * state.pulseValue + const strokeBrightness = 55 + wave2 * 25 * state.pulseValue + + const shimmerIntensity = wave3 * 0.15 * state.pulseValue + + return { + glowIntensity, + fillOpacity, + strokeBrightness, + shimmerPos, + shimmerIntensity, + } + }) + + const svgLightingStyle = createMemo(() => { + const values = svgLightingValues() + return { + "--hero-black-glow-intensity": values.glowIntensity.toFixed(3), + "--hero-black-stroke-brightness": `${values.strokeBrightness.toFixed(0)}%`, + } as Record + }) + + const handleAnimationFrame = (state: LightRaysAnimationState) => { + setRayAnimationState(state) + } return ( -
+
- - - + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
{props.children}