feat: add brand logo and animated background

This commit is contained in:
MrFrostDev
2026-05-23 13:58:27 +02:00
parent 9d8cf47d2a
commit df8d3940c5
3 changed files with 233 additions and 0 deletions
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

+145
View File
@@ -0,0 +1,145 @@
'use client';
import { Box } from '@mui/material';
import { useTheme } from '@mui/material/styles';
interface AnimatedBackgroundProps {
/** Intensity multiplier (0.5 = subtle, 1 = default, 1.5 = bold) */
intensity?: number;
}
export default function AnimatedBackground({ intensity = 1 }: AnimatedBackgroundProps) {
const theme = useTheme();
const isDark = theme.palette.mode === 'dark';
const base = isDark ? 0.25 : 0.4;
return (
<Box
aria-hidden
sx={{
position: 'fixed',
inset: 0,
zIndex: 0,
pointerEvents: 'none',
overflow: 'hidden',
}}
>
<Orb
color={isDark ? 'rgba(255,107,0,0.6)' : 'rgba(255,107,0,0.55)'}
size={620}
opacity={base * intensity}
delay={0}
duration={28}
x={['-15%', '5%', '-10%']}
y={['-10%', '15%', '0%']}
scale={[1, 1.15, 1]}
/>
<Orb
color={isDark ? 'rgba(45,90,39,0.55)' : 'rgba(45,90,39,0.45)'}
size={520}
opacity={base * intensity * 0.9}
delay={4}
duration={34}
x={['70%', '50%', '75%']}
y={['10%', '30%', '5%']}
scale={[1, 1.1, 1]}
/>
<Orb
color={isDark ? 'rgba(59,130,246,0.35)' : 'rgba(59,130,246,0.3)'}
size={480}
opacity={base * intensity * 0.7}
delay={8}
duration={40}
x={['20%', '40%', '15%']}
y={['65%', '50%', '70%']}
scale={[1, 1.08, 1]}
/>
<Orb
color={isDark ? 'rgba(245,158,11,0.3)' : 'rgba(245,158,11,0.28)'}
size={420}
opacity={base * intensity * 0.6}
delay={2}
duration={36}
x={['60%', '80%', '55%']}
y={['70%', '55%', '80%']}
scale={[1, 1.12, 1]}
/>
{/* Subtle grid pattern */}
<Box
sx={{
position: 'absolute',
inset: 0,
opacity: isDark ? 0.18 : 0.35,
backgroundImage:
isDark
? 'radial-gradient(circle at 1px 1px, rgba(255,255,255,0.05) 1px, transparent 0)'
: 'radial-gradient(circle at 1px 1px, rgba(15,23,42,0.06) 1px, transparent 0)',
backgroundSize: '28px 28px',
maskImage:
'linear-gradient(180deg, transparent 0%, rgba(0,0,0,0.85) 25%, rgba(0,0,0,0.85) 75%, transparent 100%)',
WebkitMaskImage:
'linear-gradient(180deg, transparent 0%, rgba(0,0,0,0.85) 25%, rgba(0,0,0,0.85) 75%, transparent 100%)',
}}
/>
{/* Vignette so foreground content reads clearly */}
<Box
sx={{
position: 'absolute',
inset: 0,
background: isDark
? 'radial-gradient(circle at 50% 30%, transparent 0%, rgba(11,15,26,0.4) 80%)'
: 'radial-gradient(circle at 50% 30%, transparent 0%, rgba(249,250,251,0.45) 80%)',
}}
/>
</Box>
);
}
interface OrbProps {
color: string;
size: number;
opacity: number;
delay: number;
duration: number;
x: string[];
y: string[];
scale: number[];
}
function Orb({ color, size, opacity, delay, duration, x, y, scale }: OrbProps) {
// Build keyframes inline as a CSS custom animation
const animName = `pp-orb-${Math.abs(
delay * 31 + duration * 7 + size
)}`;
const keyframes = `
@keyframes ${animName} {
0% { transform: translate3d(${x[0]}, ${y[0]}, 0) scale(${scale[0]}); }
50% { transform: translate3d(${x[1]}, ${y[1]}, 0) scale(${scale[1]}); }
100% { transform: translate3d(${x[2]}, ${y[2]}, 0) scale(${scale[2]}); }
}
`;
return (
<>
<style>{keyframes}</style>
<Box
sx={{
position: 'absolute',
width: size,
height: size,
left: 0,
top: 0,
borderRadius: '50%',
background: `radial-gradient(circle, ${color} 0%, transparent 65%)`,
filter: 'blur(80px)',
opacity,
willChange: 'transform',
animation: `${animName} ${duration}s ease-in-out ${delay}s infinite alternate`,
}}
/>
</>
);
}
+88
View File
@@ -0,0 +1,88 @@
'use client';
import Image from 'next/image';
import { Box } from '@mui/material';
interface LogoProps {
/** Total box size in pixels. */
size?: number;
/** Show the rounded white container backplate (for use over coloured surfaces). */
withBackplate?: boolean;
/** Padding inside the backplate (if any). */
padding?: number;
/** Optional border-radius override (defaults to 2 = 8px). */
rounded?: number;
/** className for parent positioning. */
className?: string;
/** Click handler. */
onClick?: () => void;
}
export default function Logo({
size = 36,
withBackplate = false,
padding,
rounded,
className,
onClick,
}: LogoProps) {
const inner = (
<Image
src="/logo.png"
alt="PatchPilot Logo"
width={size}
height={size}
priority
style={{
width: '100%',
height: '100%',
objectFit: 'contain',
display: 'block',
}}
/>
);
if (!withBackplate) {
return (
<Box
className={className}
onClick={onClick}
sx={{
width: size,
height: size,
flexShrink: 0,
display: 'inline-flex',
alignItems: 'center',
justifyContent: 'center',
cursor: onClick ? 'pointer' : undefined,
}}
>
{inner}
</Box>
);
}
return (
<Box
className={className}
onClick={onClick}
sx={{
width: size,
height: size,
flexShrink: 0,
p: padding ?? 0.5,
borderRadius: rounded ?? 2,
display: 'inline-flex',
alignItems: 'center',
justifyContent: 'center',
bgcolor: 'background.paper',
border: '1px solid',
borderColor: 'divider',
boxShadow: 'var(--pp-shadow-soft)',
cursor: onClick ? 'pointer' : undefined,
}}
>
{inner}
</Box>
);
}