Repository navigation
A standard for the site and public docs (WEB) #228
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
7e40383
43c983d
5bb418f
84f9ad0
b49005c
9cf1fb4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| # 0010: The site and docs get a standard | ||
|
|
||
| - **Status:** proposed, 2026-10-02 | ||
|
|
||
| ## Context | ||
|
|
||
| Every standard in `docs/standards/` is about the app. The public site, the README and the help pages had rules too, but | ||
| they lived in `folio-site`'s check script, a CI file and the owner's habits, not where a contributor would look. An | ||
| audit on 1 Oct 2026 found the live site a release behind, a roadmap that would have listed unreleased work as shipped, | ||
| and two claims (a crash-report upload and a VirusTotal link on every release) that the app and its releases did not | ||
| back up. None of it was caught by a check, because no rule said it had to be true. | ||
|
|
||
| ## Decision | ||
|
|
||
| - **Add `docs/standards/site.md` (`WEB`)** with numbered MUST / SHOULD / MAY rules, in the same shape as the others: rules, | ||
| where things stand today, ranked gaps. | ||
| - **The site reads the app at the release's tag**, so it never describes something nobody can download. | ||
| - **Public claims about privacy, permissions and signing must match the documents that make them**, and the checks that | ||
| can enforce a rule live in `folio-site` and are listed as gaps until they exist. | ||
| - **Wording that speaks for the owner comes from the owner.** | ||
|
|
||
| ## Consequences | ||
|
|
||
| - **Good:** a reviewer can cite `WEB-3` instead of a preference, and the rules for the site are in the repo. | ||
| - **Good:** the gap list ranks the checks still worth writing. | ||
| - **Bad:** one more standard to keep current. Several rules (WEB-3, WEB-7) are held by review until a check exists. | ||
| - **Bad:** WEB-8 means a tool or contributor cannot publish intro or pitch copy on its own, which slows some work on | ||
| purpose. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,164 @@ | ||
| # Site and docs standard (`WEB`) | ||
|
|
||
| How foliolauncher.com and Folio's public pages are written and kept true: the claims they make, the words they use, | ||
| the pictures on them, and what stops them rotting. The site lives in [McCal-Codes/folio-site](https://github.com/McCal-Codes/folio-site); | ||
| these rules apply to it, to the README and to anything else the project says to the public. | ||
|
|
||
| The short version: | ||
|
|
||
| > **The site says what the app says, at the release people can download, in words a person wrote, and a check fails | ||
| > when it stops being true.** | ||
|
|
||
| This standard exists because the site drifted in the places nobody was looking. On 1 Oct 2026 an audit found the live | ||
| site a release behind (0.6.7 while 0.6.7.3 was out) because the deploy skipped quietly without its token; a roadmap | ||
| that would have listed unreleased work as shipped; a privacy claim ("two things leave the phone, one of them a crash | ||
| report") that `PRIVACY.md` contradicts, since Folio sends no crash reports; a promise of a VirusTotal link on every | ||
| release that 0.6.6, 0.6.7 and 0.6.7.3 did not carry; and help pages that could never be flagged as stale. Every rule below is aimed at | ||
| that shape of mistake. | ||
|
|
||
| ## Rules | ||
|
|
||
| ### Truth | ||
|
|
||
| - **WEB-1 MUST** build product facts from the app repository at the latest release's tag, never from `main` and never | ||
| retyped: version, date, size, checksum, signing certificate, changelog, roadmap, tweak pages and the screen matrix. | ||
| The site never runs ahead of the APK a reader can download. The one other source is Keyd's README, read at Keyd's | ||
| latest release, because Keyd is a separate app with its own releases. | ||
| - **WEB-2 MUST NOT** present planned, exploring or unreleased work as shipped. A roadmap section counts as shipped | ||
| only when its version is a dated changelog entry. | ||
| - **WEB-3 MUST** keep every claim about privacy, permissions, signing, networking and what a release contains in step | ||
| with [PRIVACY.md](../../PRIVACY.md), [PERMISSIONS.md](../../PERMISSIONS.md) and the release's own assets. When one of | ||
| those changes, the page that repeats it changes in the same release. A claim that cannot be traced to one of them does | ||
| not go on the site. | ||
| - **WEB-4 MUST** say so when something is missing rather than fall back silently. A release without | ||
| `signing-certificate.txt` is covered by an explicit fallback that logs it, and a blank or non-hex checksum or signer | ||
| fails the build. | ||
| - **WEB-5 MUST NOT** invent content: no testimonials, ratings, download counts, compatibility claims, comparison | ||
| tables about other products, or "verified" and "official" badges Folio did not earn ([MKT-13](market.md)). | ||
| Compatibility claims come from what Folio is tested on ([ADP](adaptive-layout.md), the screen matrix). | ||
| - **WEB-6 MUST** credit what a thing is inspired by wherever it is described, as the app does. A tweak's page names | ||
| the person who made the idea, taken from the package's own depiction. | ||
|
|
||
| ### Words | ||
|
|
||
| - **WEB-7 MUST** be first person and plain: concrete claims over adjectives, no "revolutionary", "seamless" or | ||
| "next-generation". Punctuate without em dashes ([REL-9](releases.md)) and spell American. | ||
| - **WEB-8 MUST** get wording for bios, intros, pitches and anything else that speaks for the project's owner from the | ||
| owner. A draft by anyone else, including a tool, is marked as a draft in its pull request, says what in it is the | ||
| drafter's own phrasing, and does not ship until the owner has read and approved the wording. | ||
| - **WEB-9 SHOULD** name the thing the way someone would say the problem: "An update will not install", not | ||
| "Signature mismatch". The answer comes in the first paragraph. | ||
| - **WEB-10 MUST NOT** claim that Folio, a download or an update is "safe". Quoting a visitor's own question ("Is | ||
| sideloading it safe?") is fine and is answered with what is true and checkable: "signed with the same key as every | ||
| release", "asks for no permissions". | ||
|
|
||
| ### Pictures | ||
|
|
||
| - **WEB-11 MUST** use real screenshots, taken in Screenshot Mode (9:41, no personal notifications, no private device | ||
| names or calendar data), or label the image "Design preview, not final". A mockup never looks like a shipped | ||
| feature, and AI-generated phone screens never stand in for Folio's. | ||
| - **WEB-12 MUST** give every image useful alt text and declared dimensions. An image wider than three times its slot is | ||
| wasted bytes. | ||
| - **WEB-13 MUST** attach a screenshot to a release or a feature only when it really shows it. A stop with no matching | ||
| shot has none. | ||
| - **WEB-14 SHOULD** give each page its own share image, drawn from its own title and description. A page without one | ||
| must not link a missing file. | ||
|
|
||
| ### Privacy and security | ||
|
|
||
| - **WEB-15 MUST NOT** add analytics, trackers, session replay, fingerprinting, ads or third-party marketing scripts. | ||
| Performance is measured in the lab, never from visitors. | ||
| - **WEB-16 MUST** keep a restrictive Content Security Policy, HSTS, `X-Content-Type-Options`, `Referrer-Policy` and | ||
| `Permissions-Policy` on every HTML response, and **MUST** know whether each response comes from the static assets or | ||
| from Worker code, since headers rules do not apply to Worker responses. `script-src` **MUST NOT** allow | ||
| `'unsafe-inline'` or `'unsafe-eval'`; `scripts/check.mjs` fails the build if it does. The one allowance is in | ||
| Recorded exceptions. | ||
| - **WEB-17 MUST NOT** put a secret, token, supporter code or private address in the repository or in a script the site | ||
| ships. A code a visitor brings with them may be handled in their browser, as `/redeem/` does with the part of the | ||
| link after the `#`, but is never sent to a server. | ||
| Deploy tokens live in repository secrets and are set by their owner. | ||
| - **WEB-18 MUST** treat anything rendered from the app repo as trusted only because the tag it comes from is. A | ||
| changelog is rendered from markdown with `marked`, which lets raw HTML through, so it is the Content Security Policy | ||
| (WEB-16, no inline script) and not the renderer that stops a script in it from running. A new source of content gets | ||
| the same thought before it is added. | ||
|
|
||
| ### Accessibility and performance | ||
|
|
||
| - **WEB-19 MUST** meet WCAG 2.2 AA, with [A11Y](accessibility.md) applying to the web: one meaningful `h1`, no skipped | ||
| heading levels, a skip link, visible focus, targets large enough to touch, contrast in both light and dark, no | ||
| information carried by color alone, and a usable page at 200% zoom. | ||
| - **WEB-20 MUST** respect `prefers-reduced-motion` and add no animation merely because it is available. Navigation is | ||
| ordinary browser navigation. | ||
| - **WEB-21 MUST** ship no JavaScript a page does not need. Interactions CSS and HTML can do (filters, disclosure, | ||
| scrolling rows) are done with CSS and HTML. | ||
| - **WEB-22 MUST** hold Largest Contentful Paint at or under 2.5 s and Cumulative Layout Shift at or under 0.1 on the | ||
| pages that matter, and accessibility at 95 or above, as measured by Lighthouse in CI. These limits fail the run. | ||
|
Comment on lines
+94
to
+95
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
The new source entry says WEB-22 defines limits for LCP, INP, and CLS, but this rule only constrains LCP and CLS. As written, a page with severely delayed interaction can still satisfy the standard and keep Lighthouse CI green, so either add an INP threshold here or stop claiming that WEB-22 covers all three metrics. Useful? React with 👍 / 👎. |
||
| Lighthouse cannot measure Interaction to Next Paint, so total blocking time at or under 200 ms stands in for it and | ||
| warns. | ||
| - **WEB-23 MUST** keep the layout correct from a 320px phone to a very wide display, with no horizontal scrolling of | ||
| the page itself. | ||
|
|
||
| ### Checks and upkeep | ||
|
|
||
| - **WEB-24 MUST NOT** weaken `scripts/check.mjs` to make a build pass. A legitimate new class of mistake gets a new | ||
| check, not a bigger exception list. | ||
| - **WEB-25 MUST** give each help page the release it was written for, set by hand, so the freshness check can fail. A | ||
| page is flagged when what it describes changed, not because time passed. | ||
| - **WEB-26 MUST** make a green deploy mean the site was published. A missing deploy token fails the run. | ||
| - **WEB-27 SHOULD** show the work before it is committed: screenshots or a preview of any visible change, and what was | ||
| and was not checked in the pull request. Say what you could not check. | ||
|
|
||
| ## Where Folio is today | ||
|
|
||
| Measured on 1 and 2 Oct 2026, against folio-site `main`. | ||
|
|
||
| - **Truth (WEB-1, 2, 4).** `scripts/fetch-release.mjs` reads the latest release, then the changelog, roadmap, tweak | ||
| packages (manifest, depiction, icon) and screen matrix at that release's tag. It fails the build on a failed fetch, an | ||
| empty list or a blank checksum. `roadmap.astro` counts a section as shipped only if it is a dated changelog entry. | ||
| `check.mjs` fails if the download version has no dated changelog entry and page. | ||
| - **Claims (WEB-3).** Partly enforced. `src/data/claims.json` in folio-site ties eight claims about privacy, permissions | ||
| and signing to the phrase in `PRIVACY.md`, `PERMISSIONS.md` or Keyd's README that supports each, and `check.mjs` | ||
| fails the build when a page makes a claim whose phrase is gone, or repeats a phrase the documents contradict. It is a | ||
| tripwire: it only recognizes the phrasings listed, and it proves the documents say something, not that the app does. | ||
| The permission lists themselves are checked against the code: the app's CI fails if `PERMISSIONS.md` and the manifest | ||
| disagree, and Keyd's CI fails if its merged manifest declares any permission. | ||
| - **Words (WEB-7, 8).** Held by habit and by review. No check looks for em dashes or banned words. | ||
| - **Pictures (WEB-11 to 14).** `check.mjs` checks alt text, dimensions, aspect ratio, images wider than three times | ||
| their slot, and unused files. It also fails if a page's share image does not exist. 43 pages have share images | ||
| drawn per page by `npm run og` (41 on 2 Oct, plus the new Market and Keyd help pages); the rest are hand-made. The roadmap timeline has screenshots only for 0.6.0 and | ||
| 0.6.6. | ||
| - **Privacy and security (WEB-15 to 18).** No analytics. One page runs a script: `/redeem/`, the landing page for the | ||
| link in a supporter's email, loads its own file (`public/redeem.js`, so `script-src` stays `'self'`) to show the code | ||
| from the part of the URL after the `#`, copy it, and build the Android link, and the browser never sends that part to a | ||
| server. It is not in the sitemap and is `noindex`. Every other built page carries no script tag other than JSON-LD | ||
| data. `public/_headers` sets the CSP, HSTS, nosniff, Referrer-Policy and Permissions-Policy, and | ||
| they appear on HTML responses served through the Worker. `workers_dev` is off. `script-src` was `'self' | ||
| 'unsafe-inline'` until folio-site#17, which tightened it to `'self'` and added the check (deployed 2 Oct 2026). | ||
| - **Accessibility and performance (WEB-19 to 23).** 258 page and width combinations (43 pages, six widths from 320 to | ||
| 2560) have no horizontal overflow. A contrast sweep of nine pages found no failures in light or dark. Lighthouse in CI | ||
| (`.github/workflows/lighthouse.yml`) measures six pages: LCP 1.1 to 1.9 s, layout shift 0.000, every category 100. | ||
| - **Upkeep (WEB-24 to 26).** Help pages are pinned to 0.6.7 by hand and the check fails when one is two releases | ||
| behind. `deploy.yml` fails without `CLOUDFLARE_API_TOKEN`. The dispatch from this repo (`site.yml`) has not fired | ||
| for a real release yet. | ||
|
|
||
| ## Gaps | ||
|
|
||
| | | What it takes | Size | | ||
| |---|---|---| | ||
| | 1 | ~~A check that the site's privacy, permissions and signing sentences match `PRIVACY.md` and `PERMISSIONS.md`~~ (done as a tripwire, see Claims above) | M | | ||
| | 1a | ~~Check the claims against the code instead of prose~~ (done for permissions: `tools/check-permissions.py` runs in the app's CI and compares the manifest with `PERMISSIONS.md` in both directions; Keyd's `tools/check-no-permissions.py` reads its merged manifest in `folio-keyd`'s CI). Descriptive claims, such as "Market packages get no Android permissions", are still held by review | M | | ||
| | 1b | Flag any sentence on a page that makes a privacy or safety claim ("never", "no tracking", "safe") and is not in the registry, so a person reviews new phrasings | S | | ||
| | 2 | A lint for em dashes and banned words in `src/` (WEB-7) | S | | ||
| | 3 | Lighthouse over every page type, not six (WEB-22), and a test that a failing page really turns the run red | S | | ||
| | 4 | Screenshots for the timeline stops and the tweak pages, from Screenshot Mode (WEB-11, 13) | M | | ||
| | 5 | The release workflow attaches `signing-certificate.txt` every time, so the fallback in WEB-4 is never used | S | | ||
| | 6 | Confirm the `site.yml` dispatch fires on the next real release (WEB-26) | S | | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When Useful? React with 👍 / 👎. |
||
| | 7 | Tab order through the interactive pages with a real keyboard and a screen reader (WEB-19) | S | | ||
| | 8 | ~~`download.astro` called the signing certificate what "makes future updates safe"~~ (done: folio-site#18 reworded it to what Android checks) | S | | ||
| | 9 | Drop `'unsafe-inline'` from `style-src` by moving Astro's scoped styles to external CSS or hashing them, then remove the exception below (WEB-16) | M | | ||
|
|
||
| ## Recorded exceptions | ||
|
|
||
| - **WEB-16, `style-src 'unsafe-inline'`.** Astro emits scoped `<style>` blocks and inline style attributes, so the | ||
| policy allows inline styles. It does not allow inline script. Condition for removing it: gap 9. The reasoning is in the | ||
| comment above the policy in `public/_headers`. | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This rationale is false when a user chooses Share Diagnostics or Share crash report:
Diagnostics.reportIntentandCrashLog.shareIntentcreateACTION_SENDintents containing the diagnostic or crash report, andPRIVACY.mdexplicitly says the user chooses where those reports go. The existing claim that data leaves only when the user shares a report is therefore consistent with the app and privacy document; treating it as contradicted risks removing an accurate disclosure.Useful? React with 👍 / 👎.