Skip to content

Commit 7dbca4b

Browse files
Adebesin-Cellclaude
andcommitted
fix: replace anchor tags with ButtonBase for SVG downloads and add loading spinners
Use ButtonBase consistently for all download buttons, add spinner loading states to SVG download buttons, create light-mode wordmark SVG, and ensure light variant downloads use the correct srcLight file. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent dc799b9 commit 7dbca4b

File tree

3 files changed

+51
-16
lines changed

3 files changed

+51
-16
lines changed

app/components/Brand/Customize.vue

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,12 +54,19 @@ function getCustomSvgString(): string {
5454
return new XMLSerializer().serializeToString(clone)
5555
}
5656
57+
const svgLoading = ref(false)
58+
5759
function downloadCustomSvg() {
5860
const svg = getCustomSvgString()
5961
if (!svg) return
60-
61-
const blob = new Blob([svg], { type: 'image/svg+xml' })
62-
downloadFile(blob, `npmx-logo-${activeAccentId.value}.svg`)
62+
svgLoading.value = true
63+
try {
64+
const blob = new Blob([svg], { type: 'image/svg+xml' })
65+
downloadFile(blob, `npmx-logo-${activeAccentId.value}.svg`)
66+
}
67+
finally {
68+
svgLoading.value = false
69+
}
6370
}
6471
6572
const pngLoading = ref(false)
@@ -202,10 +209,15 @@ async function downloadCustomPng() {
202209
<div class="flex items-center gap-2">
203210
<ButtonBase
204211
size="sm"
205-
classicon="i-lucide:download"
206212
:aria-label="$t('brand.customize.download_svg_aria')"
213+
:disabled="svgLoading"
207214
@click="downloadCustomSvg"
208215
>
216+
<span
217+
class="size-[1em]"
218+
aria-hidden="true"
219+
:class="svgLoading ? 'i-lucide:loader-circle animate-spin' : 'i-lucide:download'"
220+
/>
209221
{{ $t('brand.logos.download_svg') }}
210222
</ButtonBase>
211223
<ButtonBase

app/pages/brand.vue

Lines changed: 33 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ const logos = [
2020
{
2121
name: () => $t('brand.logos.wordmark'),
2222
src: '/logo.svg',
23+
srcLight: '/logo-light.svg',
2324
altDark: () => $t('brand.logos.wordmark_alt'),
2425
altLight: () => $t('brand.logos.wordmark_light_alt'),
2526
width: 602,
@@ -40,8 +41,20 @@ const logos = [
4041
4142
const typographySizes = ['text-2xl', 'text-xl', 'text-lg', 'text-base', 'text-sm']
4243
44+
const svgLoading = ref(new Set<string>())
4345
const pngLoading = ref(new Set<string>())
4446
47+
function handleSvgDownload(src: string) {
48+
if (svgLoading.value.has(src)) return
49+
svgLoading.value.add(src)
50+
try {
51+
downloadFileLink(src, src.replace('/', ''))
52+
}
53+
finally {
54+
svgLoading.value.delete(src)
55+
}
56+
}
57+
4558
async function handlePngDownload(logo: (typeof logos)[number]) {
4659
if (pngLoading.value.has(logo.src)) return
4760
pngLoading.value.add(logo.src)
@@ -111,19 +124,23 @@ async function handlePngDownload(logo: (typeof logos)[number]) {
111124
$t('brand.logos.on_dark')
112125
}}</span>
113126
<div class="flex items-center gap-2">
114-
<a
115-
:href="logo.src"
116-
:download="logo.src.replace('/', '')"
117-
class="inline-flex items-center gap-1 text-xs font-mono text-fg-muted border border-border rounded-md px-2 py-0.5 hover:bg-bg-muted hover:text-fg transition-colors duration-200 no-underline focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-2 focus-visible:ring-offset-bg"
127+
<ButtonBase
128+
size="sm"
118129
:aria-label="
119130
$t('brand.logos.download_svg_aria', {
120131
name: `${logo.name()} (${$t('brand.logos.on_dark')})`,
121132
})
122133
"
134+
:disabled="svgLoading.has(logo.src)"
135+
@click="handleSvgDownload(logo.src)"
123136
>
124-
<span class="i-lucide:download w-3.5 h-3.5" aria-hidden="true" />
137+
<span
138+
class="size-[1em]"
139+
aria-hidden="true"
140+
:class="svgLoading.has(logo.src) ? 'i-lucide:loader-circle animate-spin' : 'i-lucide:download'"
141+
/>
125142
{{ $t('brand.logos.download_svg') }}
126-
</a>
143+
</ButtonBase>
127144
<ButtonBase
128145
size="sm"
129146
:aria-label="
@@ -173,19 +190,23 @@ async function handlePngDownload(logo: (typeof logos)[number]) {
173190
$t('brand.logos.on_light')
174191
}}</span>
175192
<div class="flex items-center gap-2">
176-
<a
177-
:href="logo.src"
178-
:download="logo.src.replace('/', '')"
179-
class="inline-flex items-center gap-1 text-xs font-mono text-fg-muted border border-border rounded-md px-2 py-0.5 hover:bg-bg-muted hover:text-fg transition-colors duration-200 no-underline focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-2 focus-visible:ring-offset-bg"
193+
<ButtonBase
194+
size="sm"
180195
:aria-label="
181196
$t('brand.logos.download_svg_aria', {
182197
name: `${logo.name()} (${$t('brand.logos.on_light')})`,
183198
})
184199
"
200+
:disabled="svgLoading.has(logo.srcLight ?? logo.src)"
201+
@click="handleSvgDownload(logo.srcLight ?? logo.src)"
185202
>
186-
<span class="i-lucide:download w-3.5 h-3.5" aria-hidden="true" />
203+
<span
204+
class="size-[1em]"
205+
aria-hidden="true"
206+
:class="svgLoading.has(logo.srcLight ?? logo.src) ? 'i-lucide:loader-circle animate-spin' : 'i-lucide:download'"
207+
/>
187208
{{ $t('brand.logos.download_svg') }}
188-
</a>
209+
</ButtonBase>
189210
<ButtonBase
190211
size="sm"
191212
:aria-label="

public/logo-light.svg

Lines changed: 2 additions & 0 deletions
Loading

0 commit comments

Comments
 (0)