Skip to content
Open
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
23 changes: 23 additions & 0 deletions app/components/Card.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<script setup lang="ts">
defineProps<{
/** Whether this is an exact match for the query */
isExactMatch?: boolean
}>()
</script>

<template>
<article
class="group bg-bg-subtle border border-border rounded-lg p-4 sm:p-6 transition-[border-color,background-color] duration-200 hover:(border-border-hover bg-bg-muted) cursor-pointer relative focus-within:outline-none focus-within:ring-2 focus-within:ring-offset-bg focus-within:ring-offset-2 focus-within:ring-fg/50 focus-within:bg-bg-muted focus-within:border-border-hover"
:class="{
'border-accent/30 bg-accent/5': isExactMatch,
}"
>
<!-- Glow effect for exact matches -->
<div
v-if="isExactMatch"
class="absolute -inset-px rounded-lg bg-gradient-to-r from-accent/0 via-accent/20 to-accent/0 opacity-100 blur-sm -z-1 pointer-events-none motion-reduce:opacity-50"
aria-hidden="true"
/>
<slot />
</article>
</template>
2 changes: 1 addition & 1 deletion app/components/Filter/Panel.vue
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,7 @@ const hasActiveFilters = computed(() => !!filterSummary.value)
:value="filters.text"
:placeholder="searchPlaceholder"
autocomplete="off"
class="input-base"
class="w-full bg-bg-subtle border border-border rounded-md px-4 py-3 font-mono text-sm text-fg placeholder:text-fg-subtle transition-all duration-200 focus:(border-fg/40 outline-none ring-1 ring-fg/10)"
@input="handleTextInput"
/>
</div>
Expand Down
17 changes: 4 additions & 13 deletions app/components/Package/Card.vue
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
<script setup lang="ts">
import Card from '../Card.vue'

const props = defineProps<{
/** The search result object containing package data */
result: NpmSearchResult
Expand Down Expand Up @@ -29,18 +31,7 @@ const pkgDescription = useMarkdown(() => ({
</script>

<template>
<article
class="group card-interactive scroll-mt-48 scroll-mb-6 relative focus-within:outline-none focus-within:ring-2 focus-within:ring-offset-bg focus-within:ring-offset-2 focus-within:ring-fg/50 focus-within:bg-bg-muted focus-within:border-border-hover"
:class="{
'border-accent/30 bg-accent/5': isExactMatch,
}"
>
<!-- Glow effect for exact matches -->
<div
v-if="isExactMatch"
class="absolute -inset-px rounded-lg bg-gradient-to-r from-accent/0 via-accent/20 to-accent/0 opacity-100 blur-sm -z-1 pointer-events-none motion-reduce:opacity-50"
aria-hidden="true"
/>
<Card :isExactMatch="isExactMatch">
<div class="mb-2 flex items-baseline justify-start gap-2">
<component
:is="headingLevel ?? 'h3'"
Expand Down Expand Up @@ -169,5 +160,5 @@ const pkgDescription = useMarkdown(() => ({
{{ keyword }}
</li>
</ul>
</article>
</Card>
</template>
110 changes: 56 additions & 54 deletions app/components/Package/Skeleton.vue
Original file line number Diff line number Diff line change
Expand Up @@ -12,20 +12,20 @@
<div class="flex-1 min-w-0">
<!-- Package name: h1 font-mono text-2xl sm:text-3xl font-medium mb-2 -->
<h1 class="font-mono text-2xl sm:text-3xl font-medium mb-2">
<span class="skeleton inline-block h-9 w-48" />
<SkeletonInline class="h-9 w-48" />
</h1>
<!-- Description: fixed height container matching min-h-[4.5rem] (72px) to prevent CLS -->
<div class="relative max-w-2xl min-h-[4.5rem]">
<div class="space-y-2">
<span class="skeleton block h-5 w-full" />
<span class="skeleton block h-5 w-4/5" />
<span class="skeleton block h-5 w-3/5" />
<SkeletonBlock class="h-5 w-full" />
<SkeletonBlock class="h-5 w-4/5" />
<SkeletonBlock class="h-5 w-3/5" />
</div>
</div>
</div>

<!-- Version badge: shrink-0 px-3 py-1 font-mono text-sm bg-bg-muted border border-border rounded-md -->
<span class="skeleton shrink-0 h-8 w-20 rounded-md" />
<SkeletonInline class="shrink-0 h-8 w-20 rounded-md" />
</div>

<!-- Stats grid: grid grid-cols-2 sm:grid-cols-3 md:grid-cols-6 gap-4 mt-6 -->
Expand All @@ -36,7 +36,7 @@
{{ $t('package.skeleton.license') }}
</dt>
<dd class="font-mono text-sm">
<span class="skeleton inline-block h-5 w-12" />
<SkeletonInline class="h-5 w-12" />
</dd>
</div>

Expand All @@ -46,7 +46,7 @@
{{ $t('package.skeleton.weekly') }}
</dt>
<dd class="font-mono text-sm">
<span class="skeleton inline-block h-5 w-20" />
<SkeletonInline class="h-5 w-20" />
</dd>
</div>

Expand All @@ -56,7 +56,7 @@
{{ $t('package.skeleton.size') }}
</dt>
<dd class="font-mono text-sm">
<span class="skeleton inline-block h-5 w-16" />
<SkeletonInline class="h-5 w-16" />
</dd>
</div>

Expand All @@ -66,7 +66,7 @@
{{ $t('package.skeleton.deps') }}
</dt>
<dd class="font-mono text-sm">
<span class="skeleton inline-block h-5 w-8" />
<SkeletonInline class="h-5 w-8" />
</dd>
</div>

Expand All @@ -76,7 +76,7 @@
{{ $t('package.skeleton.updated') }}
</dt>
<dd class="font-mono text-sm">
<span class="skeleton inline-block h-5 w-28" />
<SkeletonInline class="h-5 w-28" />
</dd>
</div>
</dl>
Expand All @@ -85,16 +85,16 @@
<nav aria-label="Package links" class="mt-6">
<ul class="flex flex-wrap items-center gap-4 list-none m-0 p-0">
<li>
<span class="skeleton inline-block h-5 w-14" />
<SkeletonInline class="h-5 w-14" />
</li>
<li>
<span class="skeleton inline-block h-5 w-20" />
<SkeletonInline class="h-5 w-20" />
</li>
<li>
<span class="skeleton inline-block h-5 w-16" />
<SkeletonInline class="h-5 w-16" />
</li>
<li>
<span class="skeleton inline-block h-5 w-12" />
<SkeletonInline class="h-5 w-12" />
</li>
</ul>
</nav>
Expand All @@ -110,10 +110,12 @@
</h2>
<!-- code-block with relative positioning for copy button -->
<div class="relative">
<div class="code-block pe-16">
<span class="skeleton inline-block h-5 w-52" />
<div
class="bg-bg-muted border border-border rounded-md p-4 font-mono text-sm overflow-x-auto pe-16"
>
<SkeletonInline class="h-5 w-52" />
</div>
<span class="skeleton absolute top-3 inset-ie-3 h-6 w-12 rounded" />
<SkeletonInline class="absolute top-3 inset-ie-3 h-6 w-12 rounded" />
</div>
</section>

Expand All @@ -131,20 +133,20 @@
<!-- Simulated README content -->
<div class="space-y-4">
<!-- Heading -->
<span class="skeleton block h-7 w-2/3" />
<SkeletonBlock class="h-7 w-2/3" />
<!-- Paragraphs -->
<span class="skeleton block h-4 w-full" />
<span class="skeleton block h-4 w-full" />
<span class="skeleton block h-4 w-4/5" />
<SkeletonBlock class="h-4 w-full" />
<SkeletonBlock class="h-4 w-full" />
<SkeletonBlock class="h-4 w-4/5" />
<!-- Gap for section break -->
<span class="skeleton block h-6 w-1/2 mt-6" />
<span class="skeleton block h-4 w-full" />
<span class="skeleton block h-4 w-full" />
<span class="skeleton block h-4 w-3/4" />
<SkeletonBlock class="h-6 w-1/2 mt-6" />
<SkeletonBlock class="h-4 w-full" />
<SkeletonBlock class="h-4 w-full" />
<SkeletonBlock class="h-4 w-3/4" />
<!-- Code block placeholder -->
<div class="skeleton h-24 w-full rounded-lg mt-4" />
<span class="skeleton block h-4 w-full" />
<span class="skeleton block h-4 w-5/6" />
<SkeletonBlock class="h-24 w-full rounded-lg mt-4" />
<SkeletonBlock class="h-4 w-full" />
<SkeletonBlock class="h-4 w-5/6" />
</div>
</section>
</div>
Expand All @@ -161,10 +163,10 @@
</h2>
<ul class="space-y-2 list-none m-0 p-0">
<li>
<span class="skeleton inline-block h-5 w-28" />
<SkeletonInline class="h-5 w-28" />
</li>
<li>
<span class="skeleton inline-block h-5 w-24" />
<SkeletonInline class="h-5 w-24" />
</li>
</ul>
</section>
Expand All @@ -179,12 +181,12 @@
</h2>
<!-- flex flex-wrap gap-1.5 -->
<ul class="flex flex-wrap gap-1.5 list-none m-0 p-0">
<li><span class="skeleton inline-block h-6 w-16 rounded" /></li>
<li><span class="skeleton inline-block h-6 w-12 rounded" /></li>
<li><span class="skeleton inline-block h-6 w-20 rounded" /></li>
<li><span class="skeleton inline-block h-6 w-14 rounded" /></li>
<li><span class="skeleton inline-block h-6 w-18 rounded" /></li>
<li><span class="skeleton inline-block h-6 w-10 rounded" /></li>
<li><SkeletonInline class="h-6 w-16 rounded" /></li>
<li><SkeletonInline class="h-6 w-12 rounded" /></li>
<li><SkeletonInline class="h-6 w-20 rounded" /></li>
<li><SkeletonInline class="h-6 w-14 rounded" /></li>
<li><SkeletonInline class="h-6 w-18 rounded" /></li>
<li><SkeletonInline class="h-6 w-10 rounded" /></li>
</ul>
</section>

Expand All @@ -199,24 +201,24 @@
<!-- space-y-1, each row: flex items-center justify-between py-1.5 text-sm -->
<div class="space-y-1">
<div class="flex items-center justify-between py-1.5 text-sm">
<span class="skeleton inline-block h-4 w-16" />
<span class="skeleton inline-block h-4 w-24" />
<SkeletonInline class="h-4 w-16" />
<SkeletonInline class="h-4 w-24" />
</div>
<div class="flex items-center justify-between py-1.5 text-sm">
<span class="skeleton inline-block h-4 w-14" />
<span class="skeleton inline-block h-4 w-24" />
<SkeletonInline class="h-4 w-14" />
<SkeletonInline class="h-4 w-24" />
</div>
<div class="flex items-center justify-between py-1.5 text-sm">
<span class="skeleton inline-block h-4 w-18" />
<span class="skeleton inline-block h-4 w-24" />
<SkeletonInline class="h-4 w-18" />
<SkeletonInline class="h-4 w-24" />
</div>
<div class="flex items-center justify-between py-1.5 text-sm">
<span class="skeleton inline-block h-4 w-14" />
<span class="skeleton inline-block h-4 w-24" />
<SkeletonInline class="h-4 w-14" />
<SkeletonInline class="h-4 w-24" />
</div>
<div class="flex items-center justify-between py-1.5 text-sm">
<span class="skeleton inline-block h-4 w-16" />
<span class="skeleton inline-block h-4 w-24" />
<SkeletonInline class="h-4 w-16" />
<SkeletonInline class="h-4 w-24" />
</div>
</div>
</section>
Expand All @@ -232,20 +234,20 @@
<!-- space-y-1, each: flex items-center justify-between py-1 text-sm -->
<ul class="space-y-1 list-none m-0 p-0">
<li class="flex items-center justify-between py-1 text-sm">
<span class="skeleton inline-block h-4 w-24" />
<span class="skeleton inline-block h-4 w-12" />
<SkeletonInline class="h-4 w-24" />
<SkeletonInline class="h-4 w-12" />
</li>
<li class="flex items-center justify-between py-1 text-sm">
<span class="skeleton inline-block h-4 w-32" />
<span class="skeleton inline-block h-4 w-10" />
<SkeletonInline class="h-4 w-32" />
<SkeletonInline class="h-4 w-10" />
</li>
<li class="flex items-center justify-between py-1 text-sm">
<span class="skeleton inline-block h-4 w-20" />
<span class="skeleton inline-block h-4 w-14" />
<SkeletonInline class="h-4 w-20" />
<SkeletonInline class="h-4 w-14" />
</li>
<li class="flex items-center justify-between py-1 text-sm">
<span class="skeleton inline-block h-4 w-28" />
<span class="skeleton inline-block h-4 w-12" />
<SkeletonInline class="h-4 w-28" />
<SkeletonInline class="h-4 w-12" />
</li>
</ul>
</section>
Expand Down
8 changes: 4 additions & 4 deletions app/components/Package/WeeklyDownloadStats.vue
Original file line number Diff line number Diff line change
Expand Up @@ -223,20 +223,20 @@ const config = computed(() => {
<div class="min-h-[75.195px]">
<!-- Title row: date range (24px height) -->
<div class="h-6 flex items-center ps-3">
<span class="skeleton h-3 w-36" />
<SkeletonInline class="h-3 w-36" />
</div>
<!-- Chart area: data label left, sparkline right -->
<div class="aspect-[500/80] flex items-center">
<!-- Data label (covers ~42% width) -->
<div class="w-[42%] flex items-center ps-0.5">
<span class="skeleton h-7 w-24" />
<SkeletonInline class="h-7 w-24" />
</div>
<!-- Sparkline area (~58% width) -->
<div class="flex-1 flex items-end gap-0.5 h-4/5 pe-3">
<span
<SkeletonInline
v-for="i in 16"
:key="i"
class="skeleton flex-1 rounded-sm"
class="flex-1 rounded-sm"
:style="{ height: `${25 + ((i * 7) % 50)}%` }"
/>
</div>
Expand Down
15 changes: 2 additions & 13 deletions app/components/SearchSuggestionCard.vue
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,7 @@ defineProps<{
</script>

<template>
<article
class="group card-interactive relative focus-within:outline-none focus-within:ring-2 focus-within:ring-offset-bg focus-within:ring-offset-2 focus-within:ring-fg/50 focus-within:bg-bg-muted focus-within:border-border-hover"
:class="{
'border-accent/30 bg-accent/5': isExactMatch,
}"
>
<!-- Glow effect for exact matches -->
<div
v-if="isExactMatch"
class="absolute -inset-px rounded-lg bg-gradient-to-r from-accent/0 via-accent/20 to-accent/0 opacity-100 blur-sm -z-1 pointer-events-none motion-reduce:opacity-50"
aria-hidden="true"
/>
<Card :isExactMatch="isExactMatch">
<NuxtLink
:to="type === 'user' ? `/~${name}` : `/@${name}`"
:data-suggestion-index="index"
Expand Down Expand Up @@ -72,5 +61,5 @@ defineProps<{
aria-hidden="true"
/>
</NuxtLink>
</article>
</Card>
</template>
2 changes: 1 addition & 1 deletion app/components/Settings/Toggle.server.vue
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ defineProps<{
<span v-if="label" class="text-sm text-fg font-medium text-start">
{{ label }}
</span>
<span class="skeleton block h-6 w-11 shrink-0 rounded-full" />
<SkeletonBlock class="h-6 w-11 shrink-0 rounded-full" />
</div>
<p v-if="description" class="text-sm text-fg-muted">
{{ description }}
Expand Down
10 changes: 10 additions & 0 deletions app/components/SkeletonBlock.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<!-- Vue component that only takes a class as prop and adds `bg-bg-elevated rounded animate-skeleton-pulse` to that class -->
<script setup lang="ts">
const props = defineProps<{
class?: string
}>()
</script>

<template>
<div :class="`bg-bg-elevated rounded animate-skeleton-pulse ${props.class ?? ''}`" />
</template>
12 changes: 12 additions & 0 deletions app/components/SkeletonInline.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<!-- Vue component that only takes a class as prop and adds `bg-bg-elevated rounded animate-skeleton-pulse` to that class -->
<script setup lang="ts">
const props = defineProps<{
class?: string
}>()
</script>

<template>
<span
:class="`inline-block bg-bg-elevated rounded animate-skeleton-pulse ${props.class ?? ''}`"
/>
</template>
Loading
Loading