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:
Thomas Hallock 2025-11-08 14:41:29 -06:00
parent dd14062112
commit e88380a48d
1 changed files with 33 additions and 2 deletions

View File

@ -194,6 +194,7 @@ const STATUS_CODE_EASTER_EGGS: Record<
export default function NotFound() {
const [abacusValue, setAbacusValue] = useState(404);
const [activeEasterEgg, setActiveEasterEgg] = useState<number | null>(null);
const [fadeKey, setFadeKey] = useState(0);
const { updateConfig, resetToDefaults } = useAbacusDisplay();
// Easter egg activation - update global abacus config when special codes are entered
@ -202,6 +203,7 @@ export default function NotFound() {
if (easterEgg && activeEasterEgg !== abacusValue) {
setActiveEasterEgg(abacusValue);
setFadeKey((prev) => prev + 1); // Trigger fade animation
// Update global abacus display config to use custom beads
// This affects ALL abaci rendered in the app until page reload!
@ -215,6 +217,7 @@ export default function NotFound() {
} else if (!easterEgg && activeEasterEgg !== null) {
// User changed away from an easter egg code - reset to defaults
setActiveEasterEgg(null);
setFadeKey((prev) => prev + 1); // Trigger fade animation
resetToDefaults();
(window as any).__easterEggMode = null;
}
@ -227,14 +230,29 @@ export default function NotFound() {
return (
<PageWithNav>
<style>
{`
@keyframes fadeInText {
from {
opacity: 0;
transform: translateY(-10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
`}
</style>
<div
className={css({
minHeight: "calc(100vh - 64px)",
minHeight: "100vh",
display: "flex",
alignItems: "center",
justifyContent: "center",
background: currentTheme?.bgGradient || "bg.canvas",
padding: { base: "1rem", sm: "2rem" },
paddingTop: { base: "10rem", sm: "12rem", md: "14rem", lg: "16rem" },
transition: "background 0.6s ease-in-out",
position: "relative",
overflow: "hidden",
@ -303,6 +321,7 @@ export default function NotFound() {
})}
>
<h1
key={fadeKey}
className={css({
fontSize: {
base: "1.75rem",
@ -316,10 +335,22 @@ export default function NotFound() {
textShadow: currentTheme?.glowColor
? `0 0 20px ${currentTheme.glowColor}, 0 0 40px ${currentTheme.glowColor}`
: "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",
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
? STATUS_CODE_EASTER_EGGS[activeEasterEgg].message