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

View file

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

View file

@ -6,7 +6,7 @@ import { motion } from "framer-motion";
export const AboutMe = () => {
return (
<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 }}
animate={{ opacity: 1 }}
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 (
<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 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5, ease: "easeInOut" }}

View file

@ -30,7 +30,7 @@ export const ThemeSwitcher = () => {
<motion.button
aria-label="Switch Theme"
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}
initial={{ opacity: 0, scale: 0.8 }}
animate={{ opacity: 1, scale: 1 }}

View file

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

View file

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

View file

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

View file

@ -1,20 +1,52 @@
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 = {
content: [
"./src/pages/**/*.{js,ts,jsx,tsx,mdx}",
"./src/components/**/*.{js,ts,jsx,tsx,mdx}",
"./src/app/**/*.{js,ts,jsx,tsx,mdx}",
],
theme: {
extend: {
colors: {
background: "var(--background)",
foreground: "var(--foreground)",
},
},
},
darkMode: "class",
plugins: [],
content: [
"./src/pages/**/*.{js,ts,jsx,tsx,mdx}",
"./src/components/**/*.{js,ts,jsx,tsx,mdx}",
"./src/app/**/*.{js,ts,jsx,tsx,mdx}",
"./src/**/*.{ts,tsx}",
],
theme: {
extend: {
colors: {
background: "var(--background)",
foreground: "var(--foreground)",
},
},
},
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;

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