Skip to content

Commit 14e524d

Browse files
committed
feat(website): add /download, and put the product in the h1
Three findings from an external SEO audit, all reproduced before acting on them. /download returned 404 and was absent from the sitemap, so the highest-intent query in this category had no landing page at all — the only download path was the navbar CTA jumping straight out to GitHub Releases, which sends the traffic somewhere we do not control and drops a first-time visitor onto a list of six version-stamped binaries with no guidance. The new page gives each platform a card with its artifact, its caveats, and one button, plus the Gatekeeper and Nix one-liners. SoftwareApplication.downloadUrl now points at it, with the Releases list demoted to installUrl. Its buttons target /releases/latest rather than deep links. Every asset carries its version in the filename (Openscreen-Mac-arm64-1.7.0.dmg), so there is no stable per-asset URL, and resolving one at build time would go stale silently: the docs workflow only fires on website/** changes, so shipping a new app release would not rebuild this page and it would keep advertising the previous version. GitHub resolves /releases/latest on every click instead. The landing page's only h1 was the bare wordmark "OpenScreen" — a heading with no subject. The descriptor moves into it as a block-level second line at the old tagline's size, so the rendered hero is unchanged: verified in a browser, 68px wordmark, 19px descriptor, and a 0px gap to the following line, which is exactly what the <br> it replaces produced. The navbar CTA becomes a router link now that it points somewhere internal, which gets it SPA navigation and route prefetch that a raw <a> in an html item never had. A link item takes a plain string label, so the download glyph moves to a CSS mask on .navbar-download-cta. Unrelated bug found while cross-checking the audit against the actual release assets: both the installation guide and the landing page told users to run `apt install ./Openscreen-Linux-latest.deb` and `pacman -U Openscreen-Linux-latest.pacman`. No "-latest" asset has ever been published — the real names are version-stamped — so both commands failed verbatim. They now glob, like the AppImage lines two paragraphs down already did. Verified against a production build: 11 sitemap URLs including /download/, every page still carrying exactly one meta description at 149 characters or fewer, one h1 per page, and the three platform cards resolving to equal height. Checked in a browser at 1265px and 375px — cards stack on mobile, no page-level horizontal scroll, and the long xattr command scrolls inside its own pre.
1 parent f6f74c6 commit 14e524d

7 files changed

Lines changed: 501 additions & 22 deletions

File tree

website/docs/installation.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ keywords:
1515

1616
# Installation
1717

18-
Download the latest installer for your platform from [GitHub Releases](https://github.com/getopenscreen/openscreen/releases).
18+
Download the latest installer for your platform from the [download page](/download), or straight from [GitHub Releases](https://github.com/getopenscreen/openscreen/releases).
1919

2020
## macOS
2121

@@ -45,12 +45,12 @@ Three packages are published per release — pick the one matching your distro.
4545

4646
**Debian / Ubuntu / Pop!_OS**
4747
```bash
48-
sudo apt install ./Openscreen-Linux-latest.deb
48+
sudo apt install ./Openscreen-Linux-*.deb
4949
```
5050

5151
**Arch / Manjaro**
5252
```bash
53-
sudo pacman -U Openscreen-Linux-latest.pacman
53+
sudo pacman -U Openscreen-Linux-*.pacman
5454
```
5555

5656
**Any distro (AppImage)**

website/docusaurus.config.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -222,12 +222,15 @@ export default async function createConfig(): Promise<Config> {
222222
`GitHub${starBadge}</a>`,
223223
},
224224
{
225-
type: "html",
225+
// Points at our own page now, not straight out to Releases, which
226+
// means it can be a real router link: SPA navigation plus route
227+
// prefetch, neither of which a raw <a> in an html item gets. A
228+
// link item only takes a string label, so the download glyph moves
229+
// to a CSS mask on .navbar-download-cta.
230+
to: "/download",
231+
label: "Download",
232+
className: "navbar-download-cta",
226233
position: "right",
227-
value:
228-
`<a class="navbar-download-cta" href="${REPO_URL}/releases" target="_blank" rel="noopener noreferrer">` +
229-
'<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 15V3"/><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><path d="m7 10 5 5 5-5"/></svg>' +
230-
"Download</a>",
231234
},
232235
],
233236
},

website/src/css/custom.css

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,12 @@ body {
244244
font-size: 11px;
245245
}
246246

247+
/* Lucide "download", inlined as a data URI so it costs no extra request and
248+
stays theme-independent (the mask below recolors it from currentColor). */
249+
:root {
250+
--os-icon-download: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 15V3'/%3E%3Cpath d='M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4'/%3E%3Cpath d='m7 10 5 5 5-5'/%3E%3C/svg%3E");
251+
}
252+
247253
.navbar-download-cta {
248254
display: inline-flex;
249255
align-items: center;
@@ -256,6 +262,19 @@ body {
256262
font-weight: 600;
257263
}
258264

265+
/* The CTA is a router link, whose label is a plain string, so the download
266+
glyph is painted here instead of inlined as markup. A mask rather than a
267+
background image keeps it on currentColor like the other navbar icons. */
268+
.navbar-download-cta::before {
269+
content: "";
270+
width: 14px;
271+
height: 14px;
272+
flex: none;
273+
background-color: currentColor;
274+
-webkit-mask: var(--os-icon-download) center / contain no-repeat;
275+
mask: var(--os-icon-download) center / contain no-repeat;
276+
}
277+
259278
.navbar-download-cta:hover {
260279
background: var(--os-brand-lo);
261280
}
Lines changed: 280 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,280 @@
1+
/**
2+
* /download — same tokens and rhythm as the landing page hero and install grid
3+
* (src/pages/index.module.css), so the two pages read as one site rather than a
4+
* marketing page plus a bolted-on download page.
5+
*/
6+
7+
.hero,
8+
.platforms {
9+
background: var(--os-bg);
10+
color: var(--os-fg);
11+
}
12+
13+
.hero {
14+
padding: 88px 28px 0;
15+
text-align: center;
16+
background: radial-gradient(ellipse 60% 40% at 50% 0%, var(--os-accent-wash), transparent 60%), var(--os-bg);
17+
}
18+
19+
.heroInner {
20+
max-width: 720px;
21+
margin: 0 auto;
22+
}
23+
24+
.badge {
25+
display: inline-flex;
26+
align-items: center;
27+
padding: 0.3rem 0.85rem;
28+
border-radius: 999px;
29+
background: var(--os-accent-soft);
30+
border: 1px solid var(--os-accent-border);
31+
color: var(--os-accent);
32+
font-size: 0.8rem;
33+
font-weight: 600;
34+
margin-bottom: 22px;
35+
}
36+
37+
.title {
38+
margin: 20px 0 0 !important;
39+
font-size: clamp(38px, 5.2vw, 58px);
40+
font-weight: 700 !important;
41+
letter-spacing: -0.03em;
42+
line-height: 1.05;
43+
color: var(--os-fg-emphasis) !important;
44+
}
45+
46+
.tagline {
47+
font-size: 19px;
48+
line-height: 1.55;
49+
color: var(--os-fg-2);
50+
margin: 20px auto 0;
51+
max-width: 560px;
52+
}
53+
54+
.actions {
55+
display: flex;
56+
gap: 12px;
57+
justify-content: center;
58+
flex-wrap: wrap;
59+
margin: 30px 0 0;
60+
}
61+
62+
.primaryCta,
63+
.secondaryCta {
64+
display: inline-flex;
65+
align-items: center;
66+
gap: 0.5rem;
67+
padding: 0.65rem 1.35rem;
68+
border-radius: 9px;
69+
font-weight: 600;
70+
font-size: 0.95rem;
71+
text-decoration: none;
72+
transition: transform 0.15s ease, box-shadow 0.15s ease;
73+
}
74+
75+
.primaryCta {
76+
background: var(--os-accent);
77+
color: #fff;
78+
box-shadow: 0 8px 24px rgba(16, 185, 129, 0.25);
79+
}
80+
81+
.primaryCta:hover {
82+
transform: translateY(-2px);
83+
color: #fff;
84+
text-decoration: none;
85+
}
86+
87+
.secondaryCta {
88+
background: transparent;
89+
color: var(--os-fg-2);
90+
border: 1px solid var(--os-border);
91+
}
92+
93+
.secondaryCta:hover {
94+
transform: translateY(-2px);
95+
border-color: var(--os-fg-2);
96+
color: var(--os-fg-2);
97+
text-decoration: none;
98+
}
99+
100+
.note {
101+
margin: 22px auto 0;
102+
max-width: 460px;
103+
font-size: 12.5px;
104+
color: var(--os-meta);
105+
line-height: 1.5;
106+
}
107+
108+
.note strong {
109+
color: var(--os-muted);
110+
}
111+
112+
/* ---- platform cards ---- */
113+
114+
.platforms {
115+
padding: 56px 28px 100px;
116+
display: flex;
117+
justify-content: center;
118+
}
119+
120+
.platformsInner {
121+
width: 100%;
122+
max-width: 1040px;
123+
}
124+
125+
.grid {
126+
display: grid;
127+
grid-template-columns: repeat(3, 1fr);
128+
gap: 0.875rem;
129+
}
130+
131+
@media (max-width: 860px) {
132+
.grid {
133+
grid-template-columns: 1fr;
134+
}
135+
}
136+
137+
/* Flex column so .cardBody absorbs the slack and the CTA sits flush at the
138+
bottom of all three cards, whose blurbs differ in length. */
139+
.card {
140+
display: flex;
141+
flex-direction: column;
142+
border-radius: 14px;
143+
border: 1px solid var(--os-border);
144+
background: var(--os-surface-1);
145+
box-shadow: var(--os-elev-card);
146+
overflow: hidden;
147+
}
148+
149+
.cardHeader {
150+
display: flex;
151+
align-items: center;
152+
gap: 8px;
153+
padding: 10px 16px;
154+
border-bottom: 1px solid var(--os-border-soft);
155+
font-size: 12px;
156+
font-weight: 500;
157+
color: var(--os-fg-2);
158+
}
159+
160+
.artifactChip {
161+
margin-left: auto;
162+
padding: 1px 7px;
163+
border-radius: 999px;
164+
border: 1px solid var(--os-border-soft);
165+
font-family: var(--ifm-font-family-monospace);
166+
font-size: 10.5px;
167+
color: var(--os-meta);
168+
}
169+
170+
.cardBody {
171+
flex: 1;
172+
padding: 16px;
173+
}
174+
175+
.cardBlurb {
176+
margin: 0;
177+
font-size: 13px;
178+
line-height: 1.6;
179+
color: var(--os-muted);
180+
}
181+
182+
.noteList {
183+
margin: 12px 0 0;
184+
padding-left: 16px;
185+
font-size: 12.5px;
186+
line-height: 1.75;
187+
color: var(--os-meta);
188+
}
189+
190+
.noteList li::marker {
191+
color: var(--os-accent);
192+
}
193+
194+
.cardCta {
195+
display: flex;
196+
align-items: center;
197+
justify-content: center;
198+
gap: 7px;
199+
margin: 0 16px 16px;
200+
padding: 9px 14px;
201+
border-radius: 9px;
202+
background: var(--os-accent);
203+
color: #fff;
204+
font-size: 13px;
205+
font-weight: 600;
206+
text-decoration: none;
207+
}
208+
209+
.cardCta:hover {
210+
background: var(--os-brand-lo);
211+
color: #fff;
212+
text-decoration: none;
213+
}
214+
215+
/* ---- side panels ---- */
216+
217+
.panels {
218+
margin-top: 0.875rem;
219+
display: grid;
220+
grid-template-columns: repeat(2, 1fr);
221+
gap: 0.875rem;
222+
}
223+
224+
@media (max-width: 860px) {
225+
.panels {
226+
grid-template-columns: 1fr;
227+
}
228+
}
229+
230+
.panel {
231+
border-radius: 14px;
232+
border: 1px solid var(--os-border);
233+
background: var(--os-surface-1);
234+
overflow: hidden;
235+
}
236+
237+
.panelHeader {
238+
display: flex;
239+
align-items: center;
240+
gap: 8px;
241+
padding: 10px 16px;
242+
border-bottom: 1px solid var(--os-border-soft);
243+
font-size: 12px;
244+
font-weight: 500;
245+
color: var(--os-fg-2);
246+
}
247+
248+
.code {
249+
margin: 0;
250+
padding: 1rem;
251+
font-size: 12.5px;
252+
line-height: 1.7;
253+
color: var(--os-fg-2);
254+
background: transparent;
255+
overflow-x: auto;
256+
}
257+
258+
.accentText {
259+
color: var(--os-accent);
260+
}
261+
262+
.meta {
263+
color: var(--os-meta);
264+
}
265+
266+
.panelFoot {
267+
margin: 0;
268+
padding: 0 16px 14px;
269+
font-size: 12px;
270+
line-height: 1.55;
271+
color: var(--os-meta);
272+
}
273+
274+
.footNote {
275+
margin: 26px 0 0;
276+
text-align: center;
277+
font-size: 12.5px;
278+
line-height: 1.6;
279+
color: var(--os-meta);
280+
}

0 commit comments

Comments
 (0)