Design¶
Files¶
site/static/:index.html(the map),about.html,support.html(behind "apoie"/Donate),404.html(forwards oldmarola.dev/docs/*links todocs.marola.dev),favicon.svg.site/static/app.js: fetchesdata/areas.jsonand each area's board, builds the map (Mapbox GL JS), the beach markers, the list, the card, the hour slider and the satellite/NASA GIBS raster layers.ui.js: language resolution andt()(MIP-0054).flow.js: the WebGL wind/wave particle layer, a Mapbox GL custom layer interpolated from the board's own readings (no weather grid fetched).i18n.js: generated byscripts/i18n_bundle.pyfromsite/i18n/*.json— edit the catalogs, never this file.site/static/mapbox-config.js: empty in the repo (window.MAROLA_MAPBOX = { token: "", style: "" });site.ymlrewrites it at deploy fromMAPBOX_PUBLIC_TOKEN/MAPBOX_STYLE(scripts/mapbox_config.sh).site/static/chatbot-config.jsandchat.js: the chat widget (the chat widget).site/static/vendor/: vendored third-party assets, no build step (libraries).site/areas.json: the areas the boards are built for (reference).site/fixtures/: the board the test harness (scripts/site_check.js) renders, plus a schema fixture.site/board.schema.json: the board contract, vendored from the pinned image (reference).site/i18n/:pt-BR.json(source locale),en.json(translated),context.json(a note per key);scripts/i18n_bundle.pychecks and bundles them (development).
The board, beach by beach, and markers¶
app.js's colour()/band() turn each beach's score into one of five bands: no data (score
null/undefined), unfit (unfit or score <= 0), 1–39, 40–69, or ≥70 — the same bands the hour
bar's legend shows. A map marker is a 16 px SVG dot (waveIcon) with a white ring, the band's
colour (DESIGN.md's --c70…--cna tokens) written straight into its fill
attribute; the selected beach grows to 32 px and shows its score. Everywhere else the band is a
CSS class, never inline style= (which the CSP below blocks): list/card score chips use
.c70/.c40/.c1/.c0/.cna; the water layer's sampling-point markers use
wpoint c0/wpoint cna/wpoint c70; a beach's tooltip and its card's inline water-status dot use
wdot c0/wdot cna/wdot c70 (waterDotClass). The water layer draws every sampling point in
the area, in the same three bands (proper, improper, unclassified).
CSP and the no-third-party rule¶
index.html's page keeps default-src 'self'; img-src 'self' data: blob: https:; connect-src
'self' https:; worker-src 'self'; object-src 'none' (about.html/support.html, which carry no
map, drop connect-src/worker-src: default-src 'self'; img-src 'self' https:; object-src
'none'). script-src has no override, so it inherits default-src 'self': every script is
same-origin, including Mapbox GL JS's own worker (vendor/mapbox-gl-csp.js is the CSP build,
vendor/mapbox-gl-csp-worker.js loads from vendor/, not a blob:, which the non-CSP build
needs).
The page's only two third-party origins (AGENTS.md's Code style section) are:
- Mapbox: the base map (style, tiles, fonts), billed per map load above its free tier once a
token is set (
mapbox-config.js, the tile policy). - NASA GIBS: the four satellite raster layers (clouds, sea-surface temperature, anomaly,
El Niño), free and keyless, fetched only once a visitor picks one of those layers
(
app.js'srenderRaster).
img-src … https: and blob: are how Mapbox GL decodes map imagery; connect-src … https: lets
the page reach Mapbox's tile/style/event endpoints and, when set, the chat widget's own endpoint
(chatbot-config.js). No other third-party script, font or analytics origin is loaded — Inter is
self-hosted (vendor/fonts/), not pulled from a font CDN.