feat: add smooth fade-in animation for 404 message text changes
When the user changes the abacus value to trigger different easter eggs, the message text now fades in smoothly with a subtle upward motion. - Add fadeKey state that increments on text changes - Use key prop on h1 to force re-mount and trigger animation - Custom fadeInText keyframe: opacity 0→1 with translateY(-10px→0) - 0.5s ease-out animation timing - Prevents jarring text swaps, creates polished UX 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
dd14062112
commit
e88380a48d
|
|
@ -194,6 +194,7 @@ const STATUS_CODE_EASTER_EGGS: Record<
|
||||||
export default function NotFound() {
|
export default function NotFound() {
|
||||||
const [abacusValue, setAbacusValue] = useState(404);
|
const [abacusValue, setAbacusValue] = useState(404);
|
||||||
const [activeEasterEgg, setActiveEasterEgg] = useState<number | null>(null);
|
const [activeEasterEgg, setActiveEasterEgg] = useState<number | null>(null);
|
||||||
|
const [fadeKey, setFadeKey] = useState(0);
|
||||||
const { updateConfig, resetToDefaults } = useAbacusDisplay();
|
const { updateConfig, resetToDefaults } = useAbacusDisplay();
|
||||||
|
|
||||||
// Easter egg activation - update global abacus config when special codes are entered
|
// Easter egg activation - update global abacus config when special codes are entered
|
||||||
|
|
@ -202,6 +203,7 @@ export default function NotFound() {
|
||||||
|
|
||||||
if (easterEgg && activeEasterEgg !== abacusValue) {
|
if (easterEgg && activeEasterEgg !== abacusValue) {
|
||||||
setActiveEasterEgg(abacusValue);
|
setActiveEasterEgg(abacusValue);
|
||||||
|
setFadeKey((prev) => prev + 1); // Trigger fade animation
|
||||||
|
|
||||||
// Update global abacus display config to use custom beads
|
// Update global abacus display config to use custom beads
|
||||||
// This affects ALL abaci rendered in the app until page reload!
|
// This affects ALL abaci rendered in the app until page reload!
|
||||||
|
|
@ -215,6 +217,7 @@ export default function NotFound() {
|
||||||
} else if (!easterEgg && activeEasterEgg !== null) {
|
} else if (!easterEgg && activeEasterEgg !== null) {
|
||||||
// User changed away from an easter egg code - reset to defaults
|
// User changed away from an easter egg code - reset to defaults
|
||||||
setActiveEasterEgg(null);
|
setActiveEasterEgg(null);
|
||||||
|
setFadeKey((prev) => prev + 1); // Trigger fade animation
|
||||||
resetToDefaults();
|
resetToDefaults();
|
||||||
(window as any).__easterEggMode = null;
|
(window as any).__easterEggMode = null;
|
||||||
}
|
}
|
||||||
|
|
@ -227,14 +230,29 @@ export default function NotFound() {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PageWithNav>
|
<PageWithNav>
|
||||||
|
<style>
|
||||||
|
{`
|
||||||
|
@keyframes fadeInText {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(-10px);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`}
|
||||||
|
</style>
|
||||||
<div
|
<div
|
||||||
className={css({
|
className={css({
|
||||||
minHeight: "calc(100vh - 64px)",
|
minHeight: "100vh",
|
||||||
display: "flex",
|
display: "flex",
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
justifyContent: "center",
|
justifyContent: "center",
|
||||||
background: currentTheme?.bgGradient || "bg.canvas",
|
background: currentTheme?.bgGradient || "bg.canvas",
|
||||||
padding: { base: "1rem", sm: "2rem" },
|
padding: { base: "1rem", sm: "2rem" },
|
||||||
|
paddingTop: { base: "10rem", sm: "12rem", md: "14rem", lg: "16rem" },
|
||||||
transition: "background 0.6s ease-in-out",
|
transition: "background 0.6s ease-in-out",
|
||||||
position: "relative",
|
position: "relative",
|
||||||
overflow: "hidden",
|
overflow: "hidden",
|
||||||
|
|
@ -303,6 +321,7 @@ export default function NotFound() {
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<h1
|
<h1
|
||||||
|
key={fadeKey}
|
||||||
className={css({
|
className={css({
|
||||||
fontSize: {
|
fontSize: {
|
||||||
base: "1.75rem",
|
base: "1.75rem",
|
||||||
|
|
@ -316,10 +335,22 @@ export default function NotFound() {
|
||||||
textShadow: currentTheme?.glowColor
|
textShadow: currentTheme?.glowColor
|
||||||
? `0 0 20px ${currentTheme.glowColor}, 0 0 40px ${currentTheme.glowColor}`
|
? `0 0 20px ${currentTheme.glowColor}, 0 0 40px ${currentTheme.glowColor}`
|
||||||
: "none",
|
: "none",
|
||||||
transition: "all 0.6s ease-in-out",
|
transition: "color 0.6s ease-in-out, text-shadow 0.6s ease-in-out",
|
||||||
letterSpacing: "-0.02em",
|
letterSpacing: "-0.02em",
|
||||||
px: { base: "1rem", sm: "2rem" },
|
px: { base: "1rem", sm: "2rem" },
|
||||||
|
minHeight: {
|
||||||
|
base: "calc(1.75rem * 1.1 * 2)",
|
||||||
|
sm: "calc(2.5rem * 1.1 * 2)",
|
||||||
|
md: "calc(3.5rem * 1.1 * 2)",
|
||||||
|
lg: "calc(4rem * 1.1 * 2)",
|
||||||
|
},
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
justifyContent: "center",
|
||||||
})}
|
})}
|
||||||
|
style={{
|
||||||
|
animation: "fadeInText 0.5s ease-out",
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
{activeEasterEgg
|
{activeEasterEgg
|
||||||
? STATUS_CODE_EASTER_EGGS[activeEasterEgg].message
|
? STATUS_CODE_EASTER_EGGS[activeEasterEgg].message
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue