diff --git a/apps/viewer/src/__tests__/ad-carousel.spec.ts b/apps/viewer/src/__tests__/ad-carousel.spec.ts
index 3896599..01ecf81 100644
--- a/apps/viewer/src/__tests__/ad-carousel.spec.ts
+++ b/apps/viewer/src/__tests__/ad-carousel.spec.ts
@@ -56,9 +56,13 @@ describe('AdCarousel', () => {
const dots = wrapper.findAll('.ad-dots button');
expect(dots.length).toBe(3);
+ expect(dots[0].attributes('aria-current')).toBe('true'); // 当前项程序化状态
+ expect(dots[1].attributes('aria-current')).toBeUndefined();
await dots[1].trigger('click');
expect(currentSrc(wrapper)).toBe('https://ad.test/b.jpg');
- expect(dots[1].classes()).toContain('ad-dot--active');
+ expect(dots[1].classes()).toContain('active');
+ expect(dots[1].attributes('aria-current')).toBe('true');
+ expect(dots[0].attributes('aria-current')).toBeUndefined();
});
it('emits close when the ✕ button is clicked', async () => {
diff --git a/apps/viewer/src/components/ads/AdCarousel.vue b/apps/viewer/src/components/ads/AdCarousel.vue
index 2111e2e..a15139c 100644
--- a/apps/viewer/src/components/ads/AdCarousel.vue
+++ b/apps/viewer/src/components/ads/AdCarousel.vue
@@ -108,7 +108,8 @@ function openAd(ad: PublicAd): void {
@@ -127,6 +128,9 @@ function openAd(ad: PublicAd): void {
border-radius: var(--viewer-radius-img);
background: var(--viewer-dark-bg); /* 图像加载期的暗色衬底 */
font-family: var(--viewer-font-family);
+ /* 水平翻页手势自持(touchstart/end 判定),纵向滚动交还页面——
+ * ThumbnailStrip pan-x 的对偶(pan-y)。 */
+ touch-action: pan-y;
}
.ad-slide {
display: block;
@@ -191,7 +195,15 @@ function openAd(ad: PublicAd): void {
border-radius: 50%;
background: color-mix(in srgb, var(--viewer-surface) 55%, transparent);
}
-.ad-dot--active::before {
+.ad-dot.active::before {
background: var(--viewer-surface);
}
+/* 键盘焦点指示(AlbumHeader/ThumbnailStrip 同规:2px 主色描边)。主色对
+ * 暗色衬底/color-mix 蒙层对比 ≈4:1(非文本 ≥3:1)。 */
+.ad-slide:focus-visible,
+.ad-close:focus-visible,
+.ad-dot:focus-visible {
+ outline: 2px solid var(--viewer-primary);
+ outline-offset: 1px;
+}
diff --git a/apps/viewer/src/pages/ActivityPage.vue b/apps/viewer/src/pages/ActivityPage.vue
index 3d69d19..7a90f89 100644
--- a/apps/viewer/src/pages/ActivityPage.vue
+++ b/apps/viewer/src/pages/ActivityPage.vue
@@ -77,7 +77,7 @@ async function load(): Promise {
// 广告独立拉取(Task 10):失败静默(广告缺席不阻塞相册浏览);会话级
// 关闭记忆先行(命中则连请求都省)。沿用 sequence 防跨相册竞态落地。
ads.value = [];
- if (sessionStorage.getItem('viewer:ads-dismissed') !== '1') {
+ if (sessionStorage.getItem('viewer.adsDismissed') !== '1') {
getPublicAds(token.value)
.then((result) => {
if (sequence !== loadSequence) return;
@@ -122,7 +122,7 @@ function selectCategory(categoryId: string | null): void {
/** 关闭广告(Task 10):会话级记忆(本次会话不再拉取/渲染),组件只 emit。 */
function dismissAds(): void {
- sessionStorage.setItem('viewer:ads-dismissed', '1');
+ sessionStorage.setItem('viewer.adsDismissed', '1');
ads.value = [];
}