commit 6633b7858f621428cf4149e448b4508d5d9143eb Author: m4x809 Date: Sat Oct 25 00:20:37 2025 +0200 first version diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..70f8910 --- /dev/null +++ b/.gitignore @@ -0,0 +1,40 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js +.yarn/install-state.gz + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# local env files +.env*.local + +# vercel +.vercel + +# typescript +*.tsbuildinfo +next-env.d.ts + +# bun +*.lock +.log diff --git a/README.md b/README.md new file mode 100644 index 0000000..6a0ecdd --- /dev/null +++ b/README.md @@ -0,0 +1,93 @@ +# Linkin Park Albums + +A modern web application showcasing the complete discography of Linkin Park, built with Next.js 15 and React 19. + +## Features + +- 🎵 Browse all Linkin Park albums +- 📱 Responsive design with Tailwind CSS +- 🎨 Beautiful UI with Mantine components +- ⚡ Fast static generation with Next.js App Router +- 🖥️ Server components for optimal performance +- 🎭 Smooth transitions and hover effects + +## Tech Stack + +- **Framework**: Next.js 15 (App Router) +- **React**: React 19 (Canary) +- **UI Components**: Mantine Core 8.3.5 +- **Styling**: Tailwind CSS 4.1.15 +- **Package Manager**: Bun +- **TypeScript**: 5.9.3 + +## Project Structure + +``` +src/ +├── app/ +│ ├── layout.tsx # Root layout with Mantine provider +│ ├── page.tsx # Home page (server component) +│ └── album/ +│ └── [albumId]/ +│ ├── page.tsx # Album detail page (server component) +│ └── not-found.tsx +├── components/ +│ ├── AlbumCard.tsx # Album card component (client component) +│ └── BackButton.tsx # Back button (client component) +├── lib/ +│ ├── list.ts # Album data +│ └── ListTypes.ts # TypeScript types +└── index.css # Global styles +``` + +## Getting Started + +Install dependencies: + +```bash +bun install +``` + +Run the development server: + +```bash +bun run dev +``` + +Open [http://localhost:3000](http://localhost:3000) to view the app. + +## Build + +Build for production: + +```bash +bun run build +``` + +Start the production server: + +```bash +bun run start +``` + +## Features Explanation + +### Server Components + +The app leverages Next.js App Router with server components for optimal performance: +- Home page (`page.tsx`) - Server component that renders the album grid +- Album detail page (`album/[albumId]/page.tsx`) - Server component with static generation + +### Client Components + +Interactive components use the `"use client"` directive: +- `AlbumCard` - Handles click navigation to album details +- `BackButton` - Handles navigation back to home + +### Static Generation + +Album detail pages are statically generated at build time using `generateStaticParams`, providing instant page loads. + +## License + +MIT diff --git a/VIEW_TRANSITIONS_GUIDE.md b/VIEW_TRANSITIONS_GUIDE.md new file mode 100644 index 0000000..a16d097 --- /dev/null +++ b/VIEW_TRANSITIONS_GUIDE.md @@ -0,0 +1,100 @@ +# View Transitions Implementation Guide + +## Overview + +This project now uses the native **View Transitions API** to create smooth animations between pages, including browser back/forward navigation. The implementation is based on best practices from the [nmn.sh](https://github.com/nmn/nmn.sh) repository. + +## What Changed + +### 1. CSS Configuration (`src/index.css`) + +Added comprehensive view transition CSS rules: + +```css +/* Enable smooth cross-document view transitions (browser navigation) */ +@view-transition { + navigation: auto; +} + +/* Default transition for all elements */ +::view-transition-old(root), +::view-transition-new(root) { + animation-duration: 0.3s; + animation-timing-function: ease-in-out; +} +``` + +**Key Feature**: `@view-transition { navigation: auto; }` enables transitions to work with native browser navigation (back/forward buttons, URL changes), not just client-side routing. + +### 2. Component Updates + +**AlbumCard.tsx** and **album/[albumId]/page.tsx**: + +Changed from React `ViewTransition` component wrapper: +```tsx +// ❌ Old approach (only works with client-side routing) + + {label} + +``` + +To native CSS `viewTransitionName` property: +```tsx +// ✅ New approach (works with all navigation) +{label} +``` + +### 3. TypeScript Support + +Added type definitions in `src/types/view-transitions.d.ts` to extend React's `CSSProperties` interface with `viewTransitionName` support. + +## How It Works + +### Client-Side Routing +- Uses `next-view-transitions` package +- `` component from the package automatically triggers view transitions +- Smooth animations between pages when clicking links + +### Browser Navigation (Back/Forward) +- Native `@view-transition { navigation: auto; }` CSS rule +- Browser automatically captures before/after states +- Smooth transitions even with browser buttons or URL changes +- **No JavaScript required** for basic transitions + +### Named Transitions +Elements with matching `viewTransitionName` values will morph smoothly between pages: +- Album images: `album-card-image-{id}` +- Album titles: `album-card-title-{id}` +- Release dates: `album-card-release-date-{id}` + +## Browser Support + +- **Chrome/Edge**: Full support (v111+) +- **Safari**: Support in v18+ (macOS Sonoma, iOS 17) +- **Firefox**: In development +- **Fallback**: Graceful degradation to instant navigation + +## Testing + +1. **Client-side navigation**: Click any album card → smooth transition +2. **Browser back button**: Press back → smooth transition +3. **Direct URL change**: Type URL directly → smooth transition on supported browsers + +## Benefits Over Previous Implementation + +1. ✅ **Works with browser navigation** (back/forward buttons) +2. ✅ **Works with direct URL changes** +3. ✅ **Better performance** (native browser API) +4. ✅ **More reliable** (less JavaScript, more standards-based) +5. ✅ **Progressive enhancement** (works without JS) + +## References + +- [View Transitions API (MDN)](https://developer.mozilla.org/en-US/docs/Web/API/View_Transitions_API) +- [nmn.sh repository](https://github.com/nmn/nmn.sh) +- [Chrome View Transitions Guide](https://developer.chrome.com/docs/web-platform/view-transitions/) + diff --git a/biome.json b/biome.json new file mode 100644 index 0000000..23eddd7 --- /dev/null +++ b/biome.json @@ -0,0 +1,37 @@ +{ + "$schema": "https://biomejs.dev/schemas/2.2.4/schema.json", + "assist": { "actions": { "source": { "organizeImports": "off" } } }, + "formatter": { + "enabled": false + }, + "linter": { + "enabled": true, + "rules": { + "recommended": true, + "suspicious": { + "noExplicitAny": "off" + }, + "style": { + "noNonNullAssertion": "off", + "useImportType": "warn", + "noParameterAssign": "off" + }, + "a11y": { + "useKeyWithClickEvents": "off", + "useMediaCaption": "off", + "noSvgWithoutTitle": "off" + } + }, + "includes": [ + "**", + "!**/node_modules/**/*", + "!**/biome.json", + "!**/build/**/*", + "!**/.next/**/*", + "!**/drizzle/**/*", + "!**/*dockerignore*", + "!**/*.css", + "!**/*.log" + ] + } +} diff --git a/eslint.config.js b/eslint.config.js new file mode 100644 index 0000000..b19330b --- /dev/null +++ b/eslint.config.js @@ -0,0 +1,23 @@ +import js from '@eslint/js' +import globals from 'globals' +import reactHooks from 'eslint-plugin-react-hooks' +import reactRefresh from 'eslint-plugin-react-refresh' +import tseslint from 'typescript-eslint' +import { defineConfig, globalIgnores } from 'eslint/config' + +export default defineConfig([ + globalIgnores(['dist']), + { + files: ['**/*.{ts,tsx}'], + extends: [ + js.configs.recommended, + tseslint.configs.recommended, + reactHooks.configs['recommended-latest'], + reactRefresh.configs.vite, + ], + languageOptions: { + ecmaVersion: 2020, + globals: globals.browser, + }, + }, +]) diff --git a/next.config.ts b/next.config.ts new file mode 100644 index 0000000..788b9c6 --- /dev/null +++ b/next.config.ts @@ -0,0 +1,9 @@ +/** @type {import('next').NextConfig} */ +const nextConfig = { + experimental: { + viewTransition: true, + }, + reactCompiler: true, +}; + +module.exports = nextConfig; diff --git a/package.json b/package.json new file mode 100644 index 0000000..bac7171 --- /dev/null +++ b/package.json @@ -0,0 +1,41 @@ +{ + "name": "list-of-lp", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start", + "lint": "eslint ." + }, + "dependencies": { + "@mantine/carousel": "^8.3.5", + "@mantine/core": "^8.3.5", + "@mantine/hooks": "^8.3.5", + "embla-carousel": "^8.5.2", + "embla-carousel-react": "^8.5.2", + "next": "^16.0.0", + "next-view-transitions": "^0.3.4", + "react": "^19.2.0", + "react-dom": "^19.2.0", + "tailwindcss": "^4.1.15" + }, + "devDependencies": { + "@eslint/js": "^9.36.0", + "@types/node": "^24.6.0", + "@types/react": "^19.2.2", + "@types/react-dom": "^19.2.2", + "babel-plugin-react-compiler": "^19.1.0-rc.3", + "eslint": "^9.36.0", + "eslint-plugin-react-hooks": "^5.2.0", + "globals": "^16.4.0", + "postcss": "^8.5.6", + "postcss-preset-mantine": "^1.18.0", + "postcss-simple-vars": "^7.0.1", + "prettier": "^3.6.2", + "prettier-plugin-tailwindcss": "^0.7.1", + "typescript": "~5.9.3", + "typescript-eslint": "^8.45.0" + } +} diff --git a/postcss.config.js b/postcss.config.js new file mode 100644 index 0000000..45e1ba6 --- /dev/null +++ b/postcss.config.js @@ -0,0 +1,14 @@ +export default { + plugins: { + "postcss-preset-mantine": {}, + "postcss-simple-vars": { + variables: { + "mantine-breakpoint-xs": "36em", + "mantine-breakpoint-sm": "48em", + "mantine-breakpoint-md": "62em", + "mantine-breakpoint-lg": "75em", + "mantine-breakpoint-xl": "88em", + }, + }, + }, +}; diff --git a/prettier.config.js b/prettier.config.js new file mode 100644 index 0000000..4b5cd4c --- /dev/null +++ b/prettier.config.js @@ -0,0 +1,10 @@ +/** @type {import('prettier').Config & import('prettier-plugin-tailwindcss').PluginOptions} */ +const config = { + plugins: ["prettier-plugin-tailwindcss"], + tabWidth: 1, + useTabs: true, + arrowParens: "always", + printWidth: 120, +}; + +export default config; diff --git a/public/a_thousand_suns.jpg b/public/a_thousand_suns.jpg new file mode 100644 index 0000000..20e6191 Binary files /dev/null and b/public/a_thousand_suns.jpg differ diff --git a/public/from_zero.jpg b/public/from_zero.jpg new file mode 100644 index 0000000..69bead5 Binary files /dev/null and b/public/from_zero.jpg differ diff --git a/public/from_zero_deluxe.jpg b/public/from_zero_deluxe.jpg new file mode 100644 index 0000000..7fbf76c Binary files /dev/null and b/public/from_zero_deluxe.jpg differ diff --git a/public/hybrid_theory.jpg b/public/hybrid_theory.jpg new file mode 100644 index 0000000..0fd014d Binary files /dev/null and b/public/hybrid_theory.jpg differ diff --git a/public/living_things.jpg b/public/living_things.jpg new file mode 100644 index 0000000..5a14fe9 Binary files /dev/null and b/public/living_things.jpg differ diff --git a/public/lost_demos.jpg b/public/lost_demos.jpg new file mode 100644 index 0000000..216b868 Binary files /dev/null and b/public/lost_demos.jpg differ diff --git a/public/meteora.jpg b/public/meteora.jpg new file mode 100644 index 0000000..88a6642 Binary files /dev/null and b/public/meteora.jpg differ diff --git a/public/minutes_to_midnight.jpg b/public/minutes_to_midnight.jpg new file mode 100644 index 0000000..c24d2a0 Binary files /dev/null and b/public/minutes_to_midnight.jpg differ diff --git a/public/one_more_light.jpg b/public/one_more_light.jpg new file mode 100644 index 0000000..54d8f86 Binary files /dev/null and b/public/one_more_light.jpg differ diff --git a/public/papercuts.jpg b/public/papercuts.jpg new file mode 100644 index 0000000..7f88c90 Binary files /dev/null and b/public/papercuts.jpg differ diff --git a/public/vite.svg b/public/vite.svg new file mode 100644 index 0000000..e7b8dfb --- /dev/null +++ b/public/vite.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/Components/AlbumCard.tsx b/src/Components/AlbumCard.tsx new file mode 100644 index 0000000..4c80ce3 --- /dev/null +++ b/src/Components/AlbumCard.tsx @@ -0,0 +1,82 @@ +"use client"; + +import { getThemeColors } from "@/lib/themes"; +import type { Album } from "../lib/ListTypes"; +import { Box, Card, Image, Text, Badge, Group } from "@mantine/core"; +import { Link } from "next-view-transitions"; + +export default function AlbumCard({ album }: { album: Album }) { + const { label, releaseDate, image, tracks } = album; + + type Songs = { + count: number; + emilyLiveSongs: number; + lpLiveSongs: number; + }; + + const songs: Songs = { + count: tracks.length, + emilyLiveSongs: tracks.filter((track) => track.emilyLiveUrl !== null).length, + lpLiveSongs: tracks.filter((track) => track.lpLiveUrl !== null).length, + }; + + const theme = getThemeColors(album.id); + + return ( + + + {label} + + + + + + {label} + + + + Released:{" "} + {new Date(releaseDate).toLocaleDateString("en-US", { + year: "numeric", + month: "long", + day: "numeric", + })} + + + + + {songs.count} Songs + + {songs.emilyLiveSongs > 0 && ( + + {songs.emilyLiveSongs} Emily Live + + )} + {songs.lpLiveSongs > 0 && ( + + {songs.lpLiveSongs} LP Live + + )} + + + + ); +} diff --git a/src/Components/BackButton.tsx b/src/Components/BackButton.tsx new file mode 100644 index 0000000..464d9d4 --- /dev/null +++ b/src/Components/BackButton.tsx @@ -0,0 +1,18 @@ +"use client"; + +import { Button } from "@mantine/core"; +import { Link } from "next-view-transitions"; + +export default function BackButton() { + return ( + + ); +} diff --git a/src/app/album/[albumId]/not-found.tsx b/src/app/album/[albumId]/not-found.tsx new file mode 100644 index 0000000..127c1a0 --- /dev/null +++ b/src/app/album/[albumId]/not-found.tsx @@ -0,0 +1,24 @@ +import { Box, Container, Title, Text, Button } from "@mantine/core"; +import Link from "next/link"; + +export default function NotFound() { + return ( + + + + + 404 + + + Album not found + + + + + + + + ); +} diff --git a/src/app/album/[albumId]/page.tsx b/src/app/album/[albumId]/page.tsx new file mode 100644 index 0000000..de8d918 --- /dev/null +++ b/src/app/album/[albumId]/page.tsx @@ -0,0 +1,196 @@ +import { notFound } from "next/navigation"; +import { Box, Container, Title, Text, Group, Badge, Card, Image, Stack, Grid, GridCol } from "@mantine/core"; +import { albums } from "../../../lib/list"; +import BackButton from "../../../Components/BackButton"; +import { getThemeColors } from "@/lib/themes"; + +export function generateStaticParams() { + return albums.map((album) => ({ + albumId: album.id, + })); +} + +export async function generateMetadata({ params }: { params: Promise<{ albumId: string }> }) { + const { albumId } = await params; + const album = albums.find((a) => a.id === albumId); + + if (!album) { + return { + title: "Album Not Found", + }; + } + + return { + title: `${album.label} - Linkin Park Albums`, + description: album.description, + }; +} + +export default async function AlbumDetail({ params }: { params: Promise<{ albumId: string }> }) { + const { albumId } = await params; + const album = albums.find((a) => a.id === albumId); + + if (!album) { + notFound(); + } + + // Theme colors based on album + + const theme = getThemeColors(albumId); + + return ( + + + {/* Back Button */} + + + + + {album.label} + + + + + {album.label} + + + Released:{" "} + {new Date(album.releaseDate).toLocaleDateString("en-US", { + year: "numeric", + month: "long", + day: "numeric", + })} + + {album.description} + + + + {/* Album Header */} + {/* + {/* Album Cover */} + {/* + + {album.label} + + */} + + {/* Album Info */} + {/* + + + {album.label} + + + + Released:{" "} + {new Date(album.releaseDate).toLocaleDateString("en-US", { + year: "numeric", + month: "long", + day: "numeric", + })} + + + + + {album.tracks.length} Songs + + + Linkin Park + + + + + + + + {album.description} + + */} + {/* */} + + {/* Track List */} + {album.tracks.length > 0 && ( + + + Track List + + + + {album.tracks.map((track, index) => ( + + + + + {index + 1} + + + {track.label} + + + + + + {track.duration} + + + {track.studioUrl && ( + + Studio + + )} + {track.emilyLiveUrl && ( + + Emily Live + + )} + {track.lpLiveUrl && ( + + LP Live + + )} + + + + + ))} + + + )} + + + ); +} diff --git a/src/app/api/favico/route.tsx b/src/app/api/favico/route.tsx new file mode 100644 index 0000000..139c481 --- /dev/null +++ b/src/app/api/favico/route.tsx @@ -0,0 +1,34 @@ +import { type NextRequest, NextResponse } from "next/server"; +import { albums } from "../../../lib/list"; +import { cookies } from "next/headers"; + +// Absolute URL redirect is required by Next.js middleware & API routes +export async function GET(request: NextRequest) { + const url = new URL(request.url); + const randParam = url.searchParams.get("rand"); + const cookieStore = await cookies(); + const lastImage = cookieStore.get("lastImage")?.value; + + // Get all available albums, excluding the previous image + const availableAlbums = lastImage ? albums.filter((album) => album.image !== lastImage) : albums; + + // Choose a random album, optionally seeded by the "rand" query param + let randomIndex: number; + if (randParam && !Number.isNaN(Number(randParam))) { + randomIndex = Number(randParam) % availableAlbums.length; + } else { + randomIndex = Math.floor(Math.random() * availableAlbums.length); + } + const album = availableAlbums[randomIndex]; + + // Ensure image path starts with a slash + const imagePath = album.image.startsWith("/") ? album.image : `/${album.image}`; + + // Build absolute URL for the redirect + const { nextUrl } = request; + const absoluteUrl = `${nextUrl.protocol}//${nextUrl.host}${imagePath}`; + + cookieStore.set("lastImage", album.image); + + return NextResponse.redirect(absoluteUrl, 307); +} diff --git a/src/app/layout.tsx b/src/app/layout.tsx new file mode 100644 index 0000000..91dcaf5 --- /dev/null +++ b/src/app/layout.tsx @@ -0,0 +1,34 @@ +import "@mantine/core/styles.css"; +import "../index.css"; +import { MantineProvider, ColorSchemeScript } from "@mantine/core"; +import { ViewTransitions } from "next-view-transitions"; +import { Nunito } from "next/font/google"; + +const nunito = Nunito({ + subsets: ["latin"], + weight: ["200", "300", "400", "500", "600", "700", "800", "900"], +}); + +export const metadata = { + title: "Linkin Park Albums", + description: "Explore the complete discography of Linkin Park", + icons: { + icon: "/api/favico", + }, +}; + +export default function RootLayout({ children }: { children: React.ReactNode }) { + return ( + + + + + {/* */} + + + {children} + + + + ); +} diff --git a/src/app/page.tsx b/src/app/page.tsx new file mode 100644 index 0000000..8736ebf --- /dev/null +++ b/src/app/page.tsx @@ -0,0 +1,29 @@ +import { Box, Container, Title, Text, SimpleGrid } from "@mantine/core"; +import AlbumCard from "../Components/AlbumCard"; +import { albums } from "../lib/list"; + +export default function HomePage() { + return ( + + + + + Linkin Park Albums + + + Explore the complete discography of Linkin Park + + + + + {albums.map((album) => ( + + ))} + + + + ); +} diff --git a/src/index.css b/src/index.css new file mode 100644 index 0000000..26cf7d5 --- /dev/null +++ b/src/index.css @@ -0,0 +1,91 @@ +@import "tailwindcss"; + +/* Dark mode base - prevent white flash */ +* { + border-color: #1f2937; +} + +html { + background-color: #111827; + scroll-behavior: smooth; +} + +body { + background-color: #111827 !important; + color: white; + margin: 0; + padding: 0; +} + +/* Custom scrollbar */ +::-webkit-scrollbar { + width: 8px; +} + +::-webkit-scrollbar-track { + background: rgba(255, 255, 255, 0.1); +} + +::-webkit-scrollbar-thumb { + background: rgba(255, 255, 255, 0.3); + border-radius: 4px; +} + +::-webkit-scrollbar-thumb:hover { + background: rgba(255, 255, 255, 0.5); +} + +/* View Transitions API Configuration */ +/* Enable smooth cross-document view transitions (browser navigation) */ +@view-transition { + navigation: auto; +} + +/* Default transition for all elements */ +::view-transition-old(root), +::view-transition-new(root) { + animation-duration: 0.3s; + animation-timing-function: ease-in-out; + animation-fill-mode: both; +} + +/* Fade transition for page root */ +::view-transition-old(root) { + animation-name: fade-out; +} + +::view-transition-new(root) { + animation-name: fade-in; +} + +/* Custom transitions for specific elements with view-transition-name */ +::view-transition-old(*), +::view-transition-new(*) { + animation-duration: 0.4s; + animation-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + animation-fill-mode: both; +} + +/* Smooth image transitions */ +::view-transition-image-pair(*) { + isolation: isolate; +} + +/* Keyframes for fade animations */ +@keyframes fade-in { + from { + opacity: 0; + } + to { + opacity: 1; + } +} + +@keyframes fade-out { + from { + opacity: 1; + } + to { + opacity: 0; + } +} diff --git a/src/lib/ListTypes.ts b/src/lib/ListTypes.ts new file mode 100644 index 0000000..832ed8a --- /dev/null +++ b/src/lib/ListTypes.ts @@ -0,0 +1,18 @@ +export type Track = { + id: string; + label: string; + duration: string; + studioUrl: string | null; + emilyLiveUrl: string | null; + lpLiveUrl: string | null; +}; + +export type Album = { + id: string; + label: string; + releaseDate: string; + image: string; + url: string; + description: string; + tracks: Track[]; +}; diff --git a/src/lib/list.ts b/src/lib/list.ts new file mode 100644 index 0000000..a7cb81f --- /dev/null +++ b/src/lib/list.ts @@ -0,0 +1,333 @@ +import type { Album } from "./ListTypes"; + +export const albums: Album[] = [ + { + id: "hybrid-theory", + label: "Hybrid Theory", + releaseDate: "2000-10-24", + image: "/hybrid_theory.jpg", + url: "/hybrid-theory", + description: + "Hybrid Theory is the debut studio album by American rock band Linkin Park, released on October 24, 2000, by Warner Bros. Records. It was recorded at The Plant Studios in Sausalito, California, and produced by Don Gilmore. The album was a commercial success, reaching number one on the Billboard 200 chart and selling over 10 million copies in the United States alone.", + tracks: [ + { + id: "1", + label: "Papercut", + duration: "03:04", + studioUrl: null, + emilyLiveUrl: null, + lpLiveUrl: null, + }, + { + id: "2", + label: "One Step Closer", + duration: "02:35", + studioUrl: null, + emilyLiveUrl: null, + lpLiveUrl: null, + }, + { + id: "3", + label: "With You", + duration: "03:23", + studioUrl: null, + emilyLiveUrl: null, + lpLiveUrl: null, + }, + { + id: "4", + label: "Points of Authority", + duration: "03:20", + studioUrl: null, + emilyLiveUrl: null, + lpLiveUrl: null, + }, + { + id: "5", + label: "Crawling", + duration: "03:29", + studioUrl: null, + emilyLiveUrl: null, + lpLiveUrl: null, + }, + { + id: "6", + label: "Runaway", + duration: "03:03", + studioUrl: null, + emilyLiveUrl: null, + lpLiveUrl: null, + }, + { + id: "7", + label: "By Myself", + duration: "03:09", + studioUrl: null, + emilyLiveUrl: null, + lpLiveUrl: null, + }, + { + id: "8", + label: "In the End", + duration: "03:36", + studioUrl: null, + emilyLiveUrl: null, + lpLiveUrl: null, + }, + { + id: "9", + label: "A Place for My Head", + duration: "03:04", + studioUrl: null, + emilyLiveUrl: null, + lpLiveUrl: null, + }, + { + id: "10", + label: "Forgotten", + duration: "03:04", + studioUrl: null, + emilyLiveUrl: null, + lpLiveUrl: null, + }, + { + id: "11", + label: "Cure for the Itch", + duration: "02:37", + studioUrl: null, + emilyLiveUrl: null, + lpLiveUrl: null, + }, + { + id: "12", + label: "Pushing Me Away", + duration: "03:11", + studioUrl: null, + emilyLiveUrl: null, + lpLiveUrl: null, + }, + ], + }, + { + id: "meteora", + label: "Meteora", + releaseDate: "2003-03-24", + image: "/meteora.jpg", + url: "/meteora", + description: + "Meteora is the second studio album by American rock band Linkin Park, released on March 24, 2003, by Warner Bros. Records. It was recorded at The Plant Studios in Sausalito, California, and produced by Don Gilmore. The album was a commercial success, reaching number one on the Billboard 200 chart and selling over 10 million copies in the United States alone.", + tracks: [ + { + id: "1", + label: "Foreword", + duration: "00:13", + studioUrl: null, + emilyLiveUrl: null, + lpLiveUrl: null, + }, + { + id: "2", + label: "Don't Stay", + duration: "03:07", + studioUrl: null, + emilyLiveUrl: null, + lpLiveUrl: null, + }, + { + id: "3", + label: "Somewhere I Belong", + duration: "03:33", + studioUrl: null, + emilyLiveUrl: null, + lpLiveUrl: null, + }, + { + id: "4", + label: "Lying from You", + duration: "02:55", + studioUrl: null, + emilyLiveUrl: null, + lpLiveUrl: null, + }, + { + id: "5", + label: "Hit the Floor", + duration: "02:44", + studioUrl: null, + emilyLiveUrl: null, + lpLiveUrl: null, + }, + { + id: "6", + label: "Easier to Run", + duration: "03:24", + studioUrl: null, + emilyLiveUrl: null, + lpLiveUrl: null, + }, + { + id: "7", + label: "Faint", + duration: "02:42", + studioUrl: null, + emilyLiveUrl: null, + lpLiveUrl: null, + }, + { + id: "8", + label: "Figure.09", + duration: "03:17", + studioUrl: null, + emilyLiveUrl: null, + lpLiveUrl: null, + }, + { + id: "9", + label: "Breaking the Habit", + duration: "03:16", + studioUrl: null, + emilyLiveUrl: null, + lpLiveUrl: null, + }, + { + id: "10", + label: "From the Inside", + duration: "02:55", + studioUrl: null, + emilyLiveUrl: null, + lpLiveUrl: null, + }, + { + id: "11", + label: "Nobody's Listening", + duration: "02:58", + studioUrl: null, + emilyLiveUrl: null, + lpLiveUrl: null, + }, + { + id: "12", + label: "Session", + duration: "02:24", + studioUrl: null, + emilyLiveUrl: null, + lpLiveUrl: null, + }, + { + id: "13", + label: "Numb", + duration: "03:05", + studioUrl: null, + emilyLiveUrl: null, + lpLiveUrl: null, + }, + ], + }, + { + id: "minutes-to-midnight", + label: "Minutes to Midnight", + releaseDate: "2007-05-14", + image: "/minutes_to_midnight.jpg", + url: "/minutes-to-midnight", + description: + "Minutes to Midnight is the third studio album by American rock band Linkin Park, released on May 14, 2007, by Warner Bros. Records. The album marked a departure from the band's previous nu-metal sound, incorporating more alternative rock and experimental elements.", + tracks: [ + { + id: "1", + label: "Wake", + duration: "01:40", + studioUrl: null, + emilyLiveUrl: null, + lpLiveUrl: null, + }, + { + id: "2", + label: "Given Up", + duration: "03:09", + studioUrl: null, + emilyLiveUrl: null, + lpLiveUrl: null, + }, + { + id: "3", + label: "Leave Out All the Rest", + duration: "03:29", + studioUrl: null, + emilyLiveUrl: null, + lpLiveUrl: null, + }, + { + id: "4", + label: "Bleed It Out", + duration: "02:44", + studioUrl: null, + emilyLiveUrl: null, + lpLiveUrl: null, + }, + { + id: "5", + label: "Shadow of the Day", + duration: "04:49", + studioUrl: null, + emilyLiveUrl: null, + lpLiveUrl: null, + }, + { + id: "6", + label: "What I've Done", + duration: "03:25", + studioUrl: null, + emilyLiveUrl: null, + lpLiveUrl: null, + }, + { + id: "7", + label: "Hands Held High", + duration: "03:53", + studioUrl: null, + emilyLiveUrl: null, + lpLiveUrl: null, + }, + { + id: "8", + label: "No More Sorrow", + duration: "03:41", + studioUrl: null, + emilyLiveUrl: null, + lpLiveUrl: null, + }, + { + id: "9", + label: "Valentine's Day", + duration: "03:16", + studioUrl: null, + emilyLiveUrl: null, + lpLiveUrl: null, + }, + { + id: "10", + label: "In Between", + duration: "03:16", + studioUrl: null, + emilyLiveUrl: null, + lpLiveUrl: null, + }, + { + id: "11", + label: "In Pieces", + duration: "03:38", + studioUrl: null, + emilyLiveUrl: null, + lpLiveUrl: null, + }, + { + id: "12", + label: "The Little Things Give You Away", + duration: "06:23", + studioUrl: null, + emilyLiveUrl: null, + lpLiveUrl: null, + }, + ], + }, +]; diff --git a/src/lib/themes.ts b/src/lib/themes.ts new file mode 100644 index 0000000..45c8bfc --- /dev/null +++ b/src/lib/themes.ts @@ -0,0 +1,25 @@ +export const getThemeColors = (albumId: string) => { + return themes[albumId] || themes["hybrid-theory"]; +}; + +const themes: Record = { + "hybrid-theory": { + primary: "#ff6b35", + secondary: "#f7931e", + accent: "#ff1744", + bg: "from-orange-900 via-red-900 to-orange-800", + }, + meteora: { + primary: "#8b5cf6", + secondary: "#a855f7", + accent: "#ec4899", + bg: "from-purple-900 via-pink-900 to-purple-800", + }, + "minutes-to-midnight": { + primary: "#1e40af", + secondary: "#3b82f6", + accent: "#06b6d4", + bg: "from-slate-900 via-blue-900 to-slate-800", + }, +}; +export type Theme = (typeof themes)["hybrid-theory"]; diff --git a/src/types/view-transitions.d.ts b/src/types/view-transitions.d.ts new file mode 100644 index 0000000..13715c9 --- /dev/null +++ b/src/types/view-transitions.d.ts @@ -0,0 +1,26 @@ +// Type definitions for View Transitions API +// https://developer.mozilla.org/en-US/docs/Web/API/View_Transitions_API + +import "react"; + +declare module "react" { + interface CSSProperties { + viewTransitionName?: string; + } +} + +// Extend global Document interface for view transitions +declare global { + interface Document { + startViewTransition?: (callback: () => void | Promise) => ViewTransition; + } + + interface ViewTransition { + finished: Promise; + ready: Promise; + updateCallbackDone: Promise; + skipTransition: () => void; + } +} + +export {}; diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..584ed54 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,39 @@ +{ + "compilerOptions": { + "target": "ES2020", + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "module": "ESNext", + "skipLibCheck": true, + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "react-jsx", + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "allowJs": true, + "incremental": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "paths": { + "@/*": ["./src/*"] + }, + "plugins": [ + { + "name": "next" + } + ], + "noEmit": true + }, + "include": [ + "next-env.d.ts", + "**/*.ts", + "**/*.tsx", + ".next/types/**/*.ts", + ".next\\dev/types/**/*.ts", + ".next\\dev/types/**/*.ts" + ], + "exclude": ["node_modules"] +}