fix(collections): make per-card remove buttons usable on touch
The remove buttons on a collection's cards were revealed only on hover / focus-within (opacity 0 → 1), so on touch they were invisible and undiscoverable. Show them persistently at the 640px breakpoint and enlarge the hit area from 24px to 32px (kept below the full 44px floor because the button floats over a 4-up cover thumbnail). Test pins the CSS contract (jsdom can't evaluate @media): the mobile block makes `.remove` opaque. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -406,6 +406,19 @@
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
/* Touch has no hover, so the reveal-on-hover treatment leaves the
|
||||
remove buttons invisible and undiscoverable. Show them persistently
|
||||
and enlarge the hit area on phones. (Capped well below 44px because
|
||||
the button floats over a 4-up cover thumbnail — a full touch-floor
|
||||
square would swallow the cover.) */
|
||||
@media (max-width: 640px) {
|
||||
.remove {
|
||||
opacity: 1;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
}
|
||||
}
|
||||
|
||||
.icon-btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
|
||||
49
frontend/src/routes/collections/[id]/remove-touch.test.ts
Normal file
49
frontend/src/routes/collections/[id]/remove-touch.test.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { readFileSync } from 'node:fs';
|
||||
import { resolve } from 'node:path';
|
||||
|
||||
// The per-card "remove" buttons on a collection are revealed on hover /
|
||||
// focus-within (opacity 0 → 1), which leaves them invisible and
|
||||
// undiscoverable on touch. They must be shown persistently at the mobile
|
||||
// breakpoint. jsdom can't evaluate @media or layout, so this pins the CSS
|
||||
// contract: the 640px block makes `.remove` opaque. (Same approach as the
|
||||
// shared touch-target contract test.)
|
||||
|
||||
const SRC = readFileSync(
|
||||
resolve(process.cwd(), 'src/routes/collections/[id]/+page.svelte'),
|
||||
'utf8'
|
||||
);
|
||||
|
||||
// Brace-balanced extraction of every `@media (max-width: 640px) { ... }` body.
|
||||
function mobileBlocks(css: string): string {
|
||||
const marker = '@media (max-width: 640px)';
|
||||
let out = '';
|
||||
let from = 0;
|
||||
for (;;) {
|
||||
const start = css.indexOf(marker, from);
|
||||
if (start === -1) break;
|
||||
const open = css.indexOf('{', start);
|
||||
if (open === -1) break;
|
||||
let depth = 0;
|
||||
let i = open;
|
||||
for (; i < css.length; i++) {
|
||||
if (css[i] === '{') depth++;
|
||||
else if (css[i] === '}') {
|
||||
depth--;
|
||||
if (depth === 0) break;
|
||||
}
|
||||
}
|
||||
out += css.slice(open + 1, i) + '\n';
|
||||
from = i + 1;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
const squish = (s: string) => s.replace(/\s+/g, ' ');
|
||||
|
||||
describe('collection remove buttons on touch', () => {
|
||||
it('reveals the remove buttons persistently at the mobile breakpoint', () => {
|
||||
const mobile = squish(mobileBlocks(SRC));
|
||||
expect(mobile).toMatch(/\.remove[^}]*opacity:\s*1/);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user