diff --git a/CHANGELOG.md b/CHANGELOG.md index 30c7ad4..8ad805e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,11 +19,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- On a phone, iPodderX looks like an iOS app: filled round buttons and capsules instead of + outlined boxes, no rules between the bars and the page, the feed's name as a large title, a + segmented control for the tabs, taller rows with 17px text, and dialogs as sheets from the + bottom. Tapping a text box no longer zooms the page in. High contrast, and Increase Contrast, + keep their outlines. - The Glass theme's panels catch the light: a bright rim along the top and left edges, a fainter one along the far edges, and a soft sheen from the top-left corner. ### Fixed +- Opened from the home screen on an iPhone, the top bar, the reader's back button and the top + of the feed list keep clear of the Dynamic Island and the status bar instead of sitting under + them. - Swiping to the next or previous item takes a longer swipe, a quarter of the screen, so a diagonal scroll no longer jumps ahead by accident. The item follows your finger and slides across as it changes. diff --git a/web/app.css b/web/app.css index f5849c7..ede94f5 100644 --- a/web/app.css +++ b/web/app.css @@ -504,11 +504,12 @@ a{color:var(--accent)} } .iconbtn:hover{background:var(--raise);color:var(--fg)} /* One toolbar across the window, as the original had: grouped buttons, search on the right. */ -/* The display's own intrusions -- the home indicator along the bottom, the notch at one side in - landscape. viewport-fit=cover hands the page the whole screen, which is what a standalone +/* The display's own intrusions -- the Dynamic Island and status bar along the top (#54), the + home indicator along the bottom, the notch at one side in landscape. viewport-fit=cover hands the page the whole screen, which is what a standalone shell and an iOS home-screen app both give it, so the bars along the edges pay for them in padding. A browser with its own chrome reports nought and nothing moves. */ :root{ + --safe-t:env(safe-area-inset-top,0px); --safe-b:env(safe-area-inset-bottom,0px); --safe-l:env(safe-area-inset-left,0px); --safe-r:env(safe-area-inset-right,0px); @@ -518,7 +519,7 @@ a{color:var(--accent)} /* Longhand, and it has to stay longhand: the minifier runs a calc() in a padding shorthand into the value after it -- `calc(12px + var(--safe-r))7px` -- and the browser then throws the whole declaration away, leaving the bar with no padding at all. */ - padding-top:7px;padding-bottom:7px; + padding-top:calc(7px + var(--safe-t));padding-bottom:7px; padding-left:calc(12px + var(--safe-l));padding-right:calc(12px + var(--safe-r)); background:var(--panel);border-bottom:1px solid var(--line); } @@ -966,7 +967,7 @@ body:has(#player.on) #status{padding-bottom:3px} @media (max-width:820px){ #shell{grid-template-columns:1fr} - #sidebar{position:fixed;inset:0 auto 0 0;width:min(300px,86vw);z-index:42;transform:translateX(-100%);transition:transform .2s;box-shadow:var(--shadow)} + #sidebar{position:fixed;inset:0 auto 0 0;padding-top:var(--safe-t);width:min(300px,86vw);z-index:42;transform:translateX(-100%);transition:transform .2s;box-shadow:var(--shadow)} #sidebar.open{transform:none} #scrim{position:fixed;inset:0;background:rgba(0,0,0,.5);z-index:41} #player{ @@ -977,7 +978,10 @@ body:has(#player.on) #status{padding-bottom:3px} /* The feed list is reachable whether or not anything is playing. */ #burger{display:grid} - #topbar{gap:6px;padding:6px 8px} + #topbar{ + gap:6px;padding-top:calc(6px + var(--safe-t));padding-bottom:6px; + padding-left:calc(8px + var(--safe-l));padding-right:calc(8px + var(--safe-r)); + } #topbar .grow,.tgroup.item{display:none} #topbar #epSearch{width:auto;flex:1;min-width:0} .tgroup button,.tgroup a{padding:4px 8px;min-width:32px} @@ -990,13 +994,13 @@ body:has(#player.on) #status{padding-bottom:3px} #files,.ephead,.ep .fd,.ep .file,.ep .size,.ep .rowacts{display:none} .ep,#split.one .ep{grid-template-columns:20px 20px minmax(0,1fr) auto} #grab{display:none} - #detail{position:fixed;inset:0;z-index:38;display:none;border-top:0;padding:12px 16px 90px} + #detail{position:fixed;inset:0;z-index:38;display:none;border-top:0;padding:12px 16px 90px;padding-top:calc(12px + var(--safe-t))} body.reading #detail{display:block} #dback{display:inline-block;margin-bottom:10px} /* Beside the reader while it is swiped (gestures.ts): the item next to it, with a strip of the page's own background between them, so the list under the reader never shows (#52). */ #dpeek{position:fixed;top:0;bottom:0;left:0;width:calc(100% + 16px);z-index:38;background:var(--bg);pointer-events:none} - #dpeek>div{width:calc(100% - 16px);height:100%;overflow:hidden;padding:12px 16px 90px;background:var(--panel)} + #dpeek>div{width:calc(100% - 16px);height:100%;overflow:hidden;padding:12px 16px 90px;padding-top:calc(12px + var(--safe-t));background:var(--panel)} #dpeek.next>div{margin-left:16px} #dpeek .encbox{margin:0 0 10px} #dpeek.end{display:flex;align-items:center;padding-left:28px;color:var(--faint);font-size:13px} @@ -1036,6 +1040,90 @@ body:has(#player.on) #status{padding-bottom:3px} #logbox .tg{display:none} } +/* ---------- A phone, drawn the way iOS draws its own apps (#53) ---------- */ +/* Filled shapes rather than outlines, corners that grow rounder as the shape grows and nest inside + each other, 44px to tap and 17px to read. The outlines stay where they are what makes a control + visible at all: the high-contrast theme, and anyone who has asked the system for more contrast. + --edge is the one switch for that. */ +@media (max-width:820px){ + :root{--edge:transparent} + :root[data-theme="contrast"]{--edge:var(--line)} + /* Glass rules its panels off with an edge of its own; the light along them (--glass-rim) stays. */ + :root[data-theme="glass"],:root[data-theme="glass"][data-mode="light"]{--glass-edge:transparent} +} +@media (max-width:820px) and (prefers-contrast:more){ + :root{--edge:var(--line)} +} +@media (max-width:820px){ + /* The bars run into the page instead of being ruled off from it. */ + #topbar{background:var(--bg);border-bottom-color:var(--edge);gap:8px} + #status{background:var(--bg);border-top-color:var(--edge)} + #player{border-top-color:var(--edge)} + .sidefoot{border-top-color:var(--edge)} + + /* Buttons: filled and round. Hover is reset because Safari keeps it on whatever was last + tapped, which left an accent outline on it. */ + .btn,.sidetools button,.sidefoot button,.tgroup,.toast,kbd,#logbox{border-color:var(--edge)} + .btn:hover,.sidetools button:hover,.sidefoot button:hover{border-color:var(--edge)} + .btn,.sidetools button,.sidefoot button{border-radius:999px;min-height:40px;padding-left:14px;padding-right:14px} + .btn{display:inline-flex;align-items:center;justify-content:center;gap:6px} + .btn.ico,#dback{min-width:40px;height:40px;padding:0 10px;display:inline-grid;place-items:center} + .btn.tiny{min-height:0;padding:2px 9px} + .iconbtn{width:40px;height:40px;border-radius:999px;background:var(--panel2)} + .tgroup{border-radius:999px} + .tgroup button+button,.tgroup button+a{border-left-color:var(--edge)} + .tgroup button,.tgroup a{min-width:36px;height:40px;padding:0 8px} + + /* Text boxes filled, and at 16px or more: Safari zooms the page in on one any smaller. */ + input[type=search],input[type=text],input[type=password],input[type=number],select{ + font-size:17px;background:var(--panel2);border-color:var(--edge);border-radius:10px;padding:9px 12px; + } + #topbar #epSearch{font-size:16px;border-radius:999px;padding:9px 14px} + input:focus,select:focus{border-color:var(--accent)} + + /* The feed's name as the page's large title, the way Podcasts heads a show. */ + .fhead h2,.fhead.slim h2{font-size:26px;font-weight:700;letter-spacing:-.01em;line-height:1.2} + .fhead .sub{font-size:15px} + .acts{gap:8px} + .acts .btn{min-width:44px;height:44px;padding:0 13px;justify-content:center;font-size:15px} + + /* Tabs as a segmented control: equal segments on one track, the chosen one lifted. */ + .tabs{flex:1 1 100%;border-radius:10px;padding:2px} + .tabs button,.tabs a{flex:1;text-align:center;padding:7px 4px;border-radius:8px;font-size:14px} + .tabs button.on,.tabs a.on{box-shadow:0 1px 3px rgba(0,0,0,.22)} + + /* Rows: taller, larger type, and a hairline between them that starts where the text does. */ + .ep{padding:10px 8px;border-radius:10px;min-height:44px} + .ep.sel{border-color:var(--edge)} + .ep:hover:not(.sel){background-color:transparent} + .ep .t{font-size:17px} + .ep .line,.ep .date{font-size:15px} + .ep+.ep{background-image:linear-gradient(var(--line),var(--line));background-size:calc(100% - 64px) 1px;background-position:64px 0;background-repeat:no-repeat} + .ep.sel,.ep.sel+.ep{background-image:none} + .feed{padding:9px 10px;min-height:44px;border-radius:10px} + .places{border-bottom-color:var(--edge);padding-bottom:10px;margin-bottom:10px} + .feed .txt b{font-size:17px} + .feed .txt small{font-size:13px} + + /* The reader. */ + .dt{font-size:26px;font-weight:700;line-height:1.2;letter-spacing:-.01em;margin-bottom:8px} + .dmeta{font-size:15px;gap:10px} + .encbox{border-color:var(--edge);border-radius:14px;padding:8px 8px 8px 14px} + .dbody{font-size:17px;line-height:1.55} + + /* Dialogs are sheets, up from the bottom. */ + #modal{place-items:end stretch;padding:0} + .card,.card.wide{ + width:100%;max-height:calc(100dvh - var(--safe-t) - 12px);border-color:var(--edge);border-radius:20px 20px 0 0; + padding:20px 16px calc(20px + var(--safe-b));animation:sheet .3s cubic-bezier(.2,.8,.2,1); + } + .card h3{font-size:22px} + .field label,.field .hint{font-size:13px} + .check{font-size:17px} + .toast{border-radius:14px} +} +@keyframes sheet{from{transform:translateY(100%)}} + /* ---------- Classic: the 2004 Mac app ---------- */ /* After the iPodderX screenshots: brushed-metal chrome, a pale blue-grey source list, Aqua blue for whatever is selected, red unread badges, a striped table and Lucida Grande. */ @@ -1137,7 +1225,6 @@ body:has(#player.on) #status{padding-bottom:3px} } :root[data-theme="glass"] .card, :root[data-theme="glass"] .toast{box-shadow:var(--glass-rim),var(--shadow)} -:root[data-theme="glass"] .card{border-radius:20px} :root[data-theme="glass"] .toast{border-radius:14px} :root[data-theme="glass"] .toast.bad{border-color:var(--bad)} /* A narrow screen slides the feed list over the page, so it keeps its shadow. */ @@ -1149,12 +1236,25 @@ body:has(#player.on) #status{padding-bottom:3px} :root[data-theme="glass"] .tgroup, :root[data-theme="glass"] .tabs{ background:color-mix(in srgb,var(--panel2) 60%,transparent); - border-color:var(--glass-edge);border-radius:12px;box-shadow:var(--glass-rim); + border-color:var(--glass-edge);box-shadow:var(--glass-rim); +} +/* Rounder than the other themes; a phone has shapes of its own (#53), which these would outrank. */ +@media (min-width:821px){ + :root[data-theme="glass"] .card{border-radius:20px} + :root[data-theme="glass"] .tgroup, + :root[data-theme="glass"] .tabs{border-radius:12px} + :root[data-theme="glass"] .btn, + :root[data-theme="glass"] .sidetools button, + :root[data-theme="glass"] .sidefoot button{border-radius:10px} } -:root[data-theme="glass"] .btn, -:root[data-theme="glass"] .sidetools button, -:root[data-theme="glass"] .sidefoot button{border-radius:10px} :root[data-theme="glass"] .btn.primary{box-shadow:inset 0 1px 0 rgba(255,255,255,.35)} +/* On a phone the rim all round a small capsule or bar read as the outline #53 took away, so the + light only catches the top edge there, and the bars at the top and bottom have none. */ +@media (max-width:820px){ + :root[data-theme="glass"]{--glass-rim:inset 0 1px 0 var(--glass-lo)} + :root[data-theme="glass"] #topbar, + :root[data-theme="glass"] #status{box-shadow:none} +} /* Someone who has asked the system for less transparency, or more contrast, gets solid panels. */ @media (prefers-reduced-transparency:reduce),(prefers-contrast:more){ :root[data-theme="glass"] body{background:var(--bg)}