Home page: every feature on one row, and the copy in plain words (#216)

The player was the one feature with its picture under its text; it is a
row like the rest now, its bar cut short of the volume so it stays
legible in the narrower column. The feed list is cut to the first two
shows, so its row is no taller than its text, and the directory to its
first two rows, which the old crop cut through. On a phone the directory
shows its left column.

The copy loses its insiders' words: feeds are podcasts, the swarm and
seeding ratio are sharing back for as long as you choose, OPML is said
once and explained, Rust is gone. Headings say what you get ("New
episodes, ready to play", "Cut the dead air", "See what others listen
to"), and the hero opens with iPX by name and says it runs on your own
server, which the tagline used to.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-12 20:15:39 +00:00
parent 68ac180c9a
commit 83e7ee817b
5 changed files with 22 additions and 23 deletions

View File

@@ -4,7 +4,7 @@
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1"> <meta name="viewport" content="width=device-width,initial-scale=1">
<title>iPX</title> <title>iPX</title>
<meta name="description" content="iPX checks your feeds, downloads new episodes and gives everyone at home their own list, on your own server. The heir to iPodderX, free and open source."> <meta name="description" content="iPX checks your podcasts, downloads new episodes and gives everyone at home an account of their own, on your own server. The successor to iPodderX, free and open source.">
<meta name="color-scheme" content="light"> <meta name="color-scheme" content="light">
<link rel="icon" href="/favicon.png"> <link rel="icon" href="/favicon.png">
<link rel="preload" href="/fonts/inter.woff2" as="font" type="font/woff2" crossorigin> <link rel="preload" href="/fonts/inter.woff2" as="font" type="font/woff2" crossorigin>
@@ -21,7 +21,7 @@
<main> <main>
<section class="hero"> <section class="hero">
<h1>iPX</h1> <h1>iPX</h1>
<p class="intro">It checks your feeds, downloads new episodes, and gives everyone at home a list of their own to listen from in the browser.</p> <p class="intro">iPX checks your podcasts, downloads new episodes, and gives everyone at home their own account to listen from in the browser. It runs on your own server.</p>
<p class="acts"> <p class="acts">
<a class="btn primary" href="https://directory.ipxreader.app">Browse the directory</a> <a class="btn primary" href="https://directory.ipxreader.app">Browse the directory</a>
</p> </p>
@@ -62,34 +62,34 @@
<h3>An account for everyone at home</h3> <h3>An account for everyone at home</h3>
<p>Each person has their own shows, sees what they have and have not heard, and picks up where they left off.</p> <p>Each person has their own shows, sees what they have and have not heard, and picks up where they left off.</p>
</div> </div>
<figure class="frame"><img src="/shots/feeds.webp" width="290" height="416" loading="lazy" <figure class="frame"><img src="/shots/feeds.webp" width="290" height="276" loading="lazy"
alt="The feed list: Directory, Currently Listening and All Subscriptions, then each show with its cover and how many of its episodes are unread."></figure> alt="The feed list: Directory, Currently Listening and All Subscriptions, then each show with its cover and how many of its episodes are unread."></figure>
</div> </div>
<div class="feature"> <div class="feature">
<div class="say"> <div class="say">
<h3>It does the fetching</h3> <h3>New episodes, ready to play</h3>
<p>Feeds are checked on a schedule and new episodes download on their own, filtered by keyword, media type or explicit content. Old files are cleared to stay within the disk space you give it, never one somebody pinned.</p> <p>iPX checks your podcasts through the day and downloads new episodes as they come out. Skip the ones you don't want by keyword, or leave out explicit ones. Old episodes are cleared to stay within the space you give it, but never one someone has pinned.</p>
</div> </div>
<figure class="frame"><img class="cut" src="/shots/episodes.webp" width="904" height="283" loading="lazy" <figure class="frame"><img class="cut" src="/shots/episodes.webp" width="904" height="283" loading="lazy"
alt="99% Invisible's episodes in a table with each one's size and date, and the downloaded one's file beside it, ready to play, save, share or delete."></figure> alt="99% Invisible's episodes in a table with each one's size and date, and the downloaded one's file beside it, ready to play, save, share or delete."></figure>
</div> </div>
<div class="feature wide"> <div class="feature">
<div class="say"> <div class="say">
<h3>Pick up where you left off</h3> <h3>Pick up where you left off</h3>
<p>The player keeps your place in every episode, on every device.</p> <p>The player keeps your place in every episode, on every device.</p>
</div> </div>
<figure class="frame"><img class="cut player" src="/shots/player.webp" width="1200" height="80" loading="lazy" <figure class="frame"><img class="cut player" src="/shots/player.webp" width="950" height="80" loading="lazy"
alt="The player along the bottom of the page: the episode playing, back and forward, how far in it is, its speed and the volume."></figure> alt="The player along the bottom of the page: the episode playing, back and forward, and how far in it is."></figure>
</div> </div>
<!-- DeadAir: talk keeps its length, the quiet between it shrinks to a short pause, and the <!-- DeadAir: talk keeps its length, the quiet between it shrinks to a short pause, and the
episode ends sooner. CSS only; the page runs no script of its own. --> episode ends sooner. CSS only; the page runs no script of its own. -->
<div class="feature"> <div class="feature">
<div class="say"> <div class="say">
<h3>DeadAir</h3> <h3>Cut the dead air</h3>
<p>Turn it on for a show and long silences shrink to a short pause, so episodes finish sooner while every voice keeps its own pace. You choose how much of a pause to leave.</p> <p>Turn on DeadAir for a show and its long silences shrink to a short pause, so episodes finish sooner without anyone talking faster. You choose how long a pause to leave.</p>
</div> </div>
<figure class="frame deadair" role="img" aria-label="A waveform whose silent gaps shrink to short pauses, so the episode ends sooner."> <figure class="frame deadair" role="img" aria-label="A waveform whose silent gaps shrink to short pauses, so the episode ends sooner.">
<div class="wave" aria-hidden="true"> <div class="wave" aria-hidden="true">
@@ -105,28 +105,28 @@
<div class="feature"> <div class="feature">
<div class="say"> <div class="say">
<h3>A shared directory</h3> <h3>See what others listen to</h3>
<p>iPX servers can share the public shows their people follow. <a href="https://directory.ipxreader.app">Browse the directory</a> to find something new, and subscribe in one click from your own server.</p> <p>iPX servers can share which podcasts their listeners follow, and the directory gathers them in one place. <a href="https://directory.ipxreader.app">Browse the directory</a> to find something new and subscribe in one click.</p>
</div> </div>
<figure class="frame"><img class="cut" src="/shots/directory.webp" width="700" height="186" loading="lazy" <figure class="frame"><img class="cut half" src="/shots/directory.webp" width="700" height="133" loading="lazy"
alt="The directory's most subscribed shows, each with its cover and how many people subscribe."></figure> alt="The directory's most subscribed shows, each with its cover and how many people subscribe."></figure>
</div> </div>
<dl class="also"> <dl class="also">
<div><dt>BitTorrent too</dt> <div><dt>BitTorrent too</dt>
<dd>A show published as a torrent downloads like any other. iPX joins the swarm, seeds back until the ratio or time you set, and gives up on one that stalls. A slow swarm never holds up the rest.</dd></div> <dd>Some shows publish their episodes as torrents. iPX downloads them like any other, shares them back for as long as you choose, and a slow one never holds up the rest.</dd></div>
<div><dt>Subscribe to a whole list</dt> <div><dt>Subscribe to a whole list</dt>
<dd>Give iPX the address of an OPML list and it keeps a folder in step with it, as whoever keeps the list adds and drops shows. Your own subscriptions import and export as OPML too.</dd></div> <dd>Some people publish lists of podcasts they recommend, as OPML. Give iPX a list's address and it keeps a folder of those shows up to date as the list changes. Your own subscriptions move in and out the same way.</dd></div>
<div><dt>Sign in your way</dt> <div><dt>Sign in your way</dt>
<dd>Each person signs in with a password, or Cloudflare Access or Authentik signs them in for you. An admin adds people and decides who else is one.</dd></div> <dd>Everyone signs in with a password, or with the single sign-on you already use, Cloudflare Access or Authentik. An admin adds people and decides who else is one.</dd></div>
<div><dt>Blogs too</dt> <div><dt>Blogs too</dt>
<dd>Any RSS or Atom feed works, so the blogs you read sit beside the shows you listen to, each with its own read and unread.</dd></div> <dd>Any RSS or Atom feed works, so the blogs you read sit beside the shows you listen to, and iPX keeps track of what you have read.</dd></div>
</dl> </dl>
</section> </section>
<section class="then" aria-labelledby="then"> <section class="then" aria-labelledby="then">
<h2 id="then">Since 2004</h2> <h2 id="then">Since 2004</h2>
<p>iPodderX was one of the first podcatchers, a Mac app by Ray Slakinski and August Trometer, from the year podcasts got their name. iPX is its engine rewritten in Rust for a server, with a page modelled on the original window: the feed list down the side, the episodes in a table, the player along the bottom.</p> <p>iPodderX was one of the first podcatchers, a Mac app by Ray Slakinski and August Trometer, from the year podcasts got their name. iPX is that app rebuilt to run on your own server, with a page modelled on the original window: the feed list down the side, the episodes in a table, the player along the bottom.</p>
</section> </section>
</main> </main>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.9 KiB

After

Width:  |  Height:  |  Size: 6.5 KiB

View File

@@ -62,11 +62,9 @@ h1{margin:0;color:var(--navy);font-weight:900;font-size:clamp(6.5rem,24vw,15rem)
h2{margin:0 0 1.5rem;color:var(--navy);font-weight:800;font-size:clamp(1.75rem,4vw,2.4rem);line-height:1.1;letter-spacing:-.02em} h2{margin:0 0 1.5rem;color:var(--navy);font-weight:800;font-size:clamp(1.75rem,4vw,2.4rem);line-height:1.1;letter-spacing:-.02em}
.does,.then{padding-block:4rem 0} .does,.then{padding-block:4rem 0}
/* Each feature beside the part of the app that shows it, framed in the dial's navy at no more /* Each feature on one row: what it does on the left, beside the part of the app that shows it,
than its own size; the player, a long thin bar, takes the full width under its text. */ framed in the dial's navy at no more than its own size. */
.feature{display:grid;grid-template-columns:minmax(0,19rem) minmax(0,1fr);gap:clamp(1.5rem,5vw,4rem);align-items:center;padding-block:2.5rem} .feature{display:grid;grid-template-columns:minmax(0,22rem) minmax(0,1fr);gap:clamp(1.5rem,5vw,4rem);align-items:center;padding-block:2.5rem}
.feature.wide{grid-template-columns:minmax(0,1fr)}
.feature.wide .say{max-width:40rem}
.feature h3{margin:0 0 .5rem;color:var(--navy);font-weight:750;font-size:1.35rem;line-height:1.25} .feature h3{margin:0 0 .5rem;color:var(--navy);font-weight:750;font-size:1.35rem;line-height:1.25}
.feature p{margin:0;max-width:34rem} .feature p{margin:0;max-width:34rem}
.frame{margin:0;justify-self:start;max-width:100%;padding:clamp(.6rem,1.6vw,1.1rem);background:var(--navy);border-radius:1.1rem} .frame{margin:0;justify-self:start;max-width:100%;padding:clamp(.6rem,1.6vw,1.1rem);background:var(--navy);border-radius:1.1rem}
@@ -101,6 +99,7 @@ footer p{margin:0}
/* A wide crop at this width is too small to read: its left part, larger. */ /* A wide crop at this width is too small to read: its left part, larger. */
.frame img.cut{width:100%;aspect-ratio:4/3;object-fit:cover;object-position:0 0} .frame img.cut{width:100%;aspect-ratio:4/3;object-fit:cover;object-position:0 0}
.frame img.cut.player{aspect-ratio:2.4/1} .frame img.cut.player{aspect-ratio:2.4/1}
.frame img.cut.half{aspect-ratio:2.6/1} /* the directory's left column */
.also{grid-template-columns:minmax(0,1fr);gap:1.75rem} .also{grid-template-columns:minmax(0,1fr);gap:1.75rem}
.dial{margin-inline:-1rem} .dial{margin-inline:-1rem}
.scale{border-radius:0} .scale{border-radius:0}