update to Svelte 5

This commit is contained in:
VDawg 2025-09-27 21:38:20 +02:00
parent 29b7c040d3
commit bc6151c57e
50 changed files with 1073 additions and 864 deletions

View file

@ -3,6 +3,7 @@
"version": "1.1.0",
"description": "Website for Hyprland - Hyprland - A wayland compositor with the looks.",
"repository": "github:hyprwm/hyprland-website",
"type": "module",
"scripts": {
"dev": "vite dev",
"build": "vite build",
@ -13,7 +14,7 @@
"keywords": [
"hyprland"
],
"author": "",
"author": "VDawg",
"license": "BSD-3-Clause",
"private": "true",
"os": [
@ -27,26 +28,25 @@
"@iconify/json": "^2.2.220",
"@interactjs/types": "^1.10.27",
"@sveltejs/adapter-static": "^3.0.2",
"@sveltejs/kit": "^2.5.17",
"@sveltejs/vite-plugin-svelte": "^3.1.1",
"@sveltejs/kit": "^2.39.1",
"@sveltejs/vite-plugin-svelte": "^4.0.4",
"autoprefixer": "^10.4.19",
"eslint": "^9.5.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-svelte": "^2.40.0",
"eslint-plugin-svelte": "^2.46.1",
"mdsvex": "^0.11.2",
"postcss": "^8.4.38",
"prettier": "^3.3.2",
"prettier-plugin-svelte": "^3.2.4",
"prettier-plugin-svelte": "^3.4.0",
"simplex-noise": "^4.0.1",
"svelte": "^4.2.18",
"svelte-check": "^3.8.1",
"svelte": "^5.38.10",
"svelte-check": "^4.3.1",
"tailwindcss": "^3.4.4",
"tailwindcss-animate": "^1.0.7",
"typescript": "^5.4.5",
"typescript": "^5.9.2",
"unplugin-icons": "^0.19.0",
"vite": "^5.3.1"
"vite": "^5.4.20"
},
"type": "module",
"dependencies": {
"@fontsource-variable/inter": "^5.2.5",
"@fontsource/ibm-plex-mono": "^5.0.13",

906
pnpm-lock.yaml generated

File diff suppressed because it is too large Load diff

View file

@ -407,7 +407,7 @@
"size": 85
},
{
"image": "https://cdn.discordapp.com/avatars/321614457623019520/abd926b4f0c2ba37d6ee9359ad0599d2.webp",
"image": "https://xcdn.discordapp.com/avatars/321614457623019520/abd926b4f0c2ba37d6ee9359ad0599d2.webp",
"class": "outline-orange-500",
"coordinates": [
962,

View file

@ -1,52 +1,61 @@
<script>
<script lang="ts">
import {
BehaviorSubject,
Subject,
auditTime,
combineLatest,
debounceTime,
delay,
distinctUntilChanged,
filter,
map,
of,
startWith,
switchMap,
timer
timer,
type Observable
} from 'rxjs'
import { onDestroy, onMount } from 'svelte'
import { spring } from 'svelte/motion'
import { Spring } from 'svelte/motion'
/** The start position of the gradient. */
export let startPosition = [-1000, -1000]
type Props = {
/** The start position of the gradient. */
startPosition?: [number, number]
class?: string
}
let { class: className, startPosition = [-1000, -1000] }: Props = $props()
let wrapperElement: HTMLDivElement | undefined = $state(undefined)
/** @type {HTMLDivElement}*/
let wrapperElement = undefined
/** @type {import('rxjs').BehaviorSubject<boolean>}*/
const isMouseOver$ = new BehaviorSubject(false).pipe(
// Do not harshly stop updating the gradient when the mouse leaves, but wait a bit
switchMap((isTrue) => (isTrue ? of(isTrue) : timer(1500).pipe(map(() => false)))),
distinctUntilChanged()
)
) as BehaviorSubject<boolean>
/** @type {import('rxjs').BehaviorSubject<number>}*/
const gradientSize$ = new BehaviorSubject().pipe(
const gradientSize$: BehaviorSubject<number> = new BehaviorSubject(undefined).pipe(
// Debounce resize events with some high number for performance
debounceTime(1),
map(() => wrapperElement.getBoundingClientRect().width * 3),
auditTime(16),
map(() => (wrapperElement?.getBoundingClientRect().width ?? 0) * 3),
startWith(800)
)
/** @type {import('rxjs').Subject< {clientX: number, clientY: number} >}*/
const mousePosition$ = new Subject()
) as BehaviorSubject<number>
const mousePosition$: Subject<{ clientX: number; clientY: number }> = new Subject()
const gradientPosition$ = combineLatest([mousePosition$, gradientSize$, isMouseOver$]).pipe(
const gradientPosition$: Observable<[number, number]> = combineLatest([
mousePosition$,
gradientSize$,
isMouseOver$
]).pipe(
filter(([_, __, isMouseOver]) => isMouseOver),
map(([{ clientX, clientY }, gradientSize]) => {
const { x, y } = wrapperElement?.getBoundingClientRect() ?? { x: 0, y: 0 }
return [clientX - x - gradientSize * 0.5, clientY - y - gradientSize * 0.5]
return [clientX - x - gradientSize * 0.5, clientY - y - gradientSize * 0.5] as [
number,
number
]
}),
startWith(startPosition)
)
const gradientWiggle = spring(startPosition, { damping: 0.95, stiffness: 0.1 })
const gradientWiggle = new Spring(startPosition, { damping: 0.95, stiffness: 0.1 })
const subscription = gradientPosition$.subscribe((data) => gradientWiggle.set(data))
onDestroy(() => {
@ -60,16 +69,16 @@
hasJustMounted = false
})
$: {
$effect(() => {
if (!$isMouseOver$) {
globalThis.document?.removeEventListener('mousemove', track)
}
}
})
function resizeGradient() {
if (hasJustMounted || !isMouseOver$) return
gradientSize$.next()
gradientSize$.next(0)
}
function startTrackingMouse() {
@ -78,7 +87,7 @@
globalThis.document?.addEventListener('mousemove', track)
}
function track({ clientX, clientY }) {
function track({ clientX, clientY }: { clientX: number; clientY: number }) {
mousePosition$.next({ clientX, clientY })
}
@ -90,9 +99,9 @@
<svelte:window on:resize={resizeGradient} />
<div
class={$$props.class + ' wrapper'}
on:mouseenter={startTrackingMouse}
on:mouseleave={({ clientX, clientY, currentTarget }) => {
class={className + ' wrapper'}
onmouseenter={startTrackingMouse}
onmouseleave={({ clientX, clientY, currentTarget }) => {
const { x, width, y, height } = currentTarget.getBoundingClientRect()
const isMouseStillOver =
x <= clientX && y <= clientY && x + width > clientX && y + height > clientY
@ -103,8 +112,8 @@
>
<div
class="gradient"
style:--x={$gradientWiggle.at(0) + 'px'}
style:--y={$gradientWiggle.at(1) + 'px'}
style:--x={gradientWiggle.current.at(0) + 'px'}
style:--y={gradientWiggle.current.at(1) + 'px'}
style:--size={$gradientSize$ + 'px'}
></div>

View file

@ -1,21 +1,37 @@
<script lang="ts">
import { cn } from '$lib/Helper'
export let size: 'md' | 'lg' | 'xl' = 'md'
export let type: 'primary' | 'outline' | 'fancyOutline' = 'primary'
interface Props {
size?: 'md' | 'lg' | 'xl'
type?: 'primary' | 'outline' | 'fancyOutline'
href?: string | undefined
newTab?: boolean
children?: import('svelte').Snippet
onClick?: () => void
[key: string]: any
}
export let href: string | undefined = undefined
export let newTab = false
let {
size = 'md',
type = 'primary',
href = undefined,
newTab = false,
children,
onClick,
...rest
}: Props = $props()
$: classes = cn(
'animate rounded text-sm font-bold hover:scale-[1.01] active:scale-100',
'primary' == type && 'bg-slate-200 text-black',
'outline' == type && 'bg-transparent text-white outline outline-2 outline-slate-200',
'fancyOutline' == type && 'fancy',
'md' == size && 'min-w-[5.5rem] px-4 py-2.5',
'lg' == size && 'min-w-[5.5rem] px-6 py-3',
'xl' == size && 'min-w-[5.5rem] px-8 py-3.5',
$$restProps.class
let classes = $derived(
cn(
'animate rounded text-sm font-bold hover:scale-[1.01] active:scale-100',
'primary' == type && 'bg-slate-200 text-black',
'outline' == type && 'bg-transparent text-white outline outline-2 outline-slate-200',
'fancyOutline' == type && 'fancy',
'md' == size && 'min-w-[5.5rem] px-4 py-2.5',
'lg' == size && 'min-w-[5.5rem] px-6 py-3',
'xl' == size && 'min-w-[5.5rem] px-8 py-3.5',
rest.class
)
)
</script>
@ -23,38 +39,39 @@
<div class="relative max-w-max">
<svelte:element
this={href ? 'a' : 'button'}
{...$$restProps}
{...rest}
{href}
role="button"
tabindex="0"
target={newTab ? '_blank' : undefined}
class={classes}
on:click
onclick={onClick}
>
<slot>NO LABEL PROVIDED</slot>
{#if children}{@render children()}{:else}NO LABEL PROVIDED{/if}
</svelte:element>
<span
class="fancy-bg absolute inset-0 -z-10 h-full w-[110%] min-w-[5rem] scale-y-75 bg-cyan-500/90 px-4 py-2 blur-xl"
style="--easing: x; --duration: 8s;"
/>
></span>
<span
class="fancy-bg absolute inset-0 -z-10 h-full w-[110%] min-w-[5rem] scale-y-75 bg-secondary/90 px-4 py-2 blur-xl"
style="--easing: y; --duration: 8s;"
/>
></span>
<span
class="fancy-bg absolute inset-0 -z-10 h-full w-[110%] min-w-[5rem] scale-y-75 bg-purple-500/90 px-4 py-2 blur-xl"
style="--easing: z;--duration: 8s;"
/>
></span>
</div>
{:else}
<svelte:element
this={href ? 'a' : 'button'}
{...$$restProps}
{...rest}
{href}
role="button"
tabindex="0"
class={classes}
on:click><slot>NO LABEL PROVIDED</slot></svelte:element
onclick={onClick}
>{#if children}{@render children()}{:else}NO LABEL PROVIDED{/if}</svelte:element
>
{/if}

View file

@ -1,18 +1,28 @@
<script>
import { run, createBubbler, handlers } from 'svelte/legacy';
const bubble = createBubbler();
import clsx from 'clsx'
import { getContext, onMount } from 'svelte'
import { cardsContext } from '$lib/components/CardsContainer.svelte'
import { spring } from 'svelte/motion'
import { getIsMobile } from '$lib/Helper.ts'
/** @type {'cyan' | 'purple'}*/
export let color = 'cyan'
/** @type {number | number}*/
export let gradientOpacity = undefined
/**
* @typedef {Object} Props
* @property {'cyan' | 'purple'} [color]
* @property {number | number} [gradientOpacity]
* @property {import('svelte').Snippet} [children]
*/
/** @type {Props & { [key: string]: any }} */
let { color = 'cyan', gradientOpacity = undefined, children, ...rest } = $props();
const { mouseCoordinates$, isHoverCards, enableBorders = true } = getContext(cardsContext)
/** @type HTMLDivElement */
let container
let container = $state()
let isMobile = false
const damping = 0.2
@ -25,15 +35,10 @@
const bounceBack = 2
const soft = 0.8
let isMouseOver = false
let isMouseOver = $state(false)
/** Has the mouse entered and not left*/
let hasMouseEntered = false
$: {
if (container && $mouseCoordinates$?.x !== undefined) {
updateGradient()
}
}
onMount(() => {
isMobile = getIsMobile()
@ -75,35 +80,38 @@
if ($mouseCoordinates$.y > rectY + height) fillX.set(rectY - height - bounceBack, { soft })
else fillY.set(normY)
}
run(() => {
if (container && $mouseCoordinates$?.x !== undefined) {
updateGradient()
}
});
</script>
<div
class={clsx('card group ', $$restProps.class)}
class={clsx('card group ', rest.class)}
style:--x={$fillX}
style:--y={$fillY}
style:--borderX={enableBorders && $borderX}
style:--borderY={enableBorders && $borderY}
class:isHoverCards={$isHoverCards}
bind:this={container}
on:mouseenter={() => (isMouseOver = true)}
on:mouseleave={() => {
onmouseenter={handlers(() => (isMouseOver = true), bubble('mouseenter'))}
onmouseleave={handlers(() => {
isMouseOver = false
updateGradient()
}}
}, bubble('mouseleave'))}
class:purpleGradient={color === 'purple'}
role="contentinfo"
on:mouseenter
on:mouseleave
>
<div
class="absolute inset-0 z-10 m-0.5 size-full min-h-0 min-w-0 grow overflow-hidden rounded-3xl"
>
<slot>Nothing in the slot here</slot>
{#if children}{@render children()}{:else}Nothing in the slot here{/if}
</div>
<div class="gradient max-sm:hidden" style:opacity={gradientOpacity} />
<div class="gradient_black max-sm:hidden" />
<div class="gradient max-sm:hidden" style:opacity={gradientOpacity}></div>
<div class="gradient_black max-sm:hidden"></div>
{#if enableBorders}
<div class="border-gradient max-sm:hidden" />
<div class="border-gradient max-sm:hidden"></div>
{/if}
</div>

View file

@ -1,4 +1,4 @@
<script context="module">
<script module>
export const cardsContext = Symbol('mouseContext')
</script>
@ -9,13 +9,20 @@
import { onMount, setContext } from 'svelte'
import { writable } from 'svelte/store'
export let enableBorders = true
/**
* @typedef {Object} Props
* @property {boolean} [enableBorders]
* @property {import('svelte').Snippet} [children]
*/
/** @type {Props & { [key: string]: any }} */
let { enableBorders = true, children, ...rest } = $props();
const fps = 1000 / 60
/** @type {HTMLElement}*/
let containerElement
let isMobile = false
let containerElement = $state()
let isMobile = $state(false)
const context = setContext(cardsContext, {
mouseCoordinates$: new BehaviorSubject({ x: 0, y: 0 }).pipe(throttleTime(fps)),
@ -43,11 +50,11 @@
</script>
<div
class={$$restProps.class}
class={rest.class}
bind:this={containerElement}
role="contentinfo"
on:mouseenter={!isMobile && onMouseEnter}
on:mouseleave={!isMobile && onMouseLeave}
onmouseenter={!isMobile && onMouseEnter}
onmouseleave={!isMobile && onMouseLeave}
>
<slot />
{@render children?.()}
</div>

View file

@ -1,16 +1,22 @@
<script lang="ts">
export let href: string | undefined = undefined
export let isExternal: boolean = true
interface Props {
href?: string | undefined;
isExternal?: boolean;
children?: import('svelte').Snippet;
[key: string]: any
}
let { href = undefined, isExternal = true, children, ...rest }: Props = $props();
let isExternalProp = isExternal ? { target: '_blank', rel: 'noopener noreferrer' } : {}
</script>
{#if href}
<a {href} {...isExternalProp} class="cursor-pointer {$$restProps.class}">
<slot />
<a {href} {...isExternalProp} class="cursor-pointer {rest.class}">
{@render children?.()}
</a>
{:else}
<div class={$$restProps.class}>
<slot />
<div class={rest.class}>
{@render children?.()}
</div>
{/if}

View file

@ -1,3 +1,4 @@
<!-- @migration-task Error while migrating Svelte code: Cannot split a chunk that has already been edited (28:12 "on:click={$$slots.default ? undefined : copyCommand}") -->
<script>
import { onDestroy } from 'svelte'
import ClipboardIcon from '~icons/mingcute/copy-2-line'

View file

@ -1,56 +1,86 @@
<script lang="ts">
import clsx from 'clsx'
import { createEventDispatcher, onDestroy, onMount } from 'svelte'
import { onDestroy, onMount } from 'svelte'
import { spring, type Spring } from 'svelte/motion'
import { convertStoreToObservable, lerp } from '$lib/Helper'
import { inview } from 'svelte-inview'
import { throttleTime } from 'rxjs'
import type { Interactable } from '@interactjs/types/index'
import { lerp } from '$lib/Helper'
export let image: string
export let containerClass: string = ''
export let size: number
export let coordinates: readonly [number, number]
type Props = {
image: string
containerClass?: string
size: number
coordinates: readonly [number, number]
class?: string
export let quote: string | undefined = undefined
quote?: string | undefined
export let isAnimating = true
isAnimating?: boolean
export let element: HTMLElement | undefined = undefined
export let imageWrapper: HTMLDivElement | undefined = undefined
export let imageElement: HTMLImageElement | undefined = undefined
export let style: string | undefined = undefined
export let spawnDelay = 0
export let spawnInstanly = false
/**
* Usually just the size / biggestSize
* Goes from 0 - 1
*/
export let weight: number
export let getRestrictionElement: (() => HTMLElement) | undefined = undefined
element?: HTMLElement | undefined
imageWrapper?: HTMLDivElement | undefined
imageElement?: HTMLImageElement | undefined
style?: string | undefined
spawnDelay?: number
spawnInstanly?: boolean
/**
* Usually just the size / biggestSize
* Goes from 0 - 1
*/
weight: number
getRestrictionElement?: (() => HTMLElement) | undefined
onClick?: () => void
const dispatch = createEventDispatcher<{
enteredView: {
enteredView?: (data: {
dragCoordinates: Spring<readonly [number, number]>
imageElement: HTMLImageElement
element: HTMLElement
delay: number
}
dragged: readonly [number, number]
dragStart: any
dragEnd: any
hover: any
}>()
}) => void
dragged?: (data: readonly [number, number]) => void
dragStart?: (data: any) => void
dragEnd?: (data: any) => void
hover?: (data: any) => void
children?: import('svelte').Snippet
}
let {
image,
class: className,
containerClass = '',
size,
coordinates,
quote = undefined,
isAnimating = true,
element = undefined,
imageWrapper = undefined,
imageElement = undefined,
style = undefined,
spawnDelay = 0,
spawnInstanly = false,
/**
* Usually just the size / biggestSize
* Goes from 0 - 1
*/
weight,
getRestrictionElement = undefined,
onClick,
dragEnd,
dragStart,
enteredView,
hover,
children
}: Props = $props()
const dragCoordinates = spring([0, 0] as readonly [number, number], {
damping: lerp(0.2, 0.03, weight),
stiffness: lerp(0.2, 0.01, weight),
// stiffness: lerp(0.81, 0.9, relativeSize),
precision: 0.001
})
let hasEnteredView = false
let hasImageLoaded = false
let hasEnteredView = $state(false)
let hasImageLoaded = $state(false)
let interactionjs: Interactable
@ -63,7 +93,7 @@
() => {
hasEnteredView = true
dispatch('enteredView', {
enteredView?.({
dragCoordinates,
imageElement: imageElement!,
element: element!,
@ -79,18 +109,14 @@
inertia: { resistance: lerp(5, 200, weight) },
listeners: {
move({ dx, dy }) {
dragCoordinates.update(([x, y]) => {
x += dx
y += dy
return [x, y]
})
dragCoordinates.update(([x, y]) => [x + dx, y + dy])
},
start(event) {
dispatch('dragStart', event)
dragStart?.(event)
},
end(event) {
dispatch('dragEnd', event)
dragEnd?.(event)
}
},
modifiers: getRestrictionElement
@ -105,29 +131,21 @@
})
}
const draggedSubscription = convertStoreToObservable(dragCoordinates)
.pipe(throttleTime(80))
.subscribe((drag) => {
const displayedPosition = getDisplayedPosition(coordinates, drag)
dispatch('dragged', displayedPosition)
})
onMount(() => {
// Nesecarry as the load image event might not get fired when its already loaded ( for example after a page reload )
hasImageLoaded = hasImageLoaded || !!imageElement?.complete
})
onDestroy(() => {
draggedSubscription.unsubscribe()
interactionjs?.unset()
})
function getDisplayedPosition(
origin: readonly [x: number, y: number],
dragCoordinates: readonly [x: number, y: number]
) {
return [origin[0] + dragCoordinates[0], origin[1] + dragCoordinates[1]] as const
}
// function getDisplayedPosition(
// origin: readonly [x: number, y: number],
// dragCoordinates: readonly [x: number, y: number]
// ) {
// return [origin[0] + dragCoordinates[0], origin[1] + dragCoordinates[1]] as const
// }
</script>
<div
@ -149,18 +167,18 @@
style:translate={`calc( ${$dragCoordinates[0]}px ) ${$dragCoordinates[1]}px`}
use:inview={{ unobserveOnEnter: true, threshold: 0.2 }}
class:_animate={hasImageLoaded && isAnimating && hasEnteredView}
on:inview_enter={onViewEnter}
on:click
oninview_enter={onViewEnter}
onclick={onClick}
>
<div class="" bind:this={imageWrapper}>
<img
class="group aspect-square h-full w-full touch-none select-none rounded-[50%] object-cover outline outline-4 {$$restProps.class}"
class="group aspect-square h-full w-full touch-none select-none rounded-[50%] object-cover outline outline-4 {className}"
bind:this={imageElement}
on:load={() => (hasImageLoaded = true)}
onload={() => (hasImageLoaded = true)}
src={image}
alt="community profile picture"
aria-hidden="true"
on:mouseenter={(event) => dispatch('hover', event)}
onmouseenter={(event) => hover?.(event)}
class:hover:scale-125={!!quote}
loading="lazy"
referrerpolicy="no-referrer"
@ -170,10 +188,12 @@
{...{
/* @ts-ignore */
}}
onerror="this.__error = true"
onerror={() => (this.__error = true)}
{style}
/>
<slot />
{#if children}
{@render children()}
{/if}
</div>
{#if quote}

View file

@ -105,7 +105,7 @@
</div>
</div>
<div class="gradient" aria-hidden="true" />
<div class="gradient" aria-hidden="true"></div>
</footer>
<style lang="postcss">

View file

@ -1,25 +1,29 @@
<script>
import { lerp } from '$lib/Helper.ts'
<script lang="ts">
import { lerp } from '$lib/Helper'
import { createNoise2D } from 'simplex-noise'
import { onMount } from 'svelte'
import { expoIn } from 'svelte/easing'
/** Lifespan in milliseconds */
export let lifeSpan = 1500
export let maxSpeed = 20
export let minSpeed = 4
export let maxOpacity = 1
export let scale = 1
type Props = {
/** Lifespan in milliseconds */
lifeSpan: number
maxSpeed: number
minSpeed: number
maxOpacity: number
scale: number
}
let { lifeSpan = 1500, maxSpeed = 20, minSpeed = 4, maxOpacity = 1, scale = 1 }: Props = $props()
const isDescending = Math.random() > 0.8
const wobbliness = lerp(0.0001, 0.004, Math.random())
const speed = Math.random() * (maxSpeed - minSpeed) + minSpeed
let x = Math.random() * 30 - 30
let y = Math.random() * 2
let lifeRemaining = lifeSpan
$: lifePercentage = lifeRemaining / lifeSpan
let x = $state(Math.random() * 30 - 30)
let y = $state(Math.random() * 2)
let lifeRemaining = $state(lifeSpan)
let lifePercentage = $derived(lifeRemaining / lifeSpan)
let timestamp = Date.now()
const noiseY = createNoise2D()
@ -29,13 +33,14 @@
const color = colors.at(Math.ceil(colors.length * Math.random()))
onMount(() => {
let animationId
let animationId: number
let i = 0
animate()
async function animate() {
const deltaTime = (timestamp - Date.now()) / 17 // One frame should last roughly 17ms for 60fps
// One frame should last roughly 17ms for 60fps
const deltaTime = (timestamp - Date.now()) / 17
x += noiseX(i, 1) * speed * deltaTime * expoIn(lifePercentage)
y += noiseY(i, 1) * speed * deltaTime * expoIn(lifePercentage)

View file

@ -3,10 +3,21 @@
import type { Sponsor } from '../../routes/api/sponsors/+server'
import Clickable from './Clickable.svelte'
export let sponsor: Sponsor
export let showImage = false
export let showSlogan = false
export let sloganClass = ''
interface Props {
sponsor: Sponsor;
showImage?: boolean;
showSlogan?: boolean;
sloganClass?: string;
[key: string]: any
}
let {
sponsor,
showImage = false,
showSlogan = false,
sloganClass = '',
...rest
}: Props = $props();
</script>
<Clickable href={sponsor.link} class="flex flex-col items-center gap-4">
@ -15,7 +26,7 @@
title={sponsor.name}
class={cn(
'size-full self-center justify-self-start rounded-md object-contain',
$$restProps.class
rest.class
)}
src={sponsor.image}
alt={sponsor.name}

View file

@ -5,9 +5,15 @@
default: 'text-5xl md:text-7xl lg:text-8xl',
small: 'text-3xl md:text-5xl lg:text-6xl'
}
export let size: keyof typeof sizes = 'default'
interface Props {
size?: keyof typeof sizes;
children?: import('svelte').Snippet;
[key: string]: any
}
let { size = 'default', children, ...rest }: Props = $props();
</script>
<h1 class={cn('mb-12 font-bold', sizes[size], $$restProps.class)}>
<slot>No title given!!!</slot>
<h1 class={cn('mb-12 font-bold', sizes[size], rest.class)}>
{#if children}{@render children()}{:else}No title given!!!{/if}
</h1>

View file

@ -1,3 +1,13 @@
<script>
/**
* @typedef {Object} Props
* @property {import('svelte').Snippet} [children]
*/
/** @type {Props} */
let { children } = $props();
</script>
<p class="mb-3 font-extrabold text-slate-300">
<slot />
{@render children?.()}
</p>

View file

@ -1,3 +1,13 @@
<p class="-mt-4 mb-7 font-extrabold text-slate-300 sm:text-lg {$$restProps.class}">
<slot />
<script>
/**
* @typedef {Object} Props
* @property {import('svelte').Snippet} [children]
*/
/** @type {Props & { [key: string]: any }} */
let { children, ...rest } = $props();
</script>
<p class="-mt-4 mb-7 font-extrabold text-slate-300 sm:text-lg {rest.class}">
{@render children?.()}
</p>

View file

@ -1,26 +1,43 @@
<script>
import { animateIn, cn } from '$lib/Helper.ts'
/** @type { 'left' | 'right' | 'center'} */
export let align = 'center'
/**
* @typedef {Object} Props
* @property { 'left' | 'right' | 'center'} [align]
* @property {import('svelte').Snippet} [pre]
* @property {import('svelte').Snippet} [title]
* @property {import('svelte').Snippet} [subtitle]
* @property {import('svelte').Snippet} [children]
*/
$: alignClass =
align === 'center'
/** @type {Props & { [key: string]: any }} */
let {
align = 'center',
pre,
title,
subtitle,
children,
...rest
} = $props();
let alignClass =
$derived(align === 'center'
? 'text-center items-center'
: align === 'left'
? 'text-left items-start'
: 'text-right items-end'
: 'text-right items-end')
</script>
<hgroup
use:animateIn={{ slide: 24, fade: 0 }}
class={cn('z-10 flex flex-col px-3 ', alignClass, $$restProps.class)}
class={cn('z-10 flex flex-col px-3 ', alignClass, rest.class)}
>
<slot name="pre" />
{@render pre?.()}
<slot name="title">No title given!!!</slot>
{#if title}{@render title()}{:else}No title given!!!{/if}
<slot name="subtitle" />
{@render subtitle?.()}
<slot />
{@render children?.()}
</hgroup>

View file

@ -1,3 +1,4 @@
<!-- @migration-task Error while migrating Svelte code: $$props is used together with named props in a way that cannot be automatically migrated. -->
<script>
import clsx from 'clsx'
import PlayIcon from '~icons/mingcute/play-circle-line'

View file

@ -2,9 +2,9 @@
import ArrowRight from '~icons/mingcute/arrow-right-circle-line'
import { animateIn, formatDate } from '$lib/Helper.ts'
export let entry
let { entry } = $props();
$: link = `/news/${entry.slug}`
let link = $derived(`/news/${entry.slug}`)
</script>
<li class="flex" use:animateIn={{ fade: 0, slide: 24 }}>

View file

@ -1,4 +1,9 @@
<svg viewBox="0 330 1006.49 347.4685344827586" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg" style="width: 6rem; height: 2rem;" class={$$props.class} width="1006.49" height="347.4685344827586">
<script>
/** @type {{ [key: string]: any }} */
let { ...props } = $props();
</script>
<svg viewBox="0 330 1006.49 347.4685344827586" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg" style="width: 6rem; height: 2rem;" class={props.class} width="1006.49" height="347.4685344827586">
<defs>
<style>
.st0 {
@ -36,7 +41,7 @@
.st8 {
fill: url(#i);
}
</style><style class="darkreader darkreader--sync" media="screen"/>
</style><style class="darkreader darkreader--sync" media="screen"></style>
<linearGradient gradientUnits="userSpaceOnUse" y2="491.29" x2="561.93" y1="593.85" x1="561.93" id="i">
<stop stop-color="#00a8f4" offset="0" style="--darkreader-inline-stopcolor: var(--darkreader-background-00a8f4, #0086c3);" data-darkreader-inline-stopcolor=""/>
<stop stop-color="#00e5d0" offset="1" style="--darkreader-inline-stopcolor: var(--darkreader-background-00e5d0, #00b7a6);" data-darkreader-inline-stopcolor=""/>

Before

Width:  |  Height:  |  Size: 6.7 KiB

After

Width:  |  Height:  |  Size: 6.7 KiB

View file

@ -4,12 +4,19 @@
import './styles.css'
import '@fontsource-variable/inter'
import '@fontsource/ibm-plex-mono/500.css'
/**
* @typedef {Object} Props
* @property {import('svelte').Snippet} [children]
*/
/** @type {Props} */
let { children } = $props();
</script>
<Navbar />
<main class="mx-auto flex min-h-screen w-full flex-col">
<slot />
{@render children?.()}
</main>
<Footer />

View file

@ -10,7 +10,7 @@
import Sponsors from './home-slices/SponsorsSlice.svelte'
import CommunitySlice from './home-slices/CommunitySlice.svelte'
export let data
let { data } = $props()
</script>
<div class="overflow-hidden">

View file

@ -1,4 +1,6 @@
<script lang="ts">
import { run } from 'svelte/legacy';
import { navigating, page } from '$app/stores'
import GithubIcon from '~icons/ri/github-fill'
import DiscordIcon from '~icons/prime/discord'
@ -9,9 +11,11 @@
import { discordLink } from '$lib/constants'
import { forgejoLink } from '$lib/constants'
let isExpanded = false
let isExpanded = $state(false)
$: if ($navigating) isExpanded = false
run(() => {
if ($navigating) isExpanded = false
});
function toggleExpanded() {
isExpanded = !isExpanded
@ -30,7 +34,7 @@
<button
class="z-50 rounded-full bg-black/50 p-2 md:backdrop-blur nav:hidden"
on:click={toggleExpanded}
onclick={toggleExpanded}
aria-label="Open Navigation"
>
{#if isExpanded}

View file

@ -5,10 +5,10 @@
import Contest from './Contest.svelte'
import FamedRice from './FamedRice.svelte'
export let data
let { data } = $props();
</script>
<div class="fancy-top-gradient" />
<div class="fancy-top-gradient"></div>
<section>
<div class="hero-wrapper">
@ -19,10 +19,14 @@
</div>
</div>
<Title>
<TitleHeading slot="title" class="">Hall of Fame</TitleHeading>
<TitleSubtile slot="subtitle" class="class-w-[40ch]">
The chronicles of the triumphant from bygone rice contests held within our Discord
</TitleSubtile>
{#snippet title()}
<TitleHeading class="">Hall of Fame</TitleHeading>
{/snippet}
{#snippet subtitle()}
<TitleSubtile class="class-w-[40ch]">
The chronicles of the triumphant from bygone rice contests held within our Discord
</TitleSubtile>
{/snippet}
</Title>
</div>

View file

@ -1,15 +1,27 @@
<script>
import { inview } from 'svelte-inview'
/** @type {string} */
export let name
/** @type {number} */
export let number
/** @type {string} */
export let date
/**
* @typedef {Object} Props
* @property {string} name
* @property {number} number
* @property {string} date
* @property {import('svelte').Snippet} [children]
*/
/** @type {Props} */
let {
name,
number,
date,
children
} = $props();
/** Used to show the background gradient. Show if user scrolls, but do not disable when the user scrolls past from the top. */
let enabled = false
let enabled = $state(false)
function setEnabled({ detail }) {
// Show the effect when the user scrolls in and keep it enabled until the user scrolls up from it again
@ -31,7 +43,7 @@
<header
class="flex flex-col items-center gap-2 p-6 mix-blend-color-dodge"
use:inview={{ threshold: 0.45 }}
on:inview_change={setEnabled}
oninview_change={setEnabled}
>
<div class="text-xl font-bold text-neutral-300/80 sm:text-2xl">Contest #{number}</div>
<h2 class="text-center text-6xl font-bold text-neutral-200/80 sm:text-9xl">{name}</h2>
@ -48,7 +60,7 @@
</div>
<div class="flex flex-col gap-40">
<slot />
{@render children?.()}
</div>
</section>

View file

@ -4,25 +4,41 @@
import PlayIconFill from '~icons/mingcute/play-fill'
import PlayIconOutline from '~icons/mingcute/play-line'
export let name: string
export let creator: string
export let dotfilesLink: string
export let creatorProfilePicture: string
export let thumbnail: string
export let video: string | undefined = undefined
/**
interface Props {
name: string;
creator: string;
dotfilesLink: string;
creatorProfilePicture: string;
thumbnail: string;
video?: string | undefined;
/**
* Specify the blurred background image to be used.
* Defaults to `"generated_<thumbnail>"` * */
export let blurredThumbnail: string | undefined = undefined
export let pretitel: string
blurredThumbnail?: string | undefined;
pretitel: string;
[key: string]: any
}
let toShow: 'thumbnail' | 'video' = 'thumbnail'
let {
name,
creator,
dotfilesLink,
creatorProfilePicture,
thumbnail,
video = undefined,
blurredThumbnail = undefined,
pretitel,
...rest
}: Props = $props();
let toShow: 'thumbnail' | 'video' = $state('thumbnail')
let background = blurredThumbnail ?? getGeneratedPath(thumbnail)
</script>
<div
class="flex flex-col items-center gap-10 px-4 {$$restProps.class}"
class="flex flex-col items-center gap-10 px-4 {rest.class}"
style:--bg="url('{background}')"
>
<div class="flex flex-col items-center justify-center">
@ -57,24 +73,24 @@
<div class="rice group relative" class:hasVideo={video}>
{#if toShow === 'thumbnail'}
{#if video}
<button on:click={() => (toShow = 'video')}>
<button onclick={() => (toShow = 'video')}>
<img src={thumbnail} alt={`${name} by ${creator} thumbnail`} class="" loading="lazy" />
</button>
<button
class="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 rounded-full bg-gray-900/60 p-4 text-white/80 opacity-0 outline outline-gray-100/10 duration-300 hover:scale-105 hover:bg-gray-900/80 hover:text-white group-hover:opacity-100"
on:click={() => (toShow = 'video')}><PlayIconFill class="size-8 " /></button
onclick={() => (toShow = 'video')}><PlayIconFill class="size-8 " /></button
>
<button
class="absolute bottom-6 left-6 rounded-full bg-gray-900/60 p-2 text-white/80 outline outline-gray-100/10 duration-300 hover:scale-105 hover:bg-gray-900/80 hover:text-white"
on:click={() => (toShow = 'video')}><PlayIconOutline class="size-5 " /></button
onclick={() => (toShow = 'video')}><PlayIconOutline class="size-5 " /></button
>
{:else}
<img src={thumbnail} alt={`${name} by ${creator} thumbnail`} class="" loading="lazy" />
{/if}
{:else if toShow === 'video'}
<!-- svelte-ignore a11y-media-has-caption -->
<video autoplay controls poster={thumbnail} src={video} />
<!-- svelte-ignore a11y_media_has_caption -->
<video autoplay controls poster={thumbnail} src={video}></video>
{/if}
</div>
<!-- blur background -->
@ -85,7 +101,7 @@
alt={`${name} by ${creator} thumbnail`}
loading="lazy"
/>
<div class="grain_" />
<div class="grain_"></div>
</div>
</div>

View file

@ -10,14 +10,18 @@
import Chan from './community/Chan.svelte'
import type { CommunityProfile } from '$lib/Types'
export let communityProfiles: readonly CommunityProfile[]
interface Props {
communityProfiles: readonly CommunityProfile[];
}
let { communityProfiles }: Props = $props();
const biggestSize = communityProfiles.reduce(
(previousSize, { size }) => (size > previousSize ? size : previousSize),
0
)
let restrictionElement: HTMLElement
let restrictionElement: HTMLElement | undefined = $state()
</script>
<section
@ -25,10 +29,14 @@
bind:this={restrictionElement}
>
<Title>
<TitleHeading slot="title" class="">Join a great<br />community</TitleHeading>
<TitleSubtile slot="subtitle" class="class-w-[40ch]">
Get help from Distro Hoppers, Haiku writers,<br />Hydrohomies, and human_(probably)
</TitleSubtile>
{#snippet title()}
<TitleHeading class="">Join a great<br />community</TitleHeading>
{/snippet}
{#snippet subtitle()}
<TitleSubtile class="class-w-[40ch]">
Get help from Distro Hoppers, Haiku writers,<br />Hydrohomies, and human_(probably)
</TitleSubtile>
{/snippet}
</Title>
<div class="group mt-16 flex flex-col items-center">
@ -55,11 +63,11 @@
{...props}
weight={relativeSize}
spawnDelay={Math.pow(1 - props.size / biggestSize, 4) * 4654}
getRestrictionElement={() => restrictionElement}
getRestrictionElement={() => restrictionElement!}
/>
{/each}
<Chan {biggestSize} getRestrictionElement={() => restrictionElement} />
<Chan {biggestSize} getRestrictionElement={() => restrictionElement!} />
</div>
</div>

View file

@ -1,12 +1,22 @@
<script>
/** @type {string | undefined} */
export let image = undefined
/** @type {string | undefined} */
export let name = undefined
/**
* @type {string | undefined }
* Classes for the command text
* @typedef {Object} Props
* @property {string | undefined} [image]
* @property {string | undefined} [name]
* @property {import('svelte').Snippet} [imageExtra]
* @property {import('svelte').Snippet} [children]
*/
/** @type {Props} */
let {
image = undefined,
name = undefined,
imageExtra,
children
} = $props();
</script>
<div class="group relative flex flex-col items-center gap-2 md:flex-row md:gap-4">
@ -16,12 +26,12 @@
>
<img src={image} class="h-20 w-32 object-contain" alt="{name} Logo" loading="lazy" />{name}
<slot name="imageExtra" />
{@render imageExtra?.()}
</div>
{/if}
<!-- Command button slot -->
<div class="mb-2 w-full">
<slot />
{@render children?.()}
</div>
</div>

View file

@ -1,18 +1,25 @@
<script>
import { animateIn, getGeneratedPath } from '$lib/Helper.ts'
/** @type {string}
* The path to the image. Usually the file within `static`, but can also be an URL
/**
* @typedef {Object} Props
* @property {string} image
* @property {string | undefined} [imageClass]
* @property {string | undefined} [containerClass]
* @property {string} [blurredBackground]
*/
export let image
/** @type {string | undefined} */
export let imageClass = undefined
/** @type {string | undefined} */
export let containerClass = undefined
/** @type {string}
* The path to the image. Usually the file within `static`, but can also be an URL. Defaults to `generated_<image>`
*/
export let blurredBackground = undefined
/** @type {Props} */
let {
image,
imageClass = undefined,
containerClass = undefined,
blurredBackground = undefined
} = $props();
</script>
<div class="rice {containerClass} group">

View file

@ -1,4 +1,4 @@
<script>
<script lang='ts'>
import Card from '$lib/components/Card.svelte'
import GameIcon from '~icons/gg/games'
import SpecialWorkspaceIcon from '~icons/gg/shutterstock'
@ -21,8 +21,12 @@
<section class="relative flex max-w-screen-xl flex-col items-center px-3 md:px-8">
<Title>
<TitlePre slot="pre">TLDR</TitlePre>
<TitleHeading slot="title" class="">Features</TitleHeading>
{#snippet pre()}
<TitlePre >TLDR</TitlePre>
{/snippet}
{#snippet title()}
<TitleHeading class="">Features</TitleHeading>
{/snippet}
</Title>
<CardsContainer
@ -162,6 +166,8 @@
</section>
<style lang="postcss">
.icon-feature {
@apply flex items-center justify-center gap-3 font-bold text-slate-400;
}

View file

@ -10,12 +10,16 @@
<section class="" use:animateIn={{ fade: 0, slide: 24, duration: 3000, threshold: 0.1 }}>
<div class="z-20 -mb-40 px-4">
<Title>
<TitlePre slot="pre">Memorials of the ricing legends</TitlePre>
<TitleHeading slot="title" class="">Hall of Fame</TitleHeading>
{#snippet pre()}
<TitlePre >Memorials of the ricing legends</TitlePre>
{/snippet}
{#snippet title()}
<TitleHeading class="">Hall of Fame</TitleHeading>
{/snippet}
</Title>
</div>
<div class="atmosphere" />
<div class="atmosphere"></div>
<div
class="relative -mt-8 flex w-full max-w-[1100px] flex-col items-center justify-end gap-16 overflow-hidden [perspective:100px] md:px-16 lg:gap-24"
@ -41,7 +45,7 @@
/>
</div>
<div class="glow" />
<div class="glow"></div>
</section>
<style lang="postcss">

View file

@ -6,9 +6,9 @@
import HeroBackground from './HeroBackground.svelte'
import HyprlandLogo from '$lib/images/logos/HyprlandLogo.svelte'
export let backgroundData
let { backgroundData } = $props();
let isVisible = true
let isVisible = $state(true)
let isMobile = false
onMount(() => {
@ -19,7 +19,7 @@
<section
class="relative flex h-[100svh] min-h-[36rem] w-full flex-col items-center justify-center overflow-x-hidden"
use:inview
on:inview_change={({ detail: { inView } }) => {
oninview_change={({ detail: { inView } }) => {
isVisible = inView
}}
>

View file

@ -1,5 +1,5 @@
<script>
export let backgroundData
let { backgroundData } = $props();
const { workspacesPerRow, gapLength, workspaceHeight, height, leftColumns, rightColumns } =
backgroundData
@ -13,7 +13,7 @@
style={`--amount: ${workspacesPerRow}; --workspace-gap: ${gapLength}px;--workspace-height: ${workspaceHeight}px; --length: ${height}px;`}
>
<!-- Gradient background -->
<div class="top-light" />
<div class="top-light"></div>
<div class="columns left" aria-hidden="true">
{#each leftColumns as column}

View file

@ -17,13 +17,15 @@
<section class="relative w-full">
<div class="mb-16 md:mb-32">
<div class="h-[2px] w-full bg-gradient-to-l from-transparent via-cyan-400/50 to-transparent" />
<div class="h-[2px] w-full bg-gradient-to-l from-transparent via-cyan-400/50 to-transparent"></div>
</div>
<div class="top-gradient"></div>
<div class="relative mx-auto mb-12 max-w-5xl px-8 md:mb-20">
<Title class="mb-10">
<TitleHeading slot="title" class="">Hyprperks</TitleHeading>
{#snippet title()}
<TitleHeading class="">Hyprperks</TitleHeading>
{/snippet}
<TitleSubtile class="max-w-[55ch]">
<a href={hyprperksLink} class=" text-primary hover:underline" target="_blank"
>Subscribe for first-party configurations, priority support</a

View file

@ -1,4 +1,4 @@
<script>
<script lang="ts">
import ActiveGitIcon from '~icons/gg/git-branch'
import VaxryImage from '$lib/images/vaxry-github.webp'
@ -18,7 +18,7 @@
take
} from 'rxjs'
import GitTile from '$lib/components/GitTile.svelte'
import { lerp } from '$lib/Helper.ts'
import { lerp } from '$lib/Helper'
import { cubicInOut, expoInOut } from 'svelte/easing'
import DiscordProfilePicture from '$lib/components/DiscordProfilePicture.svelte'
import { setContext } from 'svelte'
@ -76,18 +76,18 @@
),
scan(
(acc, value) => (value === 0 ? [] : [...acc, ...Array.from({ length: value }, () => 1)]),
[]
[] as number[]
),
startWith([])
startWith([] as number[])
)
$: hue = lerp(200, 130, $cubicRelativeLevel$)
$: scale = lerp(0.9, 2, $cubicRelativeLevel$)
$: translateY = lerp(0, 10, $cubicRelativeLevel$)
let hue = $derived(lerp(200, 130, $cubicRelativeLevel$))
let scale = $derived(lerp(0.9, 2, $cubicRelativeLevel$))
let translateY = $derived(lerp(0, 10, $cubicRelativeLevel$))
/** @type {HTMLDivElement} */
let containerElement
let isAnimationComplete = false
let containerElement = $state()
let isAnimationComplete = $state(false)
const vaxrySize = 220
const contextId = Symbol('hypractive context')
@ -108,7 +108,7 @@
<div class="relative overflow-visible">
<button
class="flex items-center gap-3 font-bold text-slate-400 shadow-black drop-shadow-lg transition-colors hover:underline active:scale-95"
on:click={isAnimationComplete ? onClickUnlocked : onClick}
onclick={isAnimationComplete ? onClickUnlocked : onClick}
style:color={$relativeLevel$ > 0 ? `hsl(${hue} 64% 53%)` : undefined}
style:scale={$relativeLevel$ > 0 ? scale : undefined}
style:translate={$relativeLevel$ > 0 ? `0px -${translateY}px` : undefined}
@ -132,14 +132,14 @@
<div
class="vaxx-wrapper absolute bottom-[240px] left-1/2 z-50 aspect-square -translate-x-[100px] rounded-full animate-in fade-in-0 zoom-in-95 slide-in-from-bottom-[500px] slide-in-from-left-20 [animation-duration:2.5s]"
style:width={vaxrySize + 'px'}
on:animationend={() => (isAnimationComplete = true)}
onanimationend={() => (isAnimationComplete = true)}
>
<DiscordProfilePicture
image={VaxryImage}
size={vaxrySize}
weight={10}
coordinates={[0, 0]}
class="outline-orange-300"
{contextId}
isAnimating={false}
/>
</div>
@ -150,7 +150,7 @@
class="bg-gradient"
style:opacity={$hasAscended$ ? 1 : $relativeLevel$}
style="--relativeLevel: {$hasAscended$ ? 1 : $expoRelativeLevel$ - 0.2}"
/>
></div>
</div>
</div>

View file

@ -15,8 +15,12 @@
<section class="pb-6">
<Title>
<TitleHeading slot="title" class="">Install now</TitleHeading>
<TitleSubtile slot="subtitle" class="">For your favourite distro</TitleSubtile>
{#snippet title()}
<TitleHeading class="">Install now</TitleHeading>
{/snippet}
{#snippet subtitle()}
<TitleSubtile class="">For your favourite distro</TitleSubtile>
{/snippet}
</Title>
<div class="flex flex-col items-center gap-12 md:gap-6" use:animateIn={{ slide: 24, fade: 0 }}>
@ -25,23 +29,27 @@
>
<DistroOption name="Arch" image={archLogo}>
<CommandButton command="pacman -S hyprland">
<div slot="extra" class="absolute -bottom-4 left-1/2 min-w-max -translate-x-1/2">
AUR git version: <a
class=" "
target="_blank"
href="https://aur.archlinux.org/packages/hyprland-git/">hyprland-git</a
>
</div>
{#snippet extra()}
<div class="absolute -bottom-4 left-1/2 min-w-max -translate-x-1/2">
AUR git version: <a
class=" "
target="_blank"
href="https://aur.archlinux.org/packages/hyprland-git/">hyprland-git</a
>
</div>
{/snippet}
</CommandButton>
</DistroOption>
<DistroOption name="NixOS" image={nixLogo}>
<CommandButton command="programs.hyprland.enable">
<div slot="extra" class="absolute -bottom-4 left-1/2 min-w-max -translate-x-1/2">
<a href="https://wiki.hypr.land/Nix/" target="_blank"
>See more details and git version ↗</a
>
</div>
{#snippet extra()}
<div class="absolute -bottom-4 left-1/2 min-w-max -translate-x-1/2">
<a href="https://wiki.hypr.land/Nix/" target="_blank"
>See more details and git version ↗</a
>
</div>
{/snippet}
</CommandButton>
</DistroOption>
@ -51,17 +59,21 @@
<DistroOption name="openSUSE" image={suseLogo}>
<CommandButton command="zypper in hyprland">
<div slot="extra" class="absolute -bottom-4 left-1/2 min-w-max -translate-x-1/2">
or install “hyprland” via YaST2 Software.
</div>
{#snippet extra()}
<div class="absolute -bottom-4 left-1/2 min-w-max -translate-x-1/2">
or install “hyprland” via YaST2 Software.
</div>
{/snippet}
</CommandButton>
<img
class=" absolute inset-0 -z-10 translate-y-1 rotate-0 scale-90 opacity-0 transition-all duration-700 [transition-delay:2s] group-hover:-translate-x-3 group-hover:-translate-y-0 group-hover:-rotate-12 group-hover:scale-100 group-hover:opacity-90"
src={amongus}
slot="imageExtra"
alt=""
srcset=""
/>
{#snippet imageExtra()}
<img
class=" absolute inset-0 -z-10 translate-y-1 rotate-0 scale-90 opacity-0 transition-all duration-700 [transition-delay:2s] group-hover:-translate-x-3 group-hover:-translate-y-0 group-hover:-rotate-12 group-hover:scale-100 group-hover:opacity-90"
src={amongus}
alt=""
srcset=""
/>
{/snippet}
</DistroOption>
</div>

View file

@ -4,13 +4,17 @@
import TitlePre from '$lib/components/Title/TitlePre.svelte'
import Title from '$lib/components/Title/TitleWrapper.svelte'
export let news
let { news } = $props();
</script>
<section class="relative mb-12 max-w-5xl px-8 md:mb-20">
<Title>
<TitlePre slot="pre">Read while it's fresh!</TitlePre>
<TitleHeading slot="title" class="">Latest news</TitleHeading>
{#snippet pre()}
<TitlePre >Read while it's fresh!</TitlePre>
{/snippet}
{#snippet title()}
<TitleHeading class="">Latest news</TitleHeading>
{/snippet}
</Title>
<ul class="mt-8 flex flex-col gap-10">

View file

@ -13,9 +13,9 @@
import Button from '$lib/components/Button.svelte'
/** @type {HTMLVideoElement[]}*/
const videos = []
let activeIndex = 0
let isHoveringVideo = false
const videos = $state([])
let activeIndex = $state(0)
let isHoveringVideo = $state(false)
const isVideoCroppedInput$ = new Subject()
/** @type {import('rxjs').Subject<boolean>}*/
const isVideoCropped$ = isVideoCroppedInput$.pipe(
@ -78,7 +78,7 @@
})
</script>
<svelte:window on:resize={() => isVideoCroppedInput$.next(0)} />
<svelte:window onresize={() => isVideoCroppedInput$.next(0)} />
<section class="relative z-0 flex min-h-max w-full flex-col items-center py-20">
<div
@ -102,14 +102,15 @@
<div class="flex h-full flex-col gap-4">
{#each items as { icon, title, description }, index}
{@const isActive = index === activeIndex}
{@const SvelteComponent = icon}
<button
class={clsx(
'flex gap-3 rounded-xl px-4 py-4 outline-0 outline-cyan-400/50 transition-all sm:-ml-4',
isActive && 'bg-blue-300/5 shadow-md outline outline-1 backdrop-blur-sm '
)}
on:mouseenter={() => setActiveItem(index)}
onmouseenter={() => setActiveItem(index)}
>
<svelte:component this={icon} class="h-8 w-8 shrink-0 text-primary" />
<SvelteComponent class="h-8 w-8 shrink-0 text-primary" />
<p
class={clsx(
'txt-shadow_ text-left text-lg font-medium transition-colors ',
@ -153,7 +154,7 @@
>
{#if $isVideoCropped$}
<button
on:click={toggleVideoSlide}
onclick={toggleVideoSlide}
class:rotate-180={isHoveringVideo}
class="group absolute -left-6 top-1/2 z-50 rounded-full bg-blue-400/5 p-2 outline outline-white/10 backdrop-blur-sm transition-transform"
out:fade

View file

@ -2,13 +2,15 @@
import { inview } from 'svelte-inview'
import Video from '$lib/components/Video.svelte'
import { onMount } from 'svelte'
/** @type {{ [key: string]: any }} */
let { ...rest } = $props();
/** @type HTMLVideoElement */
let videoElement
let videoElement = $state()
let isVisible = false
let isManuallyPaused = false
let isChrome = false
let isVisible = $state(false)
let isManuallyPaused = $state(false)
let isChrome = $state(false)
onMount(() => {
isChrome = navigator.userAgent.toLowerCase().includes('chrome')
@ -21,18 +23,18 @@
})
</script>
<section class={$$restProps.class} class:isVisible>
<section class={rest.class} class:isVisible>
<div
class="wrapper"
role="banner"
use:inview={{ threshold: 0.5 }}
on:inview_enter={() => {
oninview_enter={() => {
isVisible = true
if (!isManuallyPaused && isChrome) {
videoElement.play().catch(() => {})
}
}}
on:inview_leave={() => {
oninview_leave={() => {
isVisible = false
isManuallyPaused = videoElement.paused
videoElement.pause()
@ -56,7 +58,7 @@
>
</div>
<div class="preview-rice-bg" aria="hidden" />
<div class="preview-rice-bg" aria="hidden"></div>
</section>
<style lang="postcss">

View file

@ -7,7 +7,11 @@
import Button from '$lib/components/Button.svelte'
import PatternBackground from '$lib/PatternBackground.svelte'
export let sponsors: SponsorsRanked
interface Props {
sponsors: SponsorsRanked
}
let { sponsors }: Props = $props()
const hasSponsors = Object.values(sponsors)
.filter((value) => !Array.isArray(value))
.every((array) => array.length === 0)
@ -18,13 +22,17 @@
{#if hasSponsors}
<div class="relative flex w-full flex-col items-center justify-center">
<PatternBackground
class="absolute -inset-12 inset-x-0 h-[100%] w-full text-slate-800 opacity-40"
class="absolute -inset-24 inset-x-0 h-[120%] w-full text-slate-800 opacity-40"
/>
<section class="relative mb-12 flex max-w-5xl flex-col gap-2 px-8 md:mb-20">
<Title class="px-0">
<TitlePre slot="pre">A huge thank you!</TitlePre>
<TitleHeading size="small" slot="title">Our sponsors</TitleHeading>
{#snippet pre()}
<TitlePre>A huge thank you!</TitlePre>
{/snippet}
{#snippet title()}
<TitleHeading size="small">Our sponsors</TitleHeading>
{/snippet}
</Title>
<div

View file

@ -1,18 +1,19 @@
<script lang="ts">
import DiscordProfilePicture from '$lib/components/DiscordProfilePicture.svelte'
export let biggestSize: number
export let getRestrictionElement: (() => HTMLElement) | undefined = undefined
interface Props {
biggestSize: number
getRestrictionElement?: (() => HTMLElement) | undefined
}
let { biggestSize, getRestrictionElement = undefined }: Props = $props()
const size = 90
let isDragging = false
let isHover = false
let image: string
$: image = isDragging
? 'imgs/chan/surprise.svg'
: isHover
? 'imgs/chan/wink.svg'
: 'imgs/chan/joy.svg'
let isDragging = $state(false)
let isHover = $state(false)
let image: string = $derived(
isDragging ? 'imgs/chan/surprise.svg' : isHover ? 'imgs/chan/wink.svg' : 'imgs/chan/joy.svg'
)
</script>
<div class="absolute z-20">
@ -23,8 +24,8 @@
weight={size / biggestSize}
{getRestrictionElement}
class={'bg-blue-300 outline-cyan-500'}
on:dragStart={() => (isDragging = true)}
on:dragEnd={() => (isDragging = false)}
on:hover={() => (isHover = true)}
ondragStart={() => (isDragging = true)}
ondragEnd={() => (isDragging = false)}
onhover={() => (isHover = true)}
></DiscordProfilePicture>
</div>

View file

@ -4,8 +4,12 @@
import { Subject, filter, first, map, merge, of, startWith, switchMap, timer } from 'rxjs'
import { onDestroy } from 'svelte'
export let biggestSize: number
export let getRestrictionElement: (() => HTMLElement) | undefined = undefined
interface Props {
biggestSize: number
getRestrictionElement?: (() => HTMLElement) | undefined
}
let { biggestSize, getRestrictionElement = undefined }: Props = $props()
const thePozArmy = Object.values(import.meta.glob('$lib/images/poz/*', { eager: true })).map(
(x) => x.default as string
@ -13,7 +17,7 @@
const size = 90
const origin = [710, 615] as const
let newPosition: readonly [number, number]
let newPosition: readonly [number, number] = $state()
const clicksTarget = 9
const shakeMax = 24
const clicksInput$ = new Subject()
@ -74,7 +78,7 @@
isAnimating={false}
tag="poz"
{getRestrictionElement}
on:enteredView={({ detail: { dragCoordinates } }) => {
onenteredView={({ detail: { dragCoordinates } }) => {
dragCoordinates.update(([x, y]) => {
x += lerp(400, 0, (size / maxSize) * (1 - Math.random())) * (Math.random() > 0.5 ? 1 : -1)
y += lerp(400, 0, (size / maxSize) * (1 - Math.random())) * (Math.random() > 0.5 ? 1 : -1)

View file

@ -6,10 +6,10 @@
import TitleSubtile from '$lib/components/Title/TitleSubtile.svelte'
import TitleHeading from '$lib/components/Title/TitleHeading.svelte'
export let data
let { data } = $props();
/** @type {HTMLElement} */
let asciiElement
let asciiElement = $state()
const { posts } = data
@ -68,14 +68,16 @@
<title>Hyprland News</title>
</svelte:head>
<div class="fancy-top-gradient" />
<div class="fancy-top-gradient"></div>
<section>
<header class="header">
<pre class="spinner-wrapper" bind:this={asciiElement} />
<pre class="spinner-wrapper" bind:this={asciiElement}></pre>
<Title>
<TitleHeading slot="title" class="">News</TitleHeading>
{#snippet title()}
<TitleHeading class="">News</TitleHeading>
{/snippet}
<TitleSubtile>Fresh updates straight from the oven</TitleSubtile>
</Title>
</header>

View file

@ -5,7 +5,7 @@
import NewsThumb from '$lib/components/news-thumb.svelte'
import clsx from 'clsx'
export let data
let { data } = $props();
</script>
<svelte:head>
@ -14,7 +14,7 @@
<meta property="og:title" content={data.meta.title} />
</svelte:head>
<div class="fancy-top-gradient fad animate-in" />
<div class="fancy-top-gradient fad animate-in"></div>
<article
class="mx-auto mt-navbar flex max-w-4xl flex-col gap-4 px-6 pt-20 md:gap-8 md:px-8 lg:mt-32 lg:gap-14"
@ -59,14 +59,16 @@
<div
class="prose prose-slate prose-invert transition-none delay-500 animate-in fade-in-0 fill-mode-backwards [animation-duration:2500ms] lg:prose-xl prose-a:text-cyan-400 prose-img:rounded-lg"
>
<svelte:component this={data.content} />
<data.content />
</div>
</article>
{#if data.other.length > 0}
<section class="mx-auto mt-72 max-w-screen-lg">
<Title class="mb-6">
<TitleHeading slot="title" class="">More news</TitleHeading>
{#snippet title()}
<TitleHeading class="">More news</TitleHeading>
{/snippet}
</Title>
<ul class="grid grid-cols-2 gap-x-7 gap-y-16">

View file

@ -10,7 +10,7 @@
import TitleHeading from '$lib/components/Title/TitleHeading.svelte'
import Button from '$lib/components/Button.svelte'
export let data
let { data } = $props();
const { plugins } = data
const featuredPlugins = plugins.filter(({ featured }) => featured).slice(0, 4)
@ -50,8 +50,12 @@
<header class="mt-24 flex flex-col items-center justify-center md:mt-32">
<Title>
<TitlePre slot="pre">Plugins</TitlePre>
<TitleHeading slot="title" class="">Unlock full power</TitleHeading>
{#snippet pre()}
<TitlePre >Plugins</TitlePre>
{/snippet}
{#snippet title()}
<TitleHeading class="">Unlock full power</TitleHeading>
{/snippet}
<TitleSubtile>Easily load up plugins and customize everything</TitleSubtile>
</Title>

View file

@ -6,22 +6,34 @@
// Dont forget to put this component inside of CardsContainer.svelte
// Also pass a hight class to the element, as otherwise the banner might not align properly,
// due to the parent container being unable to use height: full as there wont be a reference
export let plugin
export let color = undefined
export let showCategory = false
export let taglineMaxLength = 0
/**
* @typedef {Object} Props
* @property {any} plugin - due to the parent container being unable to use height: full as there wont be a reference
* @property {any} [color]
* @property {boolean} [showCategory]
* @property {number} [taglineMaxLength]
*/
/** @type {Props & { [key: string]: any }} */
let {
plugin,
color = undefined,
showCategory = false,
taglineMaxLength = 0,
...rest
} = $props();
/** @type {HTMLVideoElement}*/
let videoElement
let videoElement = $state()
</script>
<Card
on:mouseenter={() => videoElement && videoElement.play().catch(console.error)}
on:mouseleave={() => videoElement && videoElement.pause()}
{color}
class={$$restProps.class}
class={rest.class}
gradientOpacity={0.5}
><a
href={plugin.url}

View file

@ -1,6 +1,12 @@
<script>
/** @type {string}*/
export let tag
/**
* @typedef {Object} Props
* @property {string} tag
*/
/** @type {Props} */
let { tag } = $props();
const colors = {
Design: 'text-pink-200 bg-pink-500/15',

View file

@ -5,7 +5,7 @@
import Button from '$lib/components/Button.svelte'
import CloseIcon from '~icons/mingcute/close-line'
let kofiModal: HTMLDialogElement | undefined
let kofiModal: HTMLDialogElement | undefined = $state()
function lockScroll() {
document.querySelector('html')?.classList.add('lock-scroll')
@ -26,8 +26,12 @@
<section class="min-h-screen w-full md:mt-32">
<Title class="mb-4 md:mb-8">
<TitlePre slot="pre">Help continue Hyprland development</TitlePre>
<TitleHeading slot="title" class="">Support Hyprland</TitleHeading>
{#snippet pre()}
<TitlePre >Help continue Hyprland development</TitlePre>
{/snippet}
{#snippet title()}
<TitleHeading class="">Support Hyprland</TitleHeading>
{/snippet}
</Title>
<div
@ -101,7 +105,7 @@
<div class="relative h-[712px] min-h-[712px] shadow-2xl">
<form class="absolute -right-2 -top-4 z-10">
<button
on:click={() => {
onclick={() => {
unlockScroll()
}}
formmethod="dialog"

View file

@ -23,7 +23,7 @@ const mdsvexOptions = {
const config = {
extensions: ['.svelte', '.md'],
kit: { adapter: adapter() },
preprocess: [vitePreprocess(), mdsvex(mdsvexOptions)]
preprocess: [vitePreprocess({ script: true }), mdsvex(mdsvexOptions)]
}
export default config