mirror of
https://github.com/Such-Software/such.software.git
synced 2026-10-17 05:32:05 +02:00
Nebula monitor: typing terminal cursor, stronger impact, tame pointer, restore mobile streamers
- Replace the centered dash with a tiny CSS terminal in the screen: types 'such' once, backspaces, then the caret blinks on the left (steps() width typewriter). - Impact pulse is snappier and brighter (0.9s ping, scale 1.14, bright cyan glow) so streamer hits actually read; the screen color shifts with it. - Pointer parallax dialed down to a subtle 6px with a gentler spring, fixing the desktop 'shift up-left after load' (it was easing toward the cursor on first move). - Revert the mobile streamer thinning/density cut from the prior commit: it made them too sparse to see. Mobile now gets the full set at full weight, like desktop.
This commit is contained in:
@@ -234,6 +234,24 @@
|
||||
@keyframes splash-zoom { 0% { transform: scale(1); } 50% { transform: scale(1.06); } 100% { transform: scale(1.14); } }
|
||||
.splash-zoom { animation: splash-zoom 0.8s ease-in-out forwards; }
|
||||
|
||||
/* Blinking terminal cursor inside the nebula monitor (opacity only = compositor). */
|
||||
@keyframes nebula-cursor-blink { 0%, 49% { opacity: 0.85; } 50%, 100% { opacity: 0; } }
|
||||
.nebula-cursor { animation: nebula-cursor-blink 1.1s steps(1, end) infinite; }
|
||||
/* Tiny terminal inside the nebula monitor: type "such" once, backspace, then a blinking
|
||||
caret on the left. width via steps() is the typewriter; border-right is the caret. */
|
||||
.nebula-term {
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
||||
font-size: 9px;
|
||||
line-height: 1;
|
||||
letter-spacing: 0.5px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
width: 0;
|
||||
border-right: 1.5px solid currentColor;
|
||||
animation: nebula-type 5s steps(4, end) forwards, nebula-caret 0.9s steps(1, end) infinite;
|
||||
}
|
||||
@keyframes nebula-type {
|
||||
0% { width: 0; }
|
||||
16% { width: 4ch; }
|
||||
60% { width: 4ch; }
|
||||
74% { width: 0; }
|
||||
100% { width: 0; }
|
||||
}
|
||||
@keyframes nebula-caret { 50% { border-color: transparent; } }
|
||||
@@ -105,8 +105,8 @@ export const NebulaField = ({ className, density = 8, themeMode = 'dark', positi
|
||||
// mousemove, so there is no mobile cost.
|
||||
const pointerXRaw = useMotionValue(0);
|
||||
const pointerYRaw = useMotionValue(0);
|
||||
const pointerX = useSpring(pointerXRaw, { stiffness: 60, damping: 18, mass: 0.4 });
|
||||
const pointerY = useSpring(pointerYRaw, { stiffness: 60, damping: 18, mass: 0.4 });
|
||||
const pointerX = useSpring(pointerXRaw, { stiffness: 40, damping: 22, mass: 0.6 });
|
||||
const pointerY = useSpring(pointerYRaw, { stiffness: 40, damping: 22, mass: 0.6 });
|
||||
|
||||
// Monitor position: just to the left of the hero title, on every screen. The extra
|
||||
// top room the hero gets on mobile (see page.tsx) keeps this clear of the header.
|
||||
@@ -118,8 +118,9 @@ export const NebulaField = ({ className, density = 8, themeMode = 'dark', positi
|
||||
const targetX = computerX + 1.5;
|
||||
const targetY = computerY + 1.5;
|
||||
|
||||
// Fewer, slightly thinner streamers on mobile for a cleaner read on a small screen.
|
||||
const effectiveDensity = isMobile ? 5 : density;
|
||||
// Same streamer count on every screen. Reducing it on mobile made them too sparse
|
||||
// to read (they looked like there were none).
|
||||
const effectiveDensity = density;
|
||||
|
||||
// Check for reduced motion preference
|
||||
useEffect(() => {
|
||||
@@ -144,7 +145,7 @@ export const NebulaField = ({ className, density = 8, themeMode = 'dark', positi
|
||||
// Pointer parallax driver (desktop only). Spring-smoothed via pointerX/Y above.
|
||||
useEffect(() => {
|
||||
if (isMobile) return;
|
||||
const MAX = 12; // px of nudge at the screen edges
|
||||
const MAX = 6; // px of nudge at the screen edges (subtle, spring-eased)
|
||||
const onMove = (e: MouseEvent) => {
|
||||
pointerXRaw.set(((e.clientX / window.innerWidth) - 0.5) * 2 * MAX);
|
||||
pointerYRaw.set(((e.clientY / window.innerHeight) - 0.5) * 2 * MAX);
|
||||
@@ -189,13 +190,13 @@ export const NebulaField = ({ className, density = 8, themeMode = 'dark', positi
|
||||
setMonitorColor(colors[Math.floor(Math.random() * colors.length)]);
|
||||
|
||||
monitorControls.start({
|
||||
scale: [1, 1.1, 1],
|
||||
scale: [1, 1.14, 1],
|
||||
filter: [
|
||||
"brightness(1) drop-shadow(0 0 0px rgba(0,0,0,0))",
|
||||
"brightness(1.3) drop-shadow(0 0 12px currentColor)",
|
||||
"brightness(1.7) drop-shadow(0 0 18px currentColor)",
|
||||
"brightness(1) drop-shadow(0 0 0px rgba(0,0,0,0))"
|
||||
],
|
||||
transition: { duration: 1.5, ease: "easeOut" }
|
||||
transition: { duration: 0.9, ease: "easeOut" }
|
||||
});
|
||||
}, [monitorControls, themeMode]);
|
||||
|
||||
@@ -265,13 +266,9 @@ export const NebulaField = ({ className, density = 8, themeMode = 'dark', positi
|
||||
>
|
||||
<motion.div animate={monitorControls} className={cn("relative transition-colors duration-1000", monitorColor)}>
|
||||
<Monitor size={56} />
|
||||
{/* Live-terminal blink inside the screen. CSS opacity only, so it runs on the
|
||||
compositor for essentially no main-thread cost. */}
|
||||
<span
|
||||
aria-hidden="true"
|
||||
className="nebula-cursor pointer-events-none absolute left-1/2 top-[38%] h-[3px] w-[9px] -translate-x-1/2 rounded-[1px]"
|
||||
style={{ backgroundColor: "currentColor" }}
|
||||
/>
|
||||
{/* Tiny terminal in the screen: types "such" once, backspaces, then the caret
|
||||
blinks on the left. Pure CSS (steps + width), so it costs ~nothing. */}
|
||||
<span aria-hidden="true" className="nebula-term pointer-events-none absolute left-[26%] top-[30%]">such</span>
|
||||
</motion.div>
|
||||
</motion.div>
|
||||
|
||||
@@ -287,7 +284,7 @@ export const NebulaField = ({ className, density = 8, themeMode = 'dark', positi
|
||||
d={d}
|
||||
themeMode={themeMode}
|
||||
onHit={handleHit}
|
||||
width={(themeMode === 'light' ? 0.08 : 0.12) * (isMobile ? 0.85 : 1)}
|
||||
width={themeMode === 'light' ? 0.08 : 0.12}
|
||||
index={i}
|
||||
/>
|
||||
))}
|
||||
|
||||
Reference in New Issue
Block a user