Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
9386bb4
feat(theme): dark-only token system and Geist type foundation
Coder-soft Oct 9, 2026
0a58f37
fix(theme): restore original brand purple and document scrolling
Coder-soft Oct 9, 2026
fe67864
feat(ui): redesign home components (batch 2)
Coder-soft Oct 9, 2026
0c5311e
feat(ui): make buttons full pills
Coder-soft Oct 9, 2026
64bfa2c
feat(ui): redesign resources hub (batch 3)
Coder-soft Oct 9, 2026
c6bda25
feat(ui): Montserrat Bold headings
Coder-soft Oct 9, 2026
7acfb5f
feat(ui): sweep remaining resource components (batch 4)
Coder-soft Oct 9, 2026
68760d0
feat(ui): sweep remaining pages and components (batch 5)
Coder-soft Oct 9, 2026
11f2125
refactor(theme): drop dead pixel/cow CSS and unused fonts
Coder-soft Oct 9, 2026
402f87f
fix(ui): QA pass — dialog titles and mangled hover utilities
Coder-soft Oct 9, 2026
89164c8
fix(favorites): key card favourites by resource URL
Coder-soft Oct 9, 2026
34f24e3
feat(ui): pop transition between donate and back-to-top buttons
Coder-soft Oct 9, 2026
43569e6
feat(ui): rework Background Gen hover preview
Coder-soft Oct 9, 2026
ce46c58
fix(ui): resource hub polish
Coder-soft Oct 9, 2026
37b732c
fix(mcicons): make the category browser scrollable
Coder-soft Oct 9, 2026
41b4efb
fix(ui): use overflow-x: clip on the root so the page scrolls
Coder-soft Oct 9, 2026
64ad760
fix(mcicons): use a plain scroll container in the category browsers
Coder-soft Oct 10, 2026
47afd32
fix(ui): stop SelectContent from locking page scroll
Coder-soft Oct 10, 2026
2998e79
feat(ui): pixel browser icons, unbold headers, compact audio player
Coder-soft Oct 10, 2026
a7129bc
fix(ui): render browser header labels in body font (not headings)
Coder-soft Oct 10, 2026
4416df6
feat(ui): restyle the card category label
Coder-soft Oct 10, 2026
20d8b4c
chore(ui): remove 'Powered by' caption from the resources hub
Coder-soft Oct 10, 2026
eca87ac
fix(ui): hide the waveform cursor bar in the audio player
Coder-soft Oct 10, 2026
c626af5
fix(ui): music packs browser layout
Coder-soft Oct 10, 2026
3c08b9f
style(ui): remove translucent surfaces
Coder-soft Oct 10, 2026
a4e62ad
feat(ui): show YouTube icon + video title on music pack links
Coder-soft Oct 10, 2026
d7654f0
feat(ui): load music moods from the assets API
Coder-soft Oct 10, 2026
66418c6
fix(ui): restore the waveform playhead
Coder-soft Oct 10, 2026
4cd648c
fix(ui): match the waveform loading/error overlay background to the w…
Coder-soft Oct 10, 2026
38788da
fix(ui): font previews, dialog category label, contact redesign
Coder-soft Oct 10, 2026
c559dd2
fix(ui): only one audio preview can play at a time
Coder-soft Oct 10, 2026
081456a
feat(ui): chamfered "render panel" corner language
Coder-soft Oct 11, 2026
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
7 changes: 3 additions & 4 deletions index.html
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<!DOCTYPE html>
<html lang="en">
<html lang="en" class="dark">

<head>
<meta charset="UTF-8" />
Expand All @@ -23,8 +23,7 @@
<meta name="author" content="Team Wheels" />
<meta name="copyrighted-site-verification" content="7d293d30ff247a9a" />
<meta name="robots" content="index, follow, max-image-preview:large, max-snippet:-1, max-video-preview:-1" data-rh="true" />
<meta name="theme-color" content="#9333ea" media="(prefers-color-scheme: light)" />
<meta name="theme-color" content="#0b0b12" media="(prefers-color-scheme: dark)" />
<meta name="theme-color" content="#0A0C0F" />
<link rel="canonical" href="https://renderdragon.org" data-rh="true" />

<meta property="og:title" content="Renderdragon - Minecraft YouTube Tools & Assets" data-rh="true" />
Expand Down Expand Up @@ -58,7 +57,7 @@
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="preload" href="/fonts/minecraftia.woff2" as="font" type="font/woff2" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Press+Start+2P&family=VT323&family=Chakra+Petch:wght@300;400;500;600;700&family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=VT323&display=swap" rel="stylesheet">
<script type="application/ld+json">
{
"@context": "https://schema.org",
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
"@fontsource/geist-mono": "^5.2.7",
"@fontsource/geist-sans": "^5.2.5",
"@fontsource/jetbrains-mono": "^5.2.8",
"@fontsource/montserrat": "^5.3.0",
"@google/genai": "^0.13.0",
"@hcaptcha/react-hcaptcha": "^1.17.4",
"@headlessui/react": "^2.2.9",
Expand Down
8 changes: 8 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 1 addition & 14 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ const CreatorPackPage = lazy(() => import("@/pages/CreatorPackPage"));

const LoadingFallback = ({ message = "Loading..." }: { message?: string }) => (
<div className="flex flex-col items-center justify-center min-h-screen gap-4">
<IconLoader2 className="w-12 h-12 animate-spin text-cow-purple" />
<IconLoader2 className="w-12 h-12 animate-spin text-primary" />
<p className="text-white/80">{message}</p>
</div>
);
Expand Down Expand Up @@ -162,19 +162,6 @@ const HomeRedirect = () => {
const App = () => {
const [queryClient] = useState(() => new QueryClient());

// Global Theme Initialization
const [theme] = useState(() => {
return localStorage.getItem('theme') as 'light' | 'dark' ||
(window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light');
});

useEffect(() => {
if (theme === 'dark') {
document.documentElement.classList.add('dark');
} else {
document.documentElement.classList.remove('dark');
}
}, [theme]);
return (
<ErrorBoundary>
<QueryClientProvider client={queryClient}>
Expand Down
2 changes: 1 addition & 1 deletion src/components/AdBlockDetector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ export function AdBlockDetector() {
<AlertDialog open={isOpen} onOpenChange={setIsOpen}>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle className="font-minecraftia text-2xl">AdBlocker Detected</AlertDialogTitle>
<AlertDialogTitle className="text-2xl">AdBlocker Detected</AlertDialogTitle>
<AlertDialogDescription className="space-y-2">
<p>
It looks like you are using an adblocker.
Expand Down
75 changes: 48 additions & 27 deletions src/components/AudioPlayer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import {
import { Button } from '@/components/ui/button';
import { cn } from '@/lib/utils';

const AUDIO_PLAY_EVENT = 'rd:audio-play';

interface AudioPlayerProps {
src: string;
className?: string;
Expand All @@ -27,6 +29,23 @@ const AudioPlayer = ({ src, className, isInView = true, allowPlayBeforeReady = f

const containerRef = useRef<HTMLDivElement>(null);
const wavesurfer = useRef<import('wavesurfer.js').default | null>(null);
const instanceId = useRef<string>(
typeof crypto !== 'undefined' && 'randomUUID' in crypto
? crypto.randomUUID()
: `${Date.now()}-${Math.random().toString(36).slice(2)}`,
);

// Only one audio preview may play at a time: when a player starts, the others
// pause themselves.
useEffect(() => {
const handleOtherPlay = (event: Event) => {
if ((event as CustomEvent).detail === instanceId.current) return;
const ws = wavesurfer.current;
if (ws && ws.isPlaying()) ws.pause();
};
window.addEventListener(AUDIO_PLAY_EVENT, handleOtherPlay);
return () => window.removeEventListener(AUDIO_PLAY_EVENT, handleOtherPlay);
}, []);

useEffect(() => {
if (!containerRef.current) return;
Expand All @@ -41,11 +60,11 @@ const AudioPlayer = ({ src, className, isInView = true, allowPlayBeforeReady = f
container: containerRef.current,
waveColor: 'rgba(139, 92, 246, 0.2)', // Soft cow-purple
progressColor: '#8b5cf6', // Solid cow-purple
cursorColor: '#8b5cf6',
cursorColor: '#c4b5fd',
cursorWidth: 2,
barWidth: 2,
barRadius: 4,
height: 60,
height: 48,
barGap: 3,
normalize: true,
hideScrollbar: true,
Expand Down Expand Up @@ -78,7 +97,11 @@ const AudioPlayer = ({ src, className, isInView = true, allowPlayBeforeReady = f
setCurrentTime(ws.getCurrentTime());
});

ws.on('play', () => isMounted && setIsPlaying(true));
ws.on('play', () => {
if (!isMounted) return;
setIsPlaying(true);
window.dispatchEvent(new CustomEvent(AUDIO_PLAY_EVENT, { detail: instanceId.current }));
});
ws.on('pause', () => isMounted && setIsPlaying(false));
ws.on('finish', () => isMounted && setIsPlaying(false));
}).catch((error: unknown) => {
Expand Down Expand Up @@ -130,76 +153,74 @@ const AudioPlayer = ({ src, className, isInView = true, allowPlayBeforeReady = f

return (
<div className={cn(
"w-full rounded-xl bg-card border-2 border-primary/10 p-5 shadow-lg group/player transition-all duration-300 hover:border-primary/30",
"w-full rounded-xl bg-card border border-border p-5 shadow-lg group/player transition-all duration-300 hover:border-primary/30",
className
)}>
<div className="flex flex-col space-y-4">
<div className="flex flex-col space-y-3">
{/* Waveform Container */}
<div className="relative h-[60px] w-full bg-muted/20 rounded-lg overflow-hidden flex items-center justify-center">
<div className="relative h-12 w-full bg-muted rounded-lg overflow-hidden flex items-center justify-center">
{isLoading && !allowPlayBeforeReady && (
<div className="absolute inset-0 z-10 flex items-center justify-center bg-card/60 backdrop-blur-[1px]">
<IconLoader2 className="h-6 w-6 animate-spin text-cow-purple" />
<span className="ml-2 text-xs font-jetbrains-mono tracking-wider text-muted-foreground">LOADING WAVEFORM...</span>
<div className="absolute inset-0 z-10 flex items-center justify-center bg-muted">
<IconLoader2 className="h-6 w-6 animate-spin text-primary" />
<span className="ml-2 text-xs tracking-wider text-muted-foreground">LOADING WAVEFORM...</span>
</div>
)}
{isLoading && allowPlayBeforeReady && (
<div className="absolute top-1 left-1/2 -translate-x-1/2 z-10 flex items-center gap-1.5">
<IconLoader2 className="h-3 w-3 animate-spin text-cow-purple" />
<span className="text-[10px] font-jetbrains-mono tracking-wider text-muted-foreground">Loading waveform...</span>
<IconLoader2 className="h-3 w-3 animate-spin text-primary" />
<span className="text-[10px] tracking-wider text-muted-foreground">Loading waveform...</span>
</div>
)}
<div ref={containerRef} className="w-full" />
{loadError && (
<div className="absolute inset-0 flex items-center justify-center gap-2 bg-card/90 text-sm text-muted-foreground">
<div className="absolute inset-0 flex items-center justify-center gap-2 bg-muted text-sm text-muted-foreground">
<span>Audio preview unavailable.</span>
<Button variant="outline" size="sm" onClick={() => setRetryKey((key) => key + 1)}>Retry</Button>
</div>
)}
</div>

{/* Controls and Info */}
<div className="flex items-center justify-between gap-4">
<div className="flex items-center gap-2">
<div className="flex flex-col items-center gap-2">
<div className="flex items-center justify-center gap-3">
<Button
variant="ghost"
size="icon"
className="h-8 w-8 text-muted-foreground hover:text-primary transition-colors"
className="h-7 w-7 text-muted-foreground hover:text-primary transition-colors"
onClick={skipBackward}
disabled={!isReady && !allowPlayBeforeReady}
>
<IconPlayerSkipBack size={18} fill="currentColor" className="opacity-70" />
<IconPlayerSkipBack size={16} fill="currentColor" className="opacity-70" />
</Button>

<Button
size="icon"
className="h-12 w-12 rounded-full bg-cow-purple hover:bg-cow-purple-dark text-white shadow-md hover:scale-105 transition-all duration-200"
className="h-11 w-11 chamfer bg-primary hover:bg-primary/90 text-primary-foreground shadow-md hover:scale-105 transition-all duration-200"
onClick={togglePlay}
disabled={!isReady && !allowPlayBeforeReady}
>
{isPlaying ? (
<IconPlayerPause size={24} fill="currentColor" />
<IconPlayerPause size={22} fill="currentColor" />
) : (
<IconPlayerPlay size={24} fill="currentColor" className="ml-1" />
<IconPlayerPlay size={22} fill="currentColor" className="translate-x-[1px]" />
)}
</Button>

<Button
variant="ghost"
size="icon"
className="h-8 w-8 text-muted-foreground hover:text-primary transition-colors"
className="h-7 w-7 text-muted-foreground hover:text-primary transition-colors"
onClick={skipForward}
disabled={!isReady && !allowPlayBeforeReady}
>
<IconPlayerSkipForward size={18} fill="currentColor" className="opacity-70" />
<IconPlayerSkipForward size={16} fill="currentColor" className="opacity-70" />
</Button>
</div>

<div className="flex flex-col items-center flex-grow">
<div className="flex items-center gap-1.5 font-geist-mono text-[13px] font-medium tracking-tight text-muted-foreground">
<span className="text-foreground">{formatTime(currentTime)}</span>
<span className="opacity-30">/</span>
<span>{formatTime(duration)}</span>
</div>
<div className="flex items-center gap-1.5 font-geist-mono text-xs font-medium tracking-tight text-muted-foreground">
<span className="text-foreground">{formatTime(currentTime)}</span>
<span className="opacity-30">/</span>
<span>{formatTime(duration)}</span>
</div>
</div>
</div>
Expand Down
12 changes: 8 additions & 4 deletions src/components/DonateButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,16 +40,20 @@ const DonateButton = () => {
onMouseEnter={() => setIsHovered(true)}
onMouseLeave={() => setIsHovered(false)}
initial={false}
animate={{ opacity: isNearBottom ? 0 : 1 }}
transition={{ duration: 0.3, ease: "easeOut" }}
animate={
isNearBottom
? { opacity: 0, scale: 0.5, y: 16 }
: { opacity: 1, scale: 1, y: 0 }
}
transition={{ type: "spring", stiffness: 420, damping: 28 }}
style={{ pointerEvents: isNearBottom ? "none" : "auto" }}
aria-hidden={isNearBottom}
>
<motion.a
href="https://www.buymeacoffee.com/renderdragon"
target="_blank"
rel="noopener noreferrer"
className="flex items-center bg-cow-purple rounded-full h-12 shadow-lg border-2 border-black/20 overflow-hidden cursor-pointer"
className="flex items-center bg-primary chamfer h-12 shadow-lg border-2 border-black/20 overflow-hidden cursor-pointer"
initial={false}
animate={{
width: isHovered ? "200px" : "64px",
Expand Down Expand Up @@ -79,7 +83,7 @@ const DonateButton = () => {
className="flex items-center pr-6 whitespace-nowrap"
>
<div className="h-5 w-[1px] bg-white/20 mr-4" />
<span className="text-white text-sm font-bold uppercase tracking-[0.2em] font-jetbrains-mono">
<span className="text-white text-sm font-bold uppercase tracking-[0.2em] ">
Donate 🍕
</span>
</motion.div>
Expand Down
2 changes: 1 addition & 1 deletion src/components/ErrorBoundary.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ class ErrorBoundary extends Component<Props, State> {
<h1 className="text-2xl font-bold mb-4">Something went wrong.</h1>
<p className="mb-4">We're sorry for the inconvenience. Please try refreshing the page.</p>
<button
className="bg-cow-purple text-white px-4 py-2 rounded hover:bg-cow-purple-dark"
className="bg-primary text-white px-4 py-2 rounded hover:bg-primary/90"
onClick={() => this.setState({ hasError: false })}
>
Try again
Expand Down
14 changes: 6 additions & 8 deletions src/components/FeaturedResources.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ const FeaturedResources = () => {
};

return (
<section className="py-20 md:py-28 bg-background border-y-4 border-cow-purple cow-grid-bg">
<section className="py-20 md:py-28 bg-background border-y border-border">
<div className="container mx-auto px-4">
<motion.div
className="text-center mb-14 md:mb-16 max-w-3xl mx-auto"
Expand All @@ -88,12 +88,10 @@ const FeaturedResources = () => {
viewport={{ once: true }}
transition={{ duration: 0.5 }}
>
<h2
className="font-minecraftia text-3xl sm:text-4xl md:text-5xl lg:text-6xl mb-6 text-foreground leading-tight"
>
Featured <span className="text-cow-purple">Resources</span>
<h2 className="text-3xl sm:text-4xl md:text-5xl font-bold tracking-tight mb-4 text-foreground">
Featured <span className="text-primary">Resources</span>
</h2>
<p className="font-jetbrains-mono text-xl md:text-2xl text-foreground/70 leading-tight">
<p className="text-lg md:text-xl text-muted-foreground">
A taste of what's inside. Browse the full library anytime.
</p>
</motion.div>
Expand Down Expand Up @@ -134,9 +132,9 @@ const FeaturedResources = () => {
>
<Link
to="/resources"
className="pixel-btn-secondary inline-flex items-center space-x-2 group text-base md:text-lg"
className="group inline-flex items-center gap-2 chamfer border border-border px-5 py-2.5 text-base font-medium text-foreground transition-colors hover:bg-accent"
>
<span>View All Resources</span>
<span>View all resources</span>
<IconArrowRight className="w-5 h-5 group-hover:translate-x-1 transition-transform" />
</Link>
</motion.div>
Expand Down
12 changes: 6 additions & 6 deletions src/components/FinalCTA.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,21 +15,21 @@ const FinalCTA = () => {
transition={{ duration: 0.6, ease: [0.16, 1, 0.3, 1] }}
className="relative container mx-auto px-4"
>
<div className="max-w-4xl mx-auto bg-cow-dark border-2 border-cow-purple pixel-corners p-10 md:p-16 text-center">
<h2 className="font-minecraftia text-3xl sm:text-4xl md:text-5xl lg:text-6xl mb-6 text-foreground leading-tight">
<div className="max-w-4xl mx-auto bg-background border-2 border-primary p-10 md:p-16 text-center">
<h2 className="text-3xl sm:text-4xl md:text-5xl lg:text-6xl mb-6 text-foreground leading-tight">
Your next video
<br />
<span className="text-cow-purple">starts here.</span>
<span className="text-primary">starts here.</span>
</h2>

<p className="font-jetbrains-mono text-xl md:text-2xl text-foreground/80 mb-10 max-w-2xl mx-auto leading-tight">
<p className="text-xl md:text-2xl text-foreground/80 mb-10 max-w-2xl mx-auto leading-tight">
Browse the hub, run a tool, or hang out with the community. No strings, no waitlist, no watermarks.
</p>

<div className="flex flex-col sm:flex-row gap-4 justify-center items-center">
<Link
to="/resources"
className="pixel-btn-primary group flex items-center gap-2 text-base md:text-lg"
className="inline-flex items-center justify-center chamfer bg-primary px-4 py-2 text-sm font-medium text-primary-foreground transition-colors hover:bg-primary/90 group flex items-center gap-2 text-base md:text-lg"
>
<span>Browse Resources</span>
<IconArrowRight className="w-5 h-5 group-hover:translate-x-1 transition-transform" />
Expand All @@ -38,7 +38,7 @@ const FinalCTA = () => {
href="https://discord.renderdragon.org"
target="_blank"
rel="noopener noreferrer"
className="pixel-btn-secondary group flex items-center gap-2 text-base md:text-lg"
className="inline-flex items-center justify-center chamfer border border-border px-4 py-2 text-sm font-medium text-foreground transition-colors hover:bg-accent group flex items-center gap-2 text-base md:text-lg"
>
<IconBrandDiscord className="w-5 h-5" />
<span>Join Discord</span>
Expand Down
Loading