body is a grid of topbar / main / status / player, and nothing in the page accounted for a display's own intrusions. Mobile Safari hides that by insetting the layout viewport to the safe area, so the site was fine in a browser -- but a full-screen shell, the native app or the site added to an iOS home screen, hands the page the whole display, and the last row landed under the home indicator with its seek bar and times half cut off. viewport-fit=cover asks for the whole screen deliberately, and the bars along the edges now pay for the insets in padding: the bottom for the indicator, left and right for the notch in landscape. A browser with its own chrome reports nought and nothing moves. The padding has to be longhand, and there is a comment saying so, because the minifier drops the space between a calc() and the value after it in a shorthand -- padding:7px calc(12px + var(--safe-r))7px ... -- and a browser then throws the whole declaration away. The bars lost all their padding, which moved the item list far enough that the pull-to-refresh browser test stopped finding it; nothing reported an error, and the page still loaded. buildStyle now fails the build on a calc() run into its neighbour rather than trusting it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
90 lines
4.3 KiB
HTML
90 lines
4.3 KiB
HTML
<!doctype html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
|
||
<meta name="color-scheme" content="dark light">
|
||
<title>iPodderX</title>
|
||
<link rel="icon" type="image/png" sizes="128x128" href="/favicon.png">
|
||
<link rel="apple-touch-icon" href="/apple-touch-icon.png">
|
||
<link rel="stylesheet" data-src="app.css">
|
||
</head>
|
||
<body>
|
||
<header id="topbar">
|
||
<button class="iconbtn" id="burger" title="Feeds" aria-label="Feeds" data-icon="menu"></button>
|
||
<!-- One group per thing acted on, in the order the panes read: feeds, then the selected item.
|
||
A phone hides the item group, which it has no table for. -->
|
||
<div class="tgroup">
|
||
<button id="addFeed" title="Add a feed" aria-label="Add a feed" data-icon="plus"></button>
|
||
<button id="tbRemove" title="Unsubscribe from this feed" aria-label="Unsubscribe from this feed" data-icon="circleMinus" disabled></button>
|
||
<button id="scanAll" title="Check every feed for new items" aria-label="Check every feed for new items" data-icon="scan"></button>
|
||
</div>
|
||
<div class="tgroup item">
|
||
<button id="tbPlay" title="Play the selected item" aria-label="Play the selected item" data-icon="play" disabled></button>
|
||
<button id="tbRead" title="Mark the selected item read or unread" aria-label="Mark read or unread" data-icon="check" disabled></button>
|
||
<button id="tbFlag" title="Pin the selected item, so it is never deleted" aria-label="Pin" data-icon="pin" disabled></button>
|
||
</div>
|
||
<span class="grow"></span>
|
||
<input type="search" id="epSearch" placeholder="Search items…">
|
||
<div class="tgroup" id="admintools">
|
||
<button id="prefs" title="Settings" aria-label="Settings" data-icon="settings"></button>
|
||
<a id="admin" href="/admin" title="Admin: the server's settings, accounts and the log" aria-label="Admin" data-icon="admin"></a>
|
||
</div>
|
||
</header>
|
||
<div id="shell">
|
||
<aside id="sidebar">
|
||
<div class="brand">
|
||
<img class="logo" src="/icon.png" alt="iPodderX" title="The original iPodderX icon, 2004"><h1>iPodderX</h1>
|
||
</div>
|
||
<div class="searchwrap"><input type="search" id="feedFilter" placeholder="Filter feeds…"></div>
|
||
<div id="feedlist"></div>
|
||
<div class="sidefoot">
|
||
<div class="who"><span id="who"></span><button id="signout" title="Sign out" aria-label="Sign out" data-icon="signout"></button></div>
|
||
</div>
|
||
</aside>
|
||
<div id="main">
|
||
<div class="wrap" id="content"></div>
|
||
</div>
|
||
<div id="scrim" hidden></div>
|
||
</div>
|
||
<div id="status"><span id="count"></span></div>
|
||
|
||
<div id="player">
|
||
<div id="pnow">
|
||
<div id="partwrap"></div>
|
||
<div class="txt"><b><span class="eq" aria-hidden="true"><i></i><i></i><i></i></span><span id="ptitle"></span></b><small id="pfeed"></small></div>
|
||
</div>
|
||
<div id="pmid">
|
||
<div id="pbtns">
|
||
<button class="iconbtn" id="pback" title="Back 15 seconds (←)" aria-label="Back 15 seconds" data-icon="back"></button>
|
||
<button class="iconbtn" id="pplay" title="Play or pause (space)" aria-label="Play or pause" data-icon="play"></button>
|
||
<button class="iconbtn" id="pfwd" title="Forward 30 seconds (→)" aria-label="Forward 30 seconds" data-icon="fwd"></button>
|
||
</div>
|
||
<div id="seekrow">
|
||
<span id="pcur">0:00</span>
|
||
<input type="range" id="seek" min="0" max="1000" value="0">
|
||
<span id="pdur">0:00</span>
|
||
</div>
|
||
</div>
|
||
<div id="pright">
|
||
<select id="rate" title="Speed">
|
||
<option value="0.8">0.8×</option><option value="1" selected>1×</option>
|
||
<option value="1.25">1.25×</option><option value="1.5">1.5×</option>
|
||
<option value="1.75">1.75×</option><option value="2">2×</option><option value="2.5">2.5×</option>
|
||
</select>
|
||
<input type="range" id="vol" min="0" max="100" value="100" title="Volume">
|
||
<button class="iconbtn" id="pclose" title="Close the player" aria-label="Close the player" data-icon="close"></button>
|
||
</div>
|
||
</div>
|
||
|
||
<div id="modal"><div class="card" id="modalCard"></div></div>
|
||
<div id="toasts"></div>
|
||
<!-- One element for both: a <video> plays an audio-only file exactly like <audio> does (same
|
||
HTMLMediaElement API), and it is the only tag that can also show a picture. Hidden unless
|
||
the current file is video -- see body.has-video below. -->
|
||
<video id="audio" preload="metadata" playsinline></video>
|
||
|
||
<script data-src="web/src"></script>
|
||
</body>
|
||
</html>
|