[READ-ONLY] a fast, modern browser for the npm registry

fix(ui): improve MetricsBadges skeleton alignment (#929)

Co-authored-by: Daniel Roe <daniel@roe.dev>

authored by

Wojciech Maj
Daniel Roe
and committed by
GitHub
aecacb2d bf0c8f7a

+62 -30
+48 -18
app/components/Package/MetricsBadges.vue
··· 7 7 version?: string 8 8 }>() 9 9 10 - const { data: analysis } = usePackageAnalysis( 10 + const { data: analysis, status } = usePackageAnalysis( 11 11 () => props.packageName, 12 12 () => props.version, 13 13 ) 14 + 15 + const isLoading = computed(() => status.value !== 'error' && !analysis.value) 14 16 15 17 // ESM support 16 18 const hasEsm = computed(() => { ··· 52 54 </script> 53 55 54 56 <template> 55 - <ul v-if="analysis" class="flex items-center gap-1.5 list-none m-0 p-0"> 57 + <ul class="flex items-center gap-1.5 list-none m-0 p-0"> 56 58 <!-- TypeScript types badge --> 57 - <li v-if="!props.isBinary"> 59 + <li v-if="!props.isBinary" class="contents"> 58 60 <TooltipApp :text="typesTooltip"> 59 61 <component 60 62 :is="typesHref ? NuxtLink : 'span'" 61 63 :to="typesHref" 62 64 :tabindex="!typesHref ? 0 : undefined" 63 - class="inline-flex items-center gap-1 px-1.5 py-0.5 font-mono text-xs rounded transition-colors duration-200 focus-visible:(outline-2 outline-accent)" 65 + class="flex items-center gap-1 px-1.5 py-0.5 font-mono text-xs rounded transition-colors duration-200 focus-visible:(outline-2 outline-accent)" 64 66 :class="[ 65 - hasTypes 66 - ? 'text-fg-muted bg-bg-muted border border-border' 67 - : 'text-fg-subtle bg-bg-subtle border border-border-subtle', 67 + isLoading 68 + ? 'text-fg-subtle bg-bg-subtle border border-border-subtle' 69 + : hasTypes 70 + ? 'text-fg-muted bg-bg-muted border border-border' 71 + : 'text-fg-subtle bg-bg-subtle border border-border-subtle', 68 72 typesHref 69 73 ? 'hover:text-fg hover:border-border-hover focus-visible:outline-accent/70' 70 74 : '', 71 75 ]" 72 76 > 73 77 <span 78 + v-if="isLoading" 79 + class="i-carbon-circle-dash w-3 h-3 motion-safe:animate-spin" 80 + aria-hidden="true" 81 + /> 82 + <span 83 + v-else 74 84 class="w-3 h-3" 75 85 :class="hasTypes ? 'i-carbon-checkmark' : 'i-carbon-close'" 76 86 aria-hidden="true" ··· 81 91 </li> 82 92 83 93 <!-- ESM badge (show with X if missing) --> 84 - <li> 85 - <TooltipApp :text="hasEsm ? $t('package.metrics.esm') : $t('package.metrics.no_esm')"> 94 + <li class="contents"> 95 + <TooltipApp 96 + :text="isLoading ? '' : hasEsm ? $t('package.metrics.esm') : $t('package.metrics.no_esm')" 97 + > 86 98 <span 87 99 tabindex="0" 88 - class="inline-flex items-center gap-1 px-1.5 py-0.5 font-mono text-xs rounded transition-colors duration-200 focus-visible:(outline-2 outline-accent)" 100 + class="flex items-center gap-1 px-1.5 py-0.5 font-mono text-xs rounded transition-colors duration-200 focus-visible:(outline-2 outline-accent)" 89 101 :class=" 90 - hasEsm 91 - ? 'text-fg-muted bg-bg-muted border border-border' 92 - : 'text-fg-subtle bg-bg-subtle border border-border-subtle' 102 + isLoading 103 + ? 'text-fg-subtle bg-bg-subtle border border-border-subtle' 104 + : hasEsm 105 + ? 'text-fg-muted bg-bg-muted border border-border' 106 + : 'text-fg-subtle bg-bg-subtle border border-border-subtle' 93 107 " 94 108 > 95 109 <span 110 + v-if="isLoading" 111 + class="i-carbon-circle-dash w-3 h-3 motion-safe:animate-spin" 112 + aria-hidden="true" 113 + /> 114 + <span 115 + v-else 96 116 class="w-3 h-3" 97 117 :class="hasEsm ? 'i-carbon-checkmark' : 'i-carbon-close'" 98 118 aria-hidden="true" ··· 102 122 </TooltipApp> 103 123 </li> 104 124 105 - <!-- CJS badge (only show if present) --> 106 - <li v-if="hasCjs"> 107 - <TooltipApp :text="$t('package.metrics.cjs')"> 125 + <!-- CJS badge --> 126 + <li v-if="isLoading || hasCjs" class="contents"> 127 + <TooltipApp :text="isLoading ? '' : $t('package.metrics.cjs')"> 108 128 <span 109 129 tabindex="0" 110 - class="inline-flex items-center gap-1 px-1.5 py-0.5 font-mono text-xs text-fg-muted bg-bg-muted border border-border rounded transition-colors duration-200 focus-visible:(outline-2 outline-accent)" 130 + class="flex items-center gap-1 px-1.5 py-0.5 font-mono text-xs rounded transition-colors duration-200 focus-visible:(outline-2 outline-accent)" 131 + :class=" 132 + isLoading 133 + ? 'text-fg-subtle bg-bg-subtle border border-border-subtle' 134 + : 'text-fg-muted bg-bg-muted border border-border' 135 + " 111 136 > 112 - <span class="i-carbon-checkmark w-3 h-3" aria-hidden="true" /> 137 + <span 138 + v-if="isLoading" 139 + class="i-carbon-circle-dash w-3 h-3 motion-safe:animate-spin" 140 + aria-hidden="true" 141 + /> 142 + <span v-else class="i-carbon-checkmark w-3 h-3" aria-hidden="true" /> 113 143 CJS 114 144 </span> 115 145 </TooltipApp>
+14 -12
app/pages/package/[...package].vue
··· 585 585 </span> 586 586 587 587 <!-- Package metrics (module format, types) --> 588 - <ClientOnly> 589 - <div class="flex gap-2 sm:gap-3 flex-wrap"> 588 + <div class="flex gap-2 sm:gap-3 flex-wrap"> 589 + <ClientOnly> 590 590 <PackageMetricsBadges 591 591 v-if="resolvedVersion" 592 592 :package-name="pkg.name" ··· 613 613 /> 614 614 <span>{{ formatCompactNumber(likesData?.totalLikes ?? 0, { decimals: 1 }) }}</span> 615 615 </button> 616 - </div> 617 - 618 - <template #fallback> 619 - <div class="flex items-center gap-1.5 self-baseline ms-1 sm:ms-2"> 620 - <SkeletonBlock class="w-8 h-5 rounded" /> 621 - <SkeletonBlock class="w-12 h-5 rounded" /> 622 - <SkeletonBlock class="w-5 h-5 rounded" /> 623 - </div> 624 - </template> 625 - </ClientOnly> 616 + <template #fallback> 617 + <div 618 + class="flex items-center gap-1.5 list-none m-0 p-0 relative top-[5px] self-baseline ms-1 sm:ms-2" 619 + > 620 + <SkeletonBlock class="w-16 h-5.5 rounded" /> 621 + <SkeletonBlock class="w-13 h-5.5 rounded" /> 622 + <SkeletonBlock class="w-13 h-5.5 rounded" /> 623 + <SkeletonBlock class="w-13 h-5.5 rounded bg-bg-subtle" /> 624 + </div> 625 + </template> 626 + </ClientOnly> 627 + </div> 626 628 627 629 <!-- Internal navigation: Docs + Code + Compare (hidden on mobile, shown in external links instead) --> 628 630 <nav