-
Notifications
You must be signed in to change notification settings - Fork 0
Frontend Guide
The frontend is plain TypeScript bundled with esbuild into one self contained file per page, layered on top of Go html/template pages. There is no framework and no client side router: the server renders the page shell with translated strings, and the page's bundle builds the interactive parts into a mount div. This page is the contributor map: which file powers what, what the shared modules do, and the conventions to follow when adding a page.
package.json defines the whole build (esbuild ^0.24, TypeScript ^5.6):
esbuild ts/raids.ts ts/dps.ts ts/pvp.ts ts/events.ts ts/shinies.ts ts/iv.ts ts/raidfinder.ts ts/notifications.ts ts/social.ts
--bundle --outdir=static/js --target=es2020 --minify
npm run build produces the bundles, npm run watch rebuilds on change. Each entrypoint becomes static/js/<name>.js. See Building-and-Development for the full workflow.
| Entrypoint | Bundle | Template | Page |
|---|---|---|---|
ts/raids.ts |
raids.js |
templates/raids.html |
/raids (Raids-and-Counters) |
ts/dps.ts |
dps.js |
templates/dps.html |
/dps (DPS-Calculator) |
ts/pvp.ts |
pvp.js |
templates/pvp.html |
/pvp (PvP-IV-Ranker) |
ts/events.ts |
events.js |
templates/events.html |
/events (Events) |
ts/shinies.ts |
shinies.js |
templates/shinies.html |
/shinies (Shiny-Tracking) |
ts/iv.ts |
iv.js |
templates/iv.html |
/iv (IV Calculator page) |
ts/raidfinder.ts |
raidfinder.js |
templates/trainers.html |
/trainers, Raid Finder tab (Raid-Finder) |
ts/notifications.ts |
notifications.js |
templates/base.html |
🔔 badge poller, toast display, and ding sound, loaded on every page for logged-in users |
ts/social.ts |
social.js |
templates/trainer.html |
Social action buttons (Add/Remove Friend, Block/Unblock) on trainer profile pages |
The Trainer Directory tab of /trainers and pages like settings, admin, translate, friends, and notifications use inline <script> blocks in their templates instead of bundles.
-
gamedata.ts: the data backbone.loadGameData()fetches the merged game data once per page load and caches it in a module variable; it tries the session endpoint/api/app/datafirst and falls back to the public/api/dataon a 401. Also home topokeSprite(id)(PokeAPI GitHub raw sprite URLs),pokeName()(localizes Pokemon names through thepokemonNamesmap using theSITE_LANGglobal, English fallback),cpForLevel()andcpmFromCP()(CP math and its inverse), lookup helpers, andtypeEffectiveness(). -
damage.ts: the PvE math: per hit damage, fast move DPS and EPS, full cycle DPS, and the TDO estimate. Formulas are written out on DPS-Calculator. -
counters.ts: the counter search:calcCounters()(exhaustive best moveset search over every Pokemon, top 20 by DPS) andcalcSinglePokemon()(one Pokemon versus one boss with aPokemonConfigof CPM, IVs, and form). Handles Shadow, Purified, Primal, and Mega forms, stripping the form prefix to fall back to the base form's move pool, and renders the shared counter table. -
picker.ts:createPicker(), the searchable sprite picker used across DPS and other pages. Takes a list ofPickerEntryitems (key, search name, localized label, sprite, type badges, optional tier tag, sort group), renders a dropdown with match ranking and a selected preview block, and reports selection through callbacks. The returnedPickerhandle exposesselectByName(name)for programmatic restore (used by the DPS page to reload a persisted target from localStorage). -
tabs.ts:buildTabs(), the lazy tab bar: each panel'srender()runs only on first activation, so heavy panels (like a PvP league) cost nothing until opened. -
pokedex.ts: on demand PokeAPI fetches:fetchSpeciesData()(flavor text, genus, legendary and mythical flags, alternate varieties),fetchCryUrl(), andfetchFormSprites()(normal and shiny pair). Every function caches in aMapand resolves to safe empty values on failure, so PokeAPI being down degrades the page rather than breaking it. -
typecolors.ts: theTYPE_COLORSpalette andtypeBadge()factory for the colored type pills. -
costumes.ts: costume resolution for the shiny collection page and the Pokemon box. Rather than carrying its own table, it importsinternal/costumes/catalog.jsonandinternal/costumes/labels.jsondirectly, the same two files the servergo:embeds, so both sides resolve from one source and cannot drift (there is no generated Go mirror any more).catalog.jsonis machine-generated from the mined asset tree (which costume codes and shiny art exist, and which species can wear each),labels.jsonis the curated human labels. Templates inject the merged label set (curated file plus admin-named overlay) as aCOSTUME_LABELSglobal so the picker knows about costumes named since the last deploy. Costumed sprites are served from the app's own proxy at/api/costume-sprite/{file}, never hotlinked. -
regionalForms.ts: the per-species regional and battle-forme table (Alolan, Galarian, Hisuian, Paldean, and other formes) with the PokeAPI variant sprite ids each one uses; drives the region picker on the shiny features and is mirrored server-side byinternal/handlers/regional.go. -
evolutions.ts: the evolution-target data for the shiny tracker's evolve action; it gained aREGIONAL_EVOLUTION_NEXTmap so region-aware evolution lines resolve to the correct regional target. -
types.ts: the shared interfaces (GameData,PokemonStat,RaidBoss, move types, and so on).
regionalForms.ts (including its VIVILLON_PATTERNS and UNOWN_LETTERS tables) is a hand-maintained table that the server mirrors in internal/handlers/regional.go; when you change one, update the other in the same change or the browser and server will disagree on what is valid (a unit test guards against silent drift). Costumes no longer need this dance: costumes.ts and the server both read the shared internal/costumes JSON, so there is a single source of truth and nothing to regenerate.
templates/base.html defines the base layout: head, nav sidebar, language switcher, footer, and the particle background. Pages define three blocks: title, content, and scripts. A typical page template is small: a header, a mount div like <div id="raids-app"> with a loading spinner, and a scripts block that loads the bundle.
Things every page gets from the layout and PageData (defined in internal/handlers/handlers.go):
-
PageData fields:
User(nil when logged out),Data(page specific payload),CSRFToken,StoreEnabled,Maintenance(per feature kill switches that templates check before rendering nav links and sections),Lang,Langs, andAssetVersion. -
CSRF:
<meta name="csrf-token" content="{{.CSRFToken}}">in the head; scripts read it and send it as theX-CSRF-Tokenheader on mutating fetches. -
SITE_LANG: injected as a global bybase.htmlfor client side name localization and date formatting. -
Inline globals: bundles cannot call
{{T}}, so templates define objects of pre-translated strings in theirscriptsblock (DPin dps,EVin events,RF/RF2/RAID_CTXin trainers,TL_STRINGSin translate). Auth context is passed the same way when a page needs to know the login state at runtime (DPS_CTXin dps,RAID_CTXin trainers). See Localization. -
Asset versioning: every static asset URL carries
?v={{.AssetVersion}}, a hash of the contents ofstatic/cssandstatic/jscomputed at startup, so browser caches bust themselves on deploy.
All styling is one file, static/css/main.css, themed by variables in :root: --bg (#07071a), --surface and --surface-2 (translucent whites), --border, --text (#ebebf5), --text-2 (#8888b8), --text-3 (#55558a), accent colors --red (#e53935), --blue (#5b9cf6), --green (#00d68f), --gold (#ffb340), radii --r (14px), --r-sm, --r-pill, the glass effect --blur (blur(18px) saturate(160%)), the --font stack (Inter first), and --trans for transitions.
The signature look is the dark glassmorphism card, composed from those variables:
.some-card {
background: var(--surface);
border: 1px solid var(--border);
border-radius: var(--r);
backdrop-filter: var(--blur);
-webkit-backdrop-filter: var(--blur);
}Grids are responsive via grid-template-columns: repeat(auto-fill, minmax(...)) patterns, and several views swap layout entirely at a 768px breakpoint (for example the DPS Compare table becomes stacked cards). A global text shadow outline keeps text readable over the rotating background images; form controls opt out of it.
-
Template: create
templates/mypage.htmlwithtitle,content(a mount div plus loading state), andscriptsblocks. -
TypeScript entrypoint: create
ts/mypage.ts; useloadGameData()and the shared modules rather than refetching. -
Build line: add
ts/mypage.tsto both thebuildandwatchscripts inpackage.json, then rebuild. -
Go route and handler: add a handler that calls
h.render(w, r, "mypage", data)and register the route ininternal/server/server.go(wrap withRequireAuthor a maintenance check as appropriate). -
Navbar link: add the link in
templates/base.html, gated on the relevant maintenance flag. -
Locale keys: add
nav.mypage,mypage.title, and the rest tointernal/i18n/locales/en.jsonfirst; see Localization.
-
static/js/is gitignored. Bundles are build artifacts; always runnpm run buildafter pulling or before deploying. A stale bundle is the usual cause of "my change does nothing". -
Sprites fail gracefully. Sprite
<img>elements attach error handlers that hide the image, because PokeAPI's repository does not have a sprite for every form ID. Follow the same pattern for new images. -
localStorage is used sparingly. The DPS Calculator saves the selected target Pokemon to
dps_calc_targetanddps_cmp_targetfor logged-in users. The notifications system usesnotif_seen_lobbies(seen toast IDs, capped at 200) andnotif_dismissed(page-dismissed lobby IDs, capped at 500), andnotif_sound_volume(0-100 integer) for the ding volume. All other client state either lives server side or is a plain module variable that resets on reload. - Locale files are BOM sensitive; if you script edits to them on Windows, read the warning in Localization.
Repository | Live site | hailsDotGO is a fan-made project, not affiliated with, endorsed by, or connected to Niantic or The Pokémon Company. Game data comes from community sources credited on the Data Sources page.
Start Here
Features
- Raids and Counters
- DPS Calculator
- IV Calculator
- PvP IV Ranker
- Events
- Shiny Tracking
- Trainer Directory
- Raid Finder
- Social Features
- Trust and Awards
- Bug Reports
- Player Reports
- Store
- Accounts and Roles
- Admin Guide
Self-Hosting
Development
Translations