Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added packages/app/public/decorative/minecraft/tnt.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
35 changes: 35 additions & 0 deletions packages/app/src/app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,41 @@
display: none;
}

/* Ender-dragon fly-across — one-shot animation on minecraft theme activation,
* triggered from minecraft-decorations.tsx. Plays once (iteration-count: 1)
* and lands the dragon off-screen left so the GIF stops being rendered. */
@keyframes mc-dragon-flyacross {
0% {
transform: translate(110vw, 0) scale(1);
opacity: 0;
}
8% {
opacity: 0.95;
}
50% {
transform: translate(40vw, -3vh) scale(1.05);
}
92% {
opacity: 0.95;
}
100% {
transform: translate(-130vw, 1vh) scale(1);
opacity: 0;
}
}

.mc-dragon-flyacross {
animation: mc-dragon-flyacross 9s cubic-bezier(0.4, 0, 0.6, 1) 1 forwards;
will-change: transform, opacity;
}

@media (prefers-reduced-motion: reduce) {
.mc-dragon-flyacross {
animation: none;
display: none;
}
}

/* Splash text — yellow bouncing rotated text (Minecraft title screen style) */
@keyframes splash-bounce {
0%,
Expand Down
2 changes: 2 additions & 0 deletions packages/app/src/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { Footer } from '@/components/footer/footer';
import { Header } from '@/components/header/header';
import { CircuitBackground } from '@/components/circuit-background';
import { MinecraftBackgroundLazy } from '@/components/minecraft/minecraft-background-lazy';
import { MinecraftDecorations } from '@/components/minecraft/minecraft-decorations';
import { ThemeProvider } from '@/components/ui/theme-provider';
import {
AUTHOR_HANDLE,
Expand Down Expand Up @@ -178,6 +179,7 @@ export default async function RootLayout({
<body className={`${dm_sans.variable} antialiased relative min-h-screen flex flex-col`}>
<CircuitBackground />
<MinecraftBackgroundLazy />
<MinecraftDecorations />
<PostHogProvider>
<script type="application/ld+json">{JSON.stringify(jsonLd)}</script>
<QueryProvider>
Expand Down
164 changes: 164 additions & 0 deletions packages/app/src/components/minecraft/minecraft-decorations.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
'use client';

import { useEffect, useState } from 'react';

/**
* Decorative Minecraft asset PNGs scattered around the viewport corners,
* plus a one-shot Ender Dragon fly-across at theme activation. Only renders
* while the minecraft theme is active. Watches `document.documentElement`
* class changes via a MutationObserver so it appears / disappears live with
* the mode-toggle (mirrors `minecraft-toggles.tsx`).
*
* All static images are `pointer-events: none` at low z-index. The dragon is
* absolutely positioned and flies right-to-left once per theme activation
* (animation-iteration-count: 1) — the GIF's wing-flap plays during the
* traversal so the entrance feels alive.
*
* Asset provenance: minecraft.wiki (CC BY-NC-SA 3.0) for blocks/items/dragon.
*/
const DECORATIONS = [
// Top-left: Zombified Piglin, mirrored so its sword arm points toward the
// middle of the screen rather than off-screen left. Keep the original
// -6deg tilt — flipping over X also flips the rotate direction visually,
// which lands as a slight forward lean toward the chart area.
{
src: '/decorative/minecraft/zombified-piglin.png',
alt: 'Zombified Piglin',
style: {
top: '5rem',
left: '0.5rem',
width: 'min(110px, 9vw)',
transform: 'scaleX(-1) rotate(-6deg)',
},
},
// Top-right: Diamond pickaxe, mining vibe.
{
src: '/decorative/minecraft/diamond-pickaxe.png',
alt: 'Diamond pickaxe',
style: {
top: '5rem',
right: '0.5rem',
width: 'min(110px, 9vw)',
transform: 'rotate(35deg)',
},
},
// Bottom-left: Grass block, classic anchor.
{
src: '/decorative/minecraft/grass-block.png',
alt: 'Grass block',
style: {
bottom: '4rem',
left: '0.5rem',
width: 'min(110px, 9vw)',
transform: 'rotate(-4deg)',
},
},
// Bottom-mid-left: TNT, ready to go boom.
{
src: '/decorative/minecraft/tnt.png',
alt: 'TNT',
style: {
bottom: '4rem',
left: '20%',
width: 'min(95px, 8vw)',
transform: 'rotate(6deg)',
},
},
// Bottom-right: Diamond, the loot.
{
src: '/decorative/minecraft/diamond.png',
alt: 'Diamond',
style: {
bottom: '4rem',
right: '0.5rem',
width: 'min(95px, 8vw)',
transform: 'rotate(-6deg)',
},
},
] as const;

export function MinecraftDecorations() {
const [active, setActive] = useState(false);
// Bumps each time the theme is (re)activated, used as the React key on the
// dragon `<img>` to force-remount and re-trigger the CSS fly-across.
const [dragonNonce, setDragonNonce] = useState(0);

useEffect(() => {
let wasMinecraft = document.documentElement.classList.contains('minecraft');
setActive(wasMinecraft);
if (wasMinecraft) setDragonNonce((n) => n + 1);

const check = () => {
const isMinecraft = document.documentElement.classList.contains('minecraft');
setActive(isMinecraft);
// Re-trigger the dragon only on a transition off→on, not on every
// unrelated class change.
if (isMinecraft && !wasMinecraft) setDragonNonce((n) => n + 1);
wasMinecraft = isMinecraft;
};

const observer = new MutationObserver(check);
observer.observe(document.documentElement, { attributes: true, attributeFilter: ['class'] });
return () => observer.disconnect();
}, []);

if (!active) return null;

return (
<>
<div
aria-hidden="true"
className="hidden lg:block fixed inset-0 pointer-events-none z-0 overflow-hidden"
>
{DECORATIONS.map((d) => (
<img
key={d.src}
src={d.src}
alt={d.alt}
className="absolute opacity-70 [image-rendering:pixelated] drop-shadow-[0_0_14px_rgba(0,0,0,0.45)]"
style={d.style}
/>
))}
</div>

{/* One-shot Ender Dragon fly-across. Keyed on dragonNonce so it
* remounts (and the CSS animation re-plays) every theme activation. */}
<div
aria-hidden="true"
className="hidden md:block fixed inset-0 pointer-events-none z-0 overflow-hidden"
>
<img
key={dragonNonce}
src="/decorative/minecraft/ender-dragon.gif"
alt=""
className="absolute top-[15%] [image-rendering:pixelated] drop-shadow-[0_0_24px_rgba(160,80,200,0.55)] mc-dragon-flyacross"
style={{ width: 'min(280px, 22vw)' }}
/>
</div>

<div className="hidden lg:block fixed bottom-1 right-2 z-0 text-[10px] text-foreground/50 text-right leading-tight">
<div>
art:{' '}
<a
href="https://minecraft.wiki/"
target="_blank"
rel="noopener noreferrer"
className="underline hover:text-foreground"
>
minecraft.wiki
</a>
</div>
<div>
<a
href="https://creativecommons.org/licenses/by-nc-sa/3.0/"
target="_blank"
rel="noopener noreferrer"
className="underline hover:text-foreground"
>
CC BY-NC-SA 3.0
</a>
</div>
</div>
</>
);
}
Loading