-
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/shinydex.ts ts/raidfinder.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/shinydex.ts |
shinydex.js |
templates/shinydex.html |
/shinydex (Shiny-Tracking) |
ts/raidfinder.ts |
raidfinder.js |
templates/trainers.html |
/trainers, Raid Finder tab (Raid-Finder) |
The Trainer Directory tab of /trainers and pages like settings, admin, and translate 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. -
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. -
types.ts-- the shared interfaces (GameData,PokemonStat,RaidBoss, move types, and so on).
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 i18n globals: bundles cannot call
{{T}}, so templates define objects of pre-translated strings in theirscriptsblock (EVin events,RF/RF2/RAID_CTXin trainers,TL_STRINGSin translate). 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. - No localStorage. Client state that matters lives server side; ephemeral UI state (like the cry volume slider on the shiny pages, which scales 0 to 100 percent onto a 0.3 maximum volume) is a plain module variable and 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