No changes after this point.

This commit is contained in:
skidoodle 2024-12-19 21:30:05 +01:00
parent e70d71134f
commit 4e2956becb
Signed by: albert
GPG key ID: A06E3070D7D55BF2
15 changed files with 1138 additions and 956 deletions

View file

@ -1,67 +1,67 @@
import type { NextConfig } from 'next' import type { NextConfig } from "next";
const securityHeaders = [ const securityHeaders = [
{ {
key: 'X-DNS-Prefetch-Control', key: "X-DNS-Prefetch-Control",
value: 'on', value: "on",
}, },
{ {
key: 'X-XSS-Protection', key: "X-XSS-Protection",
value: '1; mode=block', value: "1; mode=block",
}, },
{ {
key: 'X-Content-Type-Options', key: "X-Content-Type-Options",
value: 'nosniff', value: "nosniff",
}, },
{ {
key: 'Referrer-Policy', key: "Referrer-Policy",
value: 'strict-origin', value: "strict-origin",
}, },
{ {
key: 'Content-Security-Policy', key: "Content-Security-Policy",
value: `frame-ancestors 'self';`, value: `frame-ancestors 'self';`,
}, },
{ {
key: 'X-Frame-Options', key: "X-Frame-Options",
value: 'SAMEORIGIN', value: "SAMEORIGIN",
}, },
{ {
key: 'Strict-Transport-Security', key: "Strict-Transport-Security",
value: 'max-age=31536000; includeSubDomains; preload', value: "max-age=31536000; includeSubDomains; preload",
}, },
{ {
key: 'Permissions-Policy', key: "Permissions-Policy",
value: 'camera=(), microphone=(), geolocation=()', value: "camera=(), microphone=(), geolocation=()",
}, },
{ {
key: 'X-Source', key: "X-Source",
value: 'github.com/skidoodle/albert.lol', value: "github.com/skidoodle/albert.lol",
}, },
] ];
const nextConfig: NextConfig = { const nextConfig: NextConfig = {
async headers() { async headers() {
return [ return [
{ {
source: '/:path*', source: "/:path*",
headers: securityHeaders, headers: securityHeaders,
}, },
] ];
}, },
images: { images: {
remotePatterns: [ remotePatterns: [
{ {
protocol: 'https', protocol: "https",
hostname: 'i.scdn.co', hostname: "i.scdn.co",
}, },
{ {
protocol: 'https', protocol: "https",
hostname: 'placehold.co', hostname: "placehold.co",
}, },
], ],
}, },
reactStrictMode: true, reactStrictMode: true,
output: 'standalone', output: "standalone",
} };
export default nextConfig export default nextConfig;

View file

@ -1,33 +1,36 @@
{ {
"name": "albert.lol", "name": "albert.lol",
"version": "0.1.0", "version": "0.1.0",
"private": true, "private": true,
"scripts": { "scripts": {
"dev": "next dev --turbopack", "dev": "next dev --turbopack",
"build": "next build", "build": "next build",
"start": "next start", "start": "next start",
"lint": "biome lint --write src/", "lint": "biome lint --write src/",
"format": "biome format --write src/" "format": "biome format --write src/"
}, },
"dependencies": { "dependencies": {
"copy-to-clipboard": "^3.3.3", "clsx": "^2.1.1",
"framer-motion": "^11.11.10", "copy-to-clipboard": "^3.3.3",
"next": "15.0.1", "framer-motion": "^11.15.0",
"next-themes": "^0.3.0", "mini-svg-data-uri": "^1.4.4",
"react": "19.0.0-rc-69d4b800-20241021", "next": "15.1.2",
"react-dom": "19.0.0-rc-69d4b800-20241021", "next-themes": "^0.4.4",
"react-hot-toast": "^2.4.1", "react": "19.0.0",
"react-icons": "^5.3.0" "react-dom": "19.0.0",
}, "react-hot-toast": "^2.4.1",
"devDependencies": { "react-icons": "^5.4.0",
"@biomejs/biome": "1.9.4", "tailwind-merge": "^2.5.5"
"@types/node": "^22.8.2", },
"@types/react": "^18.3.12", "devDependencies": {
"@types/react-dom": "^18.3.1", "@biomejs/biome": "1.9.4",
"eslint": "^9.13.0", "@types/node": "^22.10.2",
"eslint-config-next": "15.0.1", "@types/react": "^19.0.2",
"postcss": "^8.4.47", "@types/react-dom": "^19.0.2",
"tailwindcss": "^3.4.14", "eslint": "^9.17.0",
"typescript": "^5.6.3" "eslint-config-next": "15.1.2",
} "postcss": "^8.4.49",
"tailwindcss": "^3.4.17",
"typescript": "^5.7.2"
}
} }

1612
pnpm-lock.yaml generated

File diff suppressed because it is too large Load diff

View file

@ -1,8 +1,8 @@
/** @type {import('postcss-load-config').Config} */ /** @type {import('postcss-load-config').Config} */
const config = { const config = {
plugins: { plugins: {
tailwindcss: {}, tailwindcss: {},
}, },
}; };
export default config; export default config;

View file

@ -6,7 +6,7 @@ import { motion } from "framer-motion";
export const AboutMe = () => { export const AboutMe = () => {
return ( return (
<motion.div <motion.div
className="p-3 max-w-[325px] lg:max-w-lg md:max-w-md max-h-[300px] h-[265px] rounded-lg shadow-lg backdrop-blur-sm bg-white/20 dark:bg-black/20 object-fit" className="p-3 max-w-xs lg:max-w-sm md:max-w-md max-h-[300px] h-[265px] rounded-lg shadow-lg bg-white dark:bg-black"
initial={{ opacity: 0 }} initial={{ opacity: 0 }}
animate={{ opacity: 1 }} animate={{ opacity: 1 }}
whileHover={{ scale: 1.05 }} whileHover={{ scale: 1.05 }}

View file

@ -1,42 +0,0 @@
"use client";
import React from "react";
import { motion } from "framer-motion";
export const Background = () => {
return (
<div className="fixed inset-0 w-screen h-screen overflow-hidden z-[-1] flex justify-center items-center">
<motion.div
className="absolute w-[800px] h-[800px] bg-gradient-to-r from-pink-500 to-purple-500 rounded-full blur-3xl"
style={{ willChange: "transform, opacity" }}
animate={{
scale: [1.5, 1.2, 1, 1.2, 1.5],
x: [0, 200, 0, -200, 0],
y: [0, -80, 150, -150, 0],
opacity: [0.6, 1, 0.6],
}}
transition={{
duration: 18,
repeat: Number.POSITIVE_INFINITY,
ease: "easeInOut",
}}
/>
<motion.div
className="absolute w-[800px] h-[800px] bg-gradient-to-r from-blue-400 to-red-500 rounded-full blur-3xl"
style={{ willChange: "transform, opacity" }}
animate={{
scale: [1, 1.3, 1.5, 1.3, 1],
x: [-150, 150, -150, 150, -150],
y: [180, -180, 180, -180, 180],
opacity: [0.6, 1, 0.6],
}}
transition={{
duration: 22,
repeat: Number.POSITIVE_INFINITY,
ease: "easeInOut",
}}
/>
</div>
);
};

View file

@ -0,0 +1,53 @@
"use client";
import { cn } from "@/utils";
import { useMotionValue, motion, useMotionTemplate } from "framer-motion";
export const HeroHighlight = ({
children,
className,
containerClassName,
}: {
children: React.ReactNode;
className?: string;
containerClassName?: string;
}) => {
const mouseX = useMotionValue(0);
const mouseY = useMotionValue(0);
function handleMouseMove({
currentTarget,
clientX,
clientY,
}: React.MouseEvent<HTMLDivElement>) {
if (!currentTarget) return;
const { left, top } = currentTarget.getBoundingClientRect();
mouseX.set(clientX - left);
mouseY.set(clientY - top);
}
return (
<div
className={cn(
"relative h-screen flex items-center bg-white dark:bg-black justify-center w-full group",
containerClassName,
)}
onMouseMove={handleMouseMove}
>
<div className="absolute inset-0 bg-dot-thick-neutral-300 dark:bg-dot-thick-neutral-800 pointer-events-none" />
<motion.div
className="pointer-events-none bg-dot-thick-indigo-500 dark:bg-dot-thick-indigo-500 absolute inset-0 opacity-0 transition duration-300 group-hover:opacity-100"
style={{
WebkitMaskImage: useMotionTemplate`
radial-gradient(200px circle at ${mouseX}px ${mouseY}px, black 0%, transparent 100%)
`,
maskImage: useMotionTemplate`
radial-gradient(200px circle at ${mouseX}px ${mouseY}px, black 0%, transparent 100%)
`,
}}
/>
<div className={cn("relative z-20", className)}>{children}</div>
</div>
);
};

View file

@ -111,7 +111,7 @@ export const NowPlayingCard = () => {
return ( return (
<motion.div <motion.div
className="mt-5 w-[325px] rounded-md shadow-lg p-3 dark:bg-black/20 bg-white/20 backfrop-blur-sm" className="mt-5 w-[325px] rounded-md shadow-lg p-3 dark:bg-black bg-white"
initial={{ opacity: 0, y: 20 }} initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }} animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5, ease: "easeInOut" }} transition={{ duration: 0.5, ease: "easeInOut" }}

View file

@ -30,7 +30,7 @@ export const ThemeSwitcher = () => {
<motion.button <motion.button
aria-label="Switch Theme" aria-label="Switch Theme"
type="button" type="button"
className="fixed top-5 right-5 p-3 rounded-full" className="fixed top-5 right-5 p-3 rounded-full z-50"
onClick={toggleTheme} onClick={toggleTheme}
initial={{ opacity: 0, scale: 0.8 }} initial={{ opacity: 0, scale: 0.8 }}
animate={{ opacity: 1, scale: 1 }} animate={{ opacity: 1, scale: 1 }}

View file

@ -3,8 +3,8 @@
@tailwind utilities; @tailwind utilities;
:root { :root {
--dark-background: #000000; --dark-background: #282828;
--dark-primary: #121212; --dark-primary: #323232;
--dark-secondary: #cecece; --dark-secondary: #cecece;
--dark-text: #eeeeee; --dark-text: #eeeeee;

View file

@ -6,43 +6,44 @@ import { SocialLayout } from "@/components/SocialLayout";
import { AboutMe } from "@/components/AboutMe"; import { AboutMe } from "@/components/AboutMe";
import { Toaster } from "react-hot-toast"; import { Toaster } from "react-hot-toast";
import { motion } from "framer-motion"; import { motion } from "framer-motion";
import { Background } from "@/components/Background"; import { HeroHighlight } from "@/components/Highlight";
import { Fragment } from "react"; import { Fragment } from "react";
export default function Home() { export default function Home() {
return ( return (
<Fragment> <Fragment>
<Background /> <HeroHighlight containerClassName="min-h-screen flex items-start">
<ThemeSwitcher /> <ThemeSwitcher />
<Toaster position="top-left" /> <Toaster position="top-left" />
<motion.div <motion.div
initial={{ opacity: 0, y: 20 }} initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }} animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5 }} transition={{ duration: 0.5 }}
> >
<div className="mx-auto mt-44 flex max-w-3xl flex-col mb-12 z-10 relative"> <div className="flex max-w-3xl flex-col z-10 relative mt-52">
<motion.div
className="flex flex-col lg:flex-row items-center justify-center lg:justify-between space-y-10 lg:space-y-5 lg:space-x-10"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ duration: 0.5 }}
>
<motion.div <motion.div
className="flex flex-col items-center text-center" className="flex flex-col lg:flex-row items-center justify-center lg:justify-between space-y-10 lg:space-y-5 lg:space-x-10"
initial={{ scale: 0.9 }} initial={{ opacity: 0 }}
animate={{ scale: 1 }} animate={{ opacity: 1 }}
transition={{ duration: 0.5 }} transition={{ duration: 0.5 }}
> >
<h1 className="text-[7.5rem] leading-none font-bold dark:text-[--dark-text] text-[--light-text]"> <motion.div
albert className="flex flex-col items-center text-center"
</h1> initial={{ scale: 0.9 }}
<SocialLayout /> animate={{ scale: 1 }}
<NowPlayingCard /> transition={{ duration: 0.5 }}
>
<h1 className="text-[7.5rem] leading-none font-bold dark:text-[--dark-text] text-[--light-text]">
albert
</h1>
<SocialLayout />
<NowPlayingCard />
</motion.div>
<AboutMe />
</motion.div> </motion.div>
<AboutMe /> </div>
</motion.div> </motion.div>
</div> </HeroHighlight>
</motion.div>
</Fragment> </Fragment>
); );
} }

View file

@ -1,3 +1,6 @@
import { type ClassValue, clsx } from "clsx";
import { twMerge } from "tailwind-merge";
export default function age() { export default function age() {
const BIRTHDATE = process.env.NEXT_PUBLIC_BIRTHDATE; const BIRTHDATE = process.env.NEXT_PUBLIC_BIRTHDATE;
if (!BIRTHDATE) { if (!BIRTHDATE) {
@ -17,3 +20,7 @@ export const truncate = (str: string, n: number): string => {
} }
return str.trim(); return str.trim();
}; };
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}

View file

@ -1,20 +1,52 @@
import type { Config } from "tailwindcss"; import type { Config } from "tailwindcss";
import svgToDataUri from "mini-svg-data-uri";
import flattenColorPalette from "tailwindcss/lib/util/flattenColorPalette";
const addVariablesForColors = ({ addBase, theme }: any) => {
const allColors = flattenColorPalette(theme("colors"));
const newVars = Object.fromEntries(
Object.entries(allColors).map(([key, val]) => [`--${key}`, val]),
);
addBase({
":root": newVars,
});
};
const config: Config = { const config: Config = {
content: [ content: [
"./src/pages/**/*.{js,ts,jsx,tsx,mdx}", "./src/pages/**/*.{js,ts,jsx,tsx,mdx}",
"./src/components/**/*.{js,ts,jsx,tsx,mdx}", "./src/components/**/*.{js,ts,jsx,tsx,mdx}",
"./src/app/**/*.{js,ts,jsx,tsx,mdx}", "./src/app/**/*.{js,ts,jsx,tsx,mdx}",
], "./src/**/*.{ts,tsx}",
theme: { ],
extend: { theme: {
colors: { extend: {
background: "var(--background)", colors: {
foreground: "var(--foreground)", background: "var(--background)",
}, foreground: "var(--foreground)",
}, },
}, },
darkMode: "class", },
plugins: [], darkMode: "class",
plugins: [
addVariablesForColors,
function ({ matchUtilities, theme }: any) {
matchUtilities(
{
"bg-dot-thick": (value: any) => ({
backgroundImage: `url("${svgToDataUri(
`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" width="16" height="16" fill="none"><circle fill="${value}" id="pattern-circle" cx="10" cy="10" r="2.5"></circle></svg>`,
)}")`,
}),
},
{
values: flattenColorPalette(theme("backgroundColor")),
type: "color",
},
);
},
],
}; };
export default config; export default config;

6
tailwindcss.d.ts vendored Normal file
View file

@ -0,0 +1,6 @@
declare module "tailwindcss/lib/util/flattenColorPalette" {
const flattenColorPalette: (
colors: Record<string, any>,
) => Record<string, string>;
export default flattenColorPalette;
}

View file

@ -1,27 +1,27 @@
{ {
"compilerOptions": { "compilerOptions": {
"target": "ES2017", "target": "ES2017",
"lib": ["dom", "dom.iterable", "esnext"], "lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true, "allowJs": true,
"skipLibCheck": true, "skipLibCheck": true,
"strict": true, "strict": true,
"noEmit": true, "noEmit": true,
"esModuleInterop": true, "esModuleInterop": true,
"module": "esnext", "module": "esnext",
"moduleResolution": "bundler", "moduleResolution": "bundler",
"resolveJsonModule": true, "resolveJsonModule": true,
"isolatedModules": true, "isolatedModules": true,
"jsx": "preserve", "jsx": "preserve",
"incremental": true, "incremental": true,
"plugins": [ "plugins": [
{ {
"name": "next" "name": "next"
} }
], ],
"paths": { "paths": {
"@/*": ["./src/*", "./src/app/*"] "@/*": ["./src/*", "./src/app/*"]
} }
}, },
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"exclude": ["node_modules"] "exclude": ["node_modules"]
} }