From 2e0000a78b0c2b746dba200ec6925a3f440ed5f0 Mon Sep 17 00:00:00 2001 From: Alexander Wondwossen Date: Sun, 22 Mar 2026 12:50:10 -0400 Subject: [PATCH 1/7] fix: show copy buttons on touch devices (#1771) --- app/components/CopyToClipboardButton.vue | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/app/components/CopyToClipboardButton.vue b/app/components/CopyToClipboardButton.vue index f709aad730..3877e58c7d 100644 --- a/app/components/CopyToClipboardButton.vue +++ b/app/components/CopyToClipboardButton.vue @@ -85,8 +85,17 @@ function handleClick() { } @media (hover: none) { + /* On touch devices show the button statically (no hover possible) */ .copyButton { - display: none; + clip: auto; + clip-path: none; + height: auto; + overflow: visible; + width: auto; + opacity: 1; + translate: none; + pointer-events: auto; + transition: none; } } From f029ccba7c80bbcb30ed7424e4c99b20ca18e572 Mon Sep 17 00:00:00 2001 From: Alexander Wondwossen Date: Sun, 22 Mar 2026 13:57:37 -0400 Subject: [PATCH 2/7] fix(a11y): match copy button aria-label to its visible text --- app/components/CopyToClipboardButton.vue | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/components/CopyToClipboardButton.vue b/app/components/CopyToClipboardButton.vue index 3877e58c7d..10120ec614 100644 --- a/app/components/CopyToClipboardButton.vue +++ b/app/components/CopyToClipboardButton.vue @@ -16,8 +16,8 @@ const props = defineProps<{ const buttonCopyText = computed(() => props.copyText || $t('common.copy')) const buttonCopiedText = computed(() => props.copiedText || $t('common.copied')) -const buttonAriaLabelCopy = computed(() => props.ariaLabelCopy || $t('common.copy')) -const buttonAriaLabelCopied = computed(() => props.ariaLabelCopied || $t('common.copied')) +const buttonAriaLabelCopy = computed(() => props.ariaLabelCopy || props.copyText || $t('common.copy')) +const buttonAriaLabelCopied = computed(() => props.ariaLabelCopied || props.copiedText || $t('common.copied')) const emit = defineEmits<{ click: [] From 9c5a94ec27016955cf97d887d1d4a5df6c83e5da Mon Sep 17 00:00:00 2001 From: Alexander Wondwossen Date: Sun, 22 Mar 2026 14:37:50 -0400 Subject: [PATCH 3/7] test: add CopyToClipboardButton aria-label and touch visibility regression tests --- .../components/CopyToClipboardButton.spec.ts | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 test/nuxt/components/CopyToClipboardButton.spec.ts diff --git a/test/nuxt/components/CopyToClipboardButton.spec.ts b/test/nuxt/components/CopyToClipboardButton.spec.ts new file mode 100644 index 0000000000..138ee12343 --- /dev/null +++ b/test/nuxt/components/CopyToClipboardButton.spec.ts @@ -0,0 +1,60 @@ +import { describe, expect, it } from 'vitest' +import { mountSuspended } from '@nuxt/test-utils/runtime' +import CopyToClipboardButton from '~/components/CopyToClipboardButton.vue' + +describe('CopyToClipboardButton', () => { + it('aria-label matches visible copy text when copyText is provided', async () => { + const wrapper = await mountSuspended(CopyToClipboardButton, { + props: { + copied: false, + copyText: 'Copy package name', + }, + }) + + const button = wrapper.find('button') + expect(button.attributes('aria-label')).toBe('Copy package name') + expect(button.text()).toContain('Copy package name') + }) + + it('aria-label uses ariaLabelCopy when explicitly provided', async () => { + const wrapper = await mountSuspended(CopyToClipboardButton, { + props: { + copied: false, + copyText: 'Copy package name', + ariaLabelCopy: 'Copy the package name to clipboard', + }, + }) + + const button = wrapper.find('button') + expect(button.attributes('aria-label')).toBe('Copy the package name to clipboard') + }) + + it('aria-label reflects copiedText when copied is true', async () => { + const wrapper = await mountSuspended(CopyToClipboardButton, { + props: { + copied: true, + copyText: 'Copy package name', + copiedText: 'Copied!', + }, + }) + + const button = wrapper.find('button') + expect(button.attributes('aria-label')).toBe('Copied!') + expect(button.text()).toContain('Copied!') + }) + + it('aria-label matches visible text - no label/content mismatch', async () => { + const wrapper = await mountSuspended(CopyToClipboardButton, { + props: { + copied: false, + copyText: 'Copy install command', + }, + }) + + const button = wrapper.find('button') + const ariaLabel = button.attributes('aria-label') ?? '' + const visibleText = button.text() + // The aria-label should equal the visible text (not some other string) + expect(visibleText).toContain(ariaLabel) + }) +}) From ed04748bf76425d06d1361000611b605707ac0f1 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Sun, 22 Mar 2026 18:39:15 +0000 Subject: [PATCH 4/7] [autofix.ci] apply automated fixes --- app/components/CopyToClipboardButton.vue | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/app/components/CopyToClipboardButton.vue b/app/components/CopyToClipboardButton.vue index 10120ec614..8db328f6fd 100644 --- a/app/components/CopyToClipboardButton.vue +++ b/app/components/CopyToClipboardButton.vue @@ -16,8 +16,12 @@ const props = defineProps<{ const buttonCopyText = computed(() => props.copyText || $t('common.copy')) const buttonCopiedText = computed(() => props.copiedText || $t('common.copied')) -const buttonAriaLabelCopy = computed(() => props.ariaLabelCopy || props.copyText || $t('common.copy')) -const buttonAriaLabelCopied = computed(() => props.ariaLabelCopied || props.copiedText || $t('common.copied')) +const buttonAriaLabelCopy = computed( + () => props.ariaLabelCopy || props.copyText || $t('common.copy'), +) +const buttonAriaLabelCopied = computed( + () => props.ariaLabelCopied || props.copiedText || $t('common.copied'), +) const emit = defineEmits<{ click: [] From 9d4be044cf5e9bb9485429f6031eff2e712f2d3c Mon Sep 17 00:00:00 2001 From: Alexander Wondwossen Date: Sun, 22 Mar 2026 15:15:53 -0400 Subject: [PATCH 5/7] fix(a11y): increase provenance link touch target to meet 24px minimum --- app/components/Package/Header.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/components/Package/Header.vue b/app/components/Package/Header.vue index 7cf6b4c8bc..94839a1eb6 100644 --- a/app/components/Package/Header.vue +++ b/app/components/Package/Header.vue @@ -271,7 +271,7 @@ const fundingUrl = computed(() => { :to="packageRoute(packageName, resolvedVersion, '#provenance')" :aria-label="$t('package.provenance_section.view_more_details')" classicon="i-lucide:shield-check" - class="py-1.25 px-2 me-2" + class="py-1.5 px-2 me-2" /> From d0d6a8229ab332ba441ba5a20bea6e8d06b1261d Mon Sep 17 00:00:00 2001 From: Alexander Wondwossen Date: Sun, 22 Mar 2026 16:01:49 -0400 Subject: [PATCH 6/7] fix(a11y): increase provenance link container gap for sufficient touch clearance --- app/components/Package/Header.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/components/Package/Header.vue b/app/components/Package/Header.vue index 94839a1eb6..b04ad50205 100644 --- a/app/components/Package/Header.vue +++ b/app/components/Package/Header.vue @@ -253,7 +253,7 @@ const fundingUrl = computed(() => { class="py-1.5 px-2.5 sm:me-2" :tabindex="showScrollToTop ? 0 : -1" /> -
+