Non-official site for The Life Series Minecraft hardcore survival multiplayer series housing every video www.life-series.online
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

seasons layout

+253 -42
src/assets/images/members/BdoubleO100.jpg

This is a binary file and will not be displayed.

src/assets/images/members/BigB.jpg

This is a binary file and will not be displayed.

src/assets/images/members/Etho.jpg

This is a binary file and will not be displayed.

src/assets/images/members/GeminiTay.jpg

This is a binary file and will not be displayed.

src/assets/images/members/GoodTimesWithScar.jpg

This is a binary file and will not be displayed.

src/assets/images/members/Grian.jpg

This is a binary file and will not be displayed.

src/assets/images/members/Impulse.jpg

This is a binary file and will not be displayed.

src/assets/images/members/InTheLittleWood.jpg

This is a binary file and will not be displayed.

src/assets/images/members/LDShadowLady.jpg

This is a binary file and will not be displayed.

src/assets/images/members/Mumbo.jpg

This is a binary file and will not be displayed.

src/assets/images/members/PearlescentMoon.jpg

This is a binary file and will not be displayed.

src/assets/images/members/Rendog.jpg

This is a binary file and will not be displayed.

src/assets/images/members/Skizzleman.jpg

This is a binary file and will not be displayed.

src/assets/images/members/Smajor1995.jpg

This is a binary file and will not be displayed.

src/assets/images/members/SmallishBeans.jpg

This is a binary file and will not be displayed.

src/assets/images/members/Solidarity.jpg

This is a binary file and will not be displayed.

src/assets/images/members/Tango.jpg

This is a binary file and will not be displayed.

src/assets/images/members/ZombieCleo.jpg

This is a binary file and will not be displayed.

+12 -1
src/content.config.ts
··· 19 19 }), 20 20 }); 21 21 22 - export const collections = { seasons }; 22 + const members = defineCollection({ 23 + loader: file('src/data/members.json'), 24 + schema: z.object({ 25 + name: z.string(), 26 + seasons: z.array(z.number()), 27 + }), 28 + }); 29 + 30 + export const collections = { 31 + seasons, 32 + members, 33 + }; 23 34
+93
src/data/members.json
··· 1 + [ 2 + { 3 + "id": "BdoubleO100", 4 + "name": "BdoubleO100", 5 + "seasons": [6, 5, 4, 3, 2, 1] 6 + }, 7 + { 8 + "id": "BigB", 9 + "name": "BigB", 10 + "seasons": [6, 5, 4, 3, 2, 1] 11 + }, 12 + { 13 + "id": "Etho", 14 + "name": "Etho", 15 + "seasons": [6, 5, 4, 3, 2, 1] 16 + }, 17 + { 18 + "id": "GeminiTay", 19 + "name": "GeminiTay", 20 + "seasons": [6, 5] 21 + }, 22 + { 23 + "id": "GoodTimesWithScar", 24 + "name": "GoodTimesWithScar", 25 + "seasons": [6, 5, 4, 3, 2, 1] 26 + }, 27 + { 28 + "id": "Grian", 29 + "name": "Grian", 30 + "seasons": [6, 5, 4, 3, 2, 1] 31 + }, 32 + { 33 + "id": "Impulse", 34 + "name": "Impulse", 35 + "seasons": [6, 5, 4, 3, 2, 1] 36 + }, 37 + { 38 + "id": "InTheLittleWood", 39 + "name": "InTheLittleWood", 40 + "seasons": [6, 5, 4, 3, 2, 1] 41 + }, 42 + { 43 + "id": "LDShadowLady", 44 + "name": "LDShadowLady", 45 + "seasons": [6, 5, 2] 46 + }, 47 + { 48 + "id": "Mumbo", 49 + "name": "Mumbo", 50 + "seasons": [6, 5, 2] 51 + }, 52 + { 53 + "id": "PearlescentMoon", 54 + "name": "PearlescentMoon", 55 + "seasons": [6, 5, 4, 3, 2] 56 + }, 57 + { 58 + "id": "Rendog", 59 + "name": "Rendog", 60 + "seasons": [6, 3, 2, 1] 61 + }, 62 + { 63 + "id": "Skizzleman", 64 + "name": "Skizzleman", 65 + "seasons": [6, 5, 4, 2, 1] 66 + }, 67 + { 68 + "id": "Smajor1995", 69 + "name": "Smajor1995", 70 + "seasons": [6, 5, 4, 3, 2, 1] 71 + }, 72 + { 73 + "id": "SmallishBeans", 74 + "name": "SmallishBeans", 75 + "seasons": [6, 5, 4, 3, 2, 1] 76 + }, 77 + { 78 + "id": "Solidarity", 79 + "name": "Solidarity", 80 + "seasons": [6, 5, 4, 3, 2, 1] 81 + }, 82 + { 83 + "id": "Tango", 84 + "name": "Tango", 85 + "seasons": [6, 5, 4, 3, 2, 1] 86 + }, 87 + { 88 + "id": "ZombieCleo", 89 + "name": "ZombieCleo", 90 + "seasons": [6, 5, 4, 3, 2, 1] 91 + } 92 + ] 93 +
+94
src/layouts/SeasonsLayout.astro
··· 1 + --- 2 + import { Image } from 'astro:assets'; 3 + import { getCollection } from 'astro:content'; 4 + import { Head } from 'astro-capo'; 5 + import { getMemberImage, getSeasonImage } from '@/utils/images'; 6 + import '@/styles/global.css'; 7 + 8 + interface Props { 9 + title: string; 10 + season: any; // TODO: type 11 + } 12 + 13 + const { title, season } = Astro.props; 14 + 15 + const seasons = await getCollection('seasons'); 16 + const members = await getCollection('members'); 17 + --- 18 + 19 + <html lang="en"> 20 + <Head> 21 + <meta charset="utf-8" /> 22 + <link rel="icon" type="image/svg+xml" href="/favicon.svg" /> 23 + <meta name="viewport" content="width=device-width" /> 24 + <meta name="generator" content={Astro.generator} /> 25 + <title>{title} · Life Series · Non-official site</title> 26 + </Head> 27 + <body> 28 + <div class="grid min-h-screen grid-cols-[auto_auto_1fr] items-start"> 29 + <div class="sticky top-0 grid h-full max-h-screen overflow-y-auto scrollbar-gutter-stable"> 30 + <div class="self-center"> 31 + <h2 id="all-seasons-title" class="sr-only">All seasons</h2> 32 + <nav aria-labelledby="all-seasons-title"> 33 + <ol role="list" class="seasons-list grid gap-24 overflow-x-clip px-4 py-16"> 34 + {seasons.map((season) => ( 35 + <li> 36 + <a href={`/seasons/${season.id}`}> 37 + <Image 38 + src={getSeasonImage(season.id)} 39 + width="360" 40 + alt="" 41 + loading="eager" 42 + class="max-w-80" 43 + /> 44 + <span class="sr-only">{season.data.title}</span> 45 + </a> 46 + </li> 47 + ))} 48 + </ol> 49 + </nav> 50 + </div> 51 + </div> 52 + <div class="sticky top-0 grid h-full max-h-screen overflow-y-auto pl-2 scrollbar-gutter-stable"> 53 + <div class="self-center"> 54 + <h2 id="all-members-title" class="sr-only">All members</h2> 55 + <nav aria-labelledby="all-members-title"> 56 + <ul role="list" class="py-16"> 57 + {members.map((member) => { 58 + const notPlaying = !Object.keys(season.data.videos).includes(member.data.name); 59 + 60 + return ( 61 + <li> 62 + <a 63 + href={`/seasons/${season.id}/${member.data.name}`} 64 + class:list={['w-full py-4', { 'grayscale-75': notPlaying }]} 65 + > 66 + <div class="flex items-center gap-8"> 67 + <Image 68 + src={getMemberImage(member.data.name)} 69 + width="64" 70 + height="64" 71 + alt="" 72 + loading="eager" 73 + class="max-h-32 max-w-32 rounded-full" 74 + /> 75 + <span class:list={[{ 'line-through decoration-2': notPlaying }]}> 76 + {member.data.name} 77 + </span> 78 + {!Object.keys(season.data.videos).includes(member.data.name) && ( 79 + <span class="sr-only">, not playing</span> 80 + )} 81 + </div> 82 + </a> 83 + </li> 84 + ); 85 + })} 86 + </ul> 87 + </nav> 88 + </div> 89 + </div> 90 + <slot /> 91 + </div> 92 + </body> 93 + </html> 94 +
+2 -1
src/pages/index.astro
··· 11 11 <main class="grid min-h-svh items-center justify-items-center overflow-x-hidden p-32"> 12 12 <h1 class="sr-only">Life Series · Non-official site</h1> 13 13 <nav class="w-full max-w-636"> 14 - <ol role="list" class="all-seasons-grid grid place-items-center gap-48"> 14 + <ol role="list" class="seasons-list grid grid-cols-[repeat(auto-fit,minmax(min(180px,100%),1fr))] place-items-center gap-48"> 15 15 {seasons.map((season) => ( 16 16 <li> 17 17 <a href={`/seasons/${season.id}`}> ··· 20 20 width="360" 21 21 alt="" 22 22 loading="eager" 23 + class="max-w-180" 23 24 /> 24 25 <span class="sr-only">{season.data.title} (Season {season.data.seasonNumber})</span> 25 26 </a>
+5 -4
src/pages/seasons/[season_id].astro
··· 1 1 --- 2 2 import { getCollection } from 'astro:content'; 3 - import BaseLayout from '@/layouts/BaseLayout.astro'; 3 + import SeasonsLayout from '@/layouts/SeasonsLayout.astro'; 4 4 5 5 export async function getStaticPaths() { 6 6 const seasons = await getCollection('seasons'); ··· 12 12 } 13 13 14 14 const { season } = Astro.props; 15 + 16 + const title = `${season.data.title} (S${season.data.seasonNumber})`; 15 17 --- 16 18 17 - <BaseLayout title={season.data.title}> 18 - <h1>{season.data.title}</h1> 19 - </BaseLayout> 19 + <SeasonsLayout title={title} season={season}> 20 20 21 + </SeasonsLayout> 21 22
+42 -36
src/styles/global.css
··· 10 10 --spring-transition-timing-function: linear(0, 0.01 1%, 0.041 2.1%, 0.091 3.2%, 0.163 4.4%, 0.329 6.6%, 0.83 12.5%, 0.955 14.2%, 1.056 15.8%, 1.138 17.4%, 1.2 19%, 1.243 20.6%, 1.268 22.2%, 1.275 24.5%, 1.251 27%, 1.204 29.6%, 1.059 35.8%, 0.999 38.8%, 0.951 42.2%, 0.928 45.6%, 0.931 50.8%, 1 62.5%, 1.019 69%, 1.019 74.7%, 0.996 90%, 1); 11 11 } 12 12 13 + @utility scrollbar-gutter-stable { 14 + scrollbar-gutter: stable; 15 + } 16 + 13 17 @layer base { 18 + @view-transition { 19 + navigation: auto; 20 + } 21 + 14 22 *, 15 23 *::before, 16 24 *::after { ··· 25 33 } 26 34 27 35 body { 36 + font-family: var(--font-sans); 28 37 background-color: var(--color-neutral-50); 29 38 color: var(--color-neutral-950); 30 39 } ··· 45 54 46 55 a { 47 56 color: inherit; 57 + text-decoration: none; 48 58 display: inline-block; 49 59 } 50 60 ··· 54 64 vertical-align: middle; 55 65 } 56 66 57 - .all-seasons-grid { 58 - grid-template-columns: repeat(auto-fit, minmax(min(180px, 100%), 1fr)); 59 - } 67 + .seasons-list { 68 + img { 69 + transition-property: filter, blur; 70 + transition-duration: 100ms; 71 + transition-timing-function: linear; 72 + } 60 73 61 - .all-seasons-grid:has(a:focus-visible) a:not(:focus-visible) img { 62 - filter: grayscale(75%); 63 - } 64 - 65 - .all-seasons-grid img { 66 - max-width: 180px; 67 - } 68 - 69 - @media (hover) { 70 - .all-seasons-grid img { 71 - transition: filter 250ms linear; 74 + &:has(a:focus-visible) a:not(:focus-visible) img { 75 + filter: grayscale(75%) blur(0.3px); 72 76 } 73 77 74 - .all-seasons-grid:has(a:hover) a:not(:hover) img { 75 - filter: grayscale(75%); 78 + @media (hover) { 79 + &:has(a:hover) a:not(:hover) img { 80 + filter: grayscale(75%) blur(0.3px); 81 + } 76 82 } 77 - } 78 83 79 - @media (prefers-reduced-motion: no-preference) { 80 - .all-seasons-grid li:nth-child(1) { --angle: 3deg; } 81 - .all-seasons-grid li:nth-child(2) { --angle: -2deg; } 82 - .all-seasons-grid li:nth-child(3) { --angle: 2deg; } 83 - .all-seasons-grid li:nth-child(4) { --angle: -1deg; } 84 - .all-seasons-grid li:nth-child(5) { --angle: 2deg; } 85 - .all-seasons-grid li:nth-child(6) { --angle: -3deg; } 84 + @media (prefers-reduced-motion: no-preference) { 85 + li:nth-child(1) { --angle: 3deg; } 86 + li:nth-child(2) { --angle: -2deg; } 87 + li:nth-child(3) { --angle: 2deg; } 88 + li:nth-child(4) { --angle: -1deg; } 89 + li:nth-child(5) { --angle: 2deg; } 90 + li:nth-child(6) { --angle: -3deg; } 86 91 87 - .all-seasons-grid a { 88 - transition: scale var(--spring-transition-duration), rotate var(--spring-transition-duration); 89 - transition-timing-function: var(--spring-transition-timing-function); 90 - } 92 + a { 93 + transition: scale var(--spring-transition-duration), rotate var(--spring-transition-duration); 94 + transition-timing-function: var(--spring-transition-timing-function); 95 + } 91 96 92 - .all-seasons-grid a:focus-visible { 93 - scale: 1.075; 94 - rotate: var(--angle); 97 + a:focus-visible { 98 + scale: 1.075; 99 + rotate: var(--angle); 100 + } 95 101 } 96 - } 97 102 98 - @media (hover) and (prefers-reduced-motion: no-preference) { 99 - .all-seasons-grid a:hover { 100 - scale: 1.075; 101 - rotate: var(--angle); 103 + @media (hover) and (prefers-reduced-motion: no-preference) { 104 + a:hover { 105 + scale: 1.075; 106 + rotate: var(--angle); 107 + } 102 108 } 103 109 } 104 110 }
+5
src/utils/images.ts
··· 1 1 import type { ImageMetadata } from 'astro'; 2 2 3 + export async function getMemberImage(member: string) { 4 + const memberImages = import.meta.glob<{ default: ImageMetadata }>('/src/assets/images/members/*'); 5 + return memberImages[`/src/assets/images/members/${member}.jpg`](); 6 + } 7 + 3 8 export async function getSeasonImage(season: string) { 4 9 const seasonImages = import.meta.glob<{ default: ImageMetadata }>('/src/assets/images/seasons/*'); 5 10 return seasonImages[`/src/assets/images/seasons/${season}.webp`]();