Reduced motion, touch hover, overscroll and artwork edges (#310 #311 #312 #313 #314 #315)

Fixes from the UI-polish review of the main window (2026-10-19).

Reduced motion took every animation away, and a turning icon was the only sign a check was
running. The refresh buttons now carry aria-busy and a "Checking for new items" label while
checking, and under reduced motion spinners pulse and toasts and sheets fade.

Every :hover rule is behind @media (hover:hover), since Safari keeps :hover on the last thing
tapped; the two phone resets that undid it for .btn and .ep had nothing left to do and are gone.
An item row's actions still show on hover without the query, so a tablet with no pointer can
reach them by tapping the row.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-20 15:16:41 +00:00
parent 6ec2c35291
commit 0696583547
4 changed files with 93 additions and 47 deletions

View File

@@ -60,7 +60,16 @@ function paintScanning(){
if(on) row.title='Checking for new items…'; else row.removeAttribute('title');
}
// A feed's button turns on its own page only, wherever the person has gone since pressing it.
document.body.classList.toggle('scan-this',spinFeed!==null&&spinFeed===S.feed);
const self=spinFeed!==null&&spinFeed===S.feed, any=document.body.classList.contains('scan-any');
document.body.classList.toggle('scan-this',self);
// Turning was all that said a check was running, and with motion reduced nothing turns (#310).
for(const b of $$('#scanAll,.fhead [data-a=scanall],.fhead [data-a=scan]')){
const on=b.dataset.a==='scan'?self:any;
b.dataset.label??=b.getAttribute('aria-label')!;
if(on) b.setAttribute('aria-busy','true'); else b.removeAttribute('aria-busy');
b.title=on?'Checking for new items…':b.dataset.label;
b.setAttribute('aria-label',b.title);
}
}
/// The refresh buttons turn from the press until that scan's scan_done (#78, #233). On <body>,
/// because a feed's page is redrawn as its items come in and would take a class on the button
@@ -68,15 +77,16 @@ function paintScanning(){
/// between one feed and the next, many times a second (#231).
let spinFeed: string|null=null;
function spinStart(feed: string|null){
if(feed===null) document.body.classList.add('scan-any');
else { spinFeed=feed; paintScanning(); }
if(feed===null) document.body.classList.add('scan-any'); else spinFeed=feed;
paintScanning();
}
/// `only` and `forced` from scan_done: a scheduled scan, or one limited to another feed, leaves
/// a pressed button turning, since the scan it asked for is still queued behind it. Nothing
/// (an error ended the job) stops them all.
function spinEnd(done?: {only: string|null, forced: boolean}){
if(!done||done.forced&&done.only==null) document.body.classList.remove('scan-any');
if(!done||done.forced&&done.only===spinFeed){ spinFeed=null; paintScanning(); }
if(!done||done.forced&&done.only===spinFeed) spinFeed=null;
paintScanning();
}
function renderFeeds(){