Coins are a light, shared engagement layer across the Fairy Fox mesh: reading a page you haven't
opened today — anywhere same-origin under fairyfox.io — earns a coin. The counter lives in the
shared chrome as a small button beside the reader "Aa" button, and the balance is shared, via one
origin-wide key, across every same-origin fairyfox.io site in the same browser. Coins make reading and
exploring the mesh quietly rewarding — a garnish, never the meal.
Canonical, project-agnostic source: assets/references/fairyfox.io/hub/standards/coins.md. It ships as
part of the shared docs-site chrome (documentation.md) and its earning engine
is the master coins.js, loaded after reader.js.
Base coin counter/button is MANDATORY (hub 1.4.0). It ships with the chrome bundle; a project does
not get to decide it's "not needed" and drop it — only project-added coin moments are optional.
RAP vendors coins.js verbatim under assets/docs-theme/modules/coins.js; the on-page presence
check (the button renders beside "Aa", +1 on first daily view) is browser-gated and tracked as the
docs-site visual gap in adoption-manifest.md (Phase D of the 2026-07-25
adopt).
The prime directive: subtle, never central
- Never gate. Coins must not unlock, restrict, or grant exclusive access to anything. The full experience is always available at zero coins.
- Extra reward, not the reward. A touch of delight on top of something already satisfying.
- Restraint by default. If unsure whether a spot warrants a coin, it doesn't. Sprinkle, don't shower.
- Never at the brand's expense. A coin flourish must not clutter the UI or degrade the chrome.
Earning model (fixed — do NOT reimplement)
Earning is owned by the shared coins.js; a project does not re-implement or alter it. First view
of a page today: +1 (10% chance of +2); repeat view: 1% chance of +1, capped at 10/day; reading pages
(data-read/data-story) also show a read-time estimate and add a read-through bonus + a rare hidden
coin. State lives under the versioned origin-wide key fairyfox:coins:a — browser-only, never sent
to a server. The project may read/adjust the balance sparingly via window.FairyFoxCoins
(get/earnedTotal/earnedToday/onChange/spend/reward); a fairyfox:coins DOM event fires on
every change. Anti-patterns: daily log-in grinds, coins-to-continue, paywall-shaped "spend to unlock",
nagging, inflated grants.
How this project ships it
The coins counter lives on the docs site (the JSDoc site themed to fairyfox.io), which is served
same-origin under fairyfox.io/<key>/ — so it shares the hub's fairyfox:coins:a wallet and the
hub's /legal/coins/ disclosure page (the coins panel links there). Concretely:
assets/docs-theme/modules/coins.js— the mastercoins.jsvendored verbatim (byte-identical toassets/references/fairyfox.io/assets/js/coins.js; do not edit — refresh by re-copying the master).assets/docs-theme/fairyfox-docs.jsinjects it as a plain<script>afterinjectHeader()+initReader(), so its button lands just left of the.ff-reader-btn"Aa" button. It's a classic IIFE exposingwindow.FairyFoxCoins, so it's script-injected, not ES-imported.assets/docs-theme/theme/coins.css— the coin button / panel / "+1" pop / read-time chip / hidden-coin styles, on the project's own tokens (same token names as the master);@imported byfairyfox-docs.cssafterreader.css(the panel reuses the reader panel's.ff-rp-*chrome).scripts/build-docs.mjscopies the wholemodules/andtheme/dirs, so both files install intodocs/jsdoc/automatically — no build-script change needed.
Because the docs are same-origin with the hub, no project-side legal page changes are needed (the
hub origin's /legal/coins/ covers the fairyfox:coins:a disclosure). The RAP web app
(targets/web, a different origin on Netlify) does not carry coins, so its legal pages are
unaffected. If coins are ever added to the web app, disclose fairyfox:coins:a in
targets/web/public/legal/{privacy,cookies}.html per the "Keep the Legal Docs Accurate" instruction.
Verify (is it being followed?)
- The coin counter comes from the shared chrome (
coins.jsvendored from master, byte-identical), not a re-implementation, and is loaded afterreader.js. - Persistence — the store is read-and-merged (never replaced) on load; nothing clears the wallet
except the user (
spend, the panel's Clear my data, or a browser reset).coins.jsowns this. - The project gates nothing on coins — the docs read fully at zero balance.
- Coin UI is subtle (a small button beside "Aa"; a "+1" pop) and doesn't clutter the docs.
- The
fairyfox:coins:astore is disclosed — via the same-origin hub/legal/coins/page the panel links to (the docs site shares the hub origin).