Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
160 changes: 10 additions & 150 deletions apps/svelte.dev/src/routes/packages/Category.svelte
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
<script lang="ts">
import type { Package } from '$lib/server/content';
import { prefersReducedMotion } from 'svelte/motion';
import PackageCard from './PackageCard.svelte';

interface Props {
Expand All @@ -10,83 +9,20 @@
}

let { title, description, packages }: Props = $props();

let content: HTMLElement;
let scroller: HTMLElement;

let behavior = $derived<ScrollBehavior>(prefersReducedMotion.current ? 'instant' : 'smooth');

let at_start = $state(true);
let at_end = $state(true);

function update() {
at_start = scroller.scrollLeft === 0;
at_end = scroller.scrollLeft + scroller.offsetWidth >= scroller.scrollWidth;
}

function go(d: number) {
const [a, b] = scroller.querySelectorAll('.item') as NodeListOf<HTMLElement>;
const left = scroller.scrollLeft + d * (b.offsetLeft - a.offsetLeft);

scroller.scrollTo({ left, behavior });
}

$effect(update);
</script>

<svelte:window onresize={update} />

<section class="category">
<header>
<h2>
{title}
</h2>

{#if !at_start || !at_end}
<div class="controls">
<button disabled={at_start} aria-label="Previous" class="raised icon" onclick={() => go(-1)}
></button>

<button disabled={at_end} aria-label="Next" class="raised icon" onclick={() => go(1)}
></button>
</div>
{/if}
<h2>{title}</h2>
</header>
{#if description}
<h3>{@html description}</h3>
{/if}

<div class="wrapper">
<!-- we duplicate the DOM for the sake of the gradient effect -
without this, the scrollbar extends beyond the content area -->
<div inert class="viewport">
<div bind:this={content} class="content">
{#each packages as pkg}
<div class="item">
<PackageCard {pkg} />
</div>
{/each}
</div>
</div>

<div
bind:this={scroller}
class="viewport"
onscroll={(e) => {
const left = e.currentTarget.scrollLeft;
content.style.translate = `-${left}px`;

update();
}}
>
<div class="content">
{#each packages as pkg}
<div class="item">
<PackageCard {pkg} />
</div>
{/each}
</div>
</div>
<div class="content">
{#each packages as pkg}
<PackageCard {pkg} />
{/each}
</div>
</section>

Expand All @@ -97,99 +33,23 @@
}

header {
display: flex;
margin-bottom: 1rem;
align-items: center;
gap: 2rem;

h2 {
flex: 1;
}

.controls {
display: flex;
gap: 0.5rem;
}

button {
background: var(--sk-bg-3);

&::after {
content: '';
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
background: currentColor;
mask: url(icons/chevron) 50% 50% no-repeat;
mask-size: 2rem 2rem;
}

&[aria-label='Next']::after {
rotate: 180deg;
}

&:disabled {
background: none;
}
margin: 0;
}
}

h3 {
font: var(--sk-font-ui-medium);
font-size: 1.5rem;
}

.wrapper {
position: relative;
}

.viewport {
overscroll-behavior-x: contain;
overscroll-behavior-y: auto;
scroll-snap-type: x mandatory;

&[inert] {
position: relative;
margin: 0 calc(-1 * var(--bleed));
padding: 1rem var(--bleed);
scroll-padding: 0 var(--bleed);
overflow: hidden;
filter: blur(0.5px);
mask-image: linear-gradient(
to right,
rgb(0 0 0 / 0) 0%,
rgb(0 0 0 / 0.5) var(--bleed),
rgb(0 0 0 / 0) var(--bleed),
rgb(0 0 0 / 0) calc(100% - var(--bleed)),
rgb(0 0 0 / 0.5) calc(100% - var(--bleed)),
rgb(0 0 0 / 0) 100%
);
}

&:not([inert]) {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
overflow-x: auto;
overflow-y: visible;
margin: 1rem 0;
}
margin-bottom: 0.5rem;
color: var(--sk-fg-2);
}

.content {
display: grid;
grid-auto-columns: 34rem;
grid-auto-flow: column;
gap: 2rem;
width: fit-content;
}

.item {
height: 16rem;
scroll-snap-align: start;
grid-template-columns: repeat(auto-fill, minmax(28rem, 1fr));
gap: 1.5rem;
}
</style>
5 changes: 3 additions & 2 deletions apps/svelte.dev/src/routes/packages/PackageCard.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,6 @@
<style>
header {
display: flex;
align-items: center;
margin-bottom: 1rem;
justify-content: space-between;
gap: 1.5rem;
Expand All @@ -110,6 +109,8 @@
gap: 0.8rem;
font: var(--sk-font-ui-small);
color: var(--sk-fg-3);
white-space: nowrap;
margin-top: 0.2rem;

strong {
color: var(--sk-fg-1);
Expand All @@ -136,7 +137,7 @@
border-radius: var(--sk-border-radius);
padding: 1rem;

min-height: 16em;
min-height: 14em;

&:hover {
filter: drop-shadow(0.2rem, 0.4rem, 1rem rgb(0 0 0 / 0.1));
Expand Down