forked from c-fu/arah2
120 lines
8.8 KiB
Markdown
120 lines
8.8 KiB
Markdown
---
|
||
phase: 01-design-tokens-rams-foundation
|
||
plan: 03
|
||
type: execute
|
||
wave: 3
|
||
depends_on: ["01-01", "01-02"]
|
||
files_modified:
|
||
- theme/basemap/liberty-rams.json
|
||
- theme/basemap/build.mjs
|
||
- theme/basemap/index.html
|
||
- theme/package.json
|
||
autonomous: true
|
||
requirements: [DSGN-01]
|
||
user_setup: []
|
||
must_haves:
|
||
decisions: [D-08, D-09, D-10]
|
||
truths:
|
||
- "A Rams-styled MapLibre basemap renders in a browser: grayscale/muted land + water, desaturated roads, muted labels, zero signal-orange in the basemap"
|
||
- "Basemap values are derived from the token package, not hand-tuned hex in the style JSON (D-09)"
|
||
- "Map labels use the self-hosted Space Grotesk / Geist assets (D-10)"
|
||
artifacts:
|
||
- path: "theme/basemap/build.mjs"
|
||
provides: "Generates liberty-rams.json by reading token values from theme/src/tokens.css"
|
||
contains: "tokens.css"
|
||
- path: "theme/basemap/liberty-rams.json"
|
||
provides: "Forked OpenFreeMap liberty style with Rams cartography"
|
||
contains: "glyphs"
|
||
- path: "theme/basemap/index.html"
|
||
provides: "MapLibre GL browser view proving the basemap renders (success criterion 3)"
|
||
contains: "maplibre-gl"
|
||
key_links:
|
||
- from: "theme/basemap/build.mjs"
|
||
to: "theme/src/tokens.css"
|
||
via: "parse token custom properties"
|
||
pattern: "tokens\\.css"
|
||
- from: "theme/basemap/index.html"
|
||
to: "theme/basemap/liberty-rams.json"
|
||
via: "MapLibre style URL"
|
||
pattern: "liberty-rams\\.json"
|
||
---
|
||
|
||
<objective>
|
||
Fork the OpenFreeMap `liberty` basemap style into a Rams-constant cartography output: grayscale/muted grounding, desaturated roads, muted labels in Rams type, `signal-orange` reserved exclusively for the future route line (not used in the basemap itself). Prove it renders in a browser with MapLibre GL.
|
||
|
||
Purpose: D-08 (full Rams pass on basemap), D-09 (derived from tokens, not hand-tuned), D-10 (Rams font assets for labels), roadmap success criterion 3.
|
||
Output: `theme/basemap/` — build script, generated style JSON, browser proof page.
|
||
</objective>
|
||
|
||
<read_first>
|
||
@planning/phases/01-design-tokens-rams-foundation/01-CONTEXT.md
|
||
@planning/phases/01-design-tokens-rams-foundation/01-01-SUMMARY.md
|
||
@stitch_minimalist_route_navigator/telemetry_rams/DESIGN.md
|
||
</read_first>
|
||
|
||
<interfaces>
|
||
Derivation targets (map token → cartography role per D-08 / telemetry_rams/DESIGN.md "Grayscale Map Grounding"):
|
||
- Land/background ground tone: between `soft-beige` (#D9D2C6) and `chassis-subtle` (#CCC4B5)
|
||
- Water: desaturated mid-gray derived from `matte-black` mixed toward low-saturation (rgba derived, NOT brand hues)
|
||
- Roads: desaturated, hierarchy by weight only (major roads slightly darker than minor)
|
||
- Buildings: light gray within the `#D9D2C6 → #1A1A1A` calibrated gray band
|
||
- Labels: `matte-black` at reduced opacity; fonts Space Grotesk (major) / Geist (minor) — same assets as theme/fonts (D-10)
|
||
- `signal-orange` (#EB8008), `functional-red` (#ED3F1C), `olive-green` (#736B1E) must NOT appear in the basemap — accent is reserved exclusively for the route line rendered in Phase 5 (D-08).
|
||
|
||
Style input: `https://tiles.openfreemap.org/styles/liberty` style JSON (fetch at build time and fork; do not vendor the full original JSON — build.mjs fetches, transforms, writes).
|
||
</interfaces>
|
||
|
||
<tasks>
|
||
|
||
<task type="auto">
|
||
<name>task 1: basemap build script deriving style from tokens</name>
|
||
<files>theme/basemap/build.mjs, theme/basemap/liberty-rams.json</files>
|
||
<action>
|
||
1. Create `theme/basemap/build.mjs` (Node ESM, no external deps beyond node builtins): fetches `https://tiles.openfreemap.org/styles/liberty` style JSON, transforms it, writes `theme/basemap/liberty-rams.json`.
|
||
2. Derivation step: parse `theme/src/tokens.css` extracting the `--color-*` custom property values (deterministic regex `-\-color-([a-z0-9-]+):\s*(#[0-9A-Fa-f]{6})`). Compute the Rams cartography palette IN SCRIPT (D-09: derived, not hand-tuned): desaturate all land/road/water/building colors to the calibrated gray band via a small `desaturate(hex, satMultiplier)` helper (pure function: convert hex→HSL, multiply saturation toward ~0.06–0.12, adjust lightness per role per Interfaces table). Map transformed values onto the liberty style's `layers[].paint` color properties (`background`, `water`, `road-*`, `building-*`, `landcover-*`, `bridge-*`).
|
||
3. FONT STEP: replace liberty's glyph/name font stacks. Point the style's `glyphs` to the OpenFreeMap hosted glyphs endpoint (keep `https://tiles.openfreemap.org/fonts/{fontstack}/{range}.pbf` to avoid self-hosting pbf glyphs in Phase 1), and rewrite every `layout["text-font"]` value to `"Space Grotesk Medium"` for major label layers (place-city, place-subtown and above) and `"Noto Sans Regular"` fallback chain ending `"Space Grotesk Regular"`… — IMPORTANT constraint: OpenFreeMap glyphs server only serves font stacks it hosts. If `Space Grotesk` is not available on the glyphs endpoint, detect a 404 at build time and fall back to the closest hosted stack, recording the substitution in a `"metadata"` block (`"rams-font-substitutions": {...}`) — this keeps D-10 satisfied in spirit (Rams typography for labels) with honest fallback provenance.
|
||
4. Common concerns: signal-orange/functional-red/olive-green token names are excluded from the derivation set (guard: throw if any of those 3 hex values end up anywhere in the output layers' paint properties).
|
||
5. Add npm script to `theme/package.json`: `"basemap": "node basemap/build.mjs"`.
|
||
6. Run it; verify `theme/basemap/liberty-rams.json` exists, is valid JSON, has `version: 8`, `glyphs`, `sources`, `layers[0].paint` uses a transformed neutral.
|
||
</action>
|
||
<verify><automated>npm run basemap --workspace theme && node -e "const s=require('./theme/basemap/liberty-rams.json'); if(!s.layers||!s.glyphs) process.exit(1); const rgx=/EB8008|ED3F1C|736B1E/i; const bad=JSON.stringify(s.layers).match(rgx); if(bad){process.exit(2)} console.log('OK layers='+s.layers.length)"</automated></verify>
|
||
<done>build.mjs derives all basemap colors from tokens.css (no hardcoded cartography hex in the script beyond token parsing), output JSON is a valid MapLibre style, and none of the 3 accent colors appear anywhere in the layer paint properties.</done>
|
||
</task>
|
||
|
||
<task type="auto">
|
||
<name>task 2: browser proof page (dummy route + rendered basemap)</name>
|
||
<files>theme/basemap/index.html, theme/package.json</files>
|
||
<action>
|
||
1. Create `theme/basemap/index.html`: static MapLibre GL viewer (CDN unpkg `maplibre-gl@^4`), `<style>` sets page chrome to Rams tokens (`var(--color-surface)` background etc. — import `../dist/rams.css`), init map centered ~3.1513, 101.6958 (KLCC / KL metro, per project region), zoom ~13, style = `./liberty-rams.json`.
|
||
2. Draw a placeholder route polyline in `signal-orange` (raw tolerance: the ROUTE line is exactly where signal-orange is allowed per D-08) with a thick 6px stroke so the visual "grayscale guiderail + orange vector" intent is previewable, labeled in a README comment as consumed by Phase 5.
|
||
3. Add `"basemap:dev": "npx serve theme/basemap || true"`? No extra server dep — just use `python3 -m http.server` invocation pattern for the verify command instead; document the open command in index.html's comment header. Add npm script `"basemap:preview": "cd theme/basemap && python3 -m http.server 8123"`.
|
||
4. Layer order: route layer added LAST in the layer stack so it sits above mapcar/roads but below any label overlap is not required here — keep simple.
|
||
</action>
|
||
<verify><automated>test -f theme/basemap/index.html && grep -q "maplibre" theme/basemap/index.html && grep -q "liberty-rams.json" theme/basemap/index.html && grep -qE "3\.15|101\.69" theme/basemap/index.html</automated></verify>
|
||
<done>index.html loads liberty-rams.json, centers KLCC, draws a signal-orange route placeholder, and is checkable via `npm run basemap:preview --workspace theme` → http://localhost:8123 (human eyes verify the grayscale+muted+Rams feel, success criterion 3).</done>
|
||
</task>
|
||
|
||
</tasks>
|
||
|
||
<threat_model>
|
||
## Trust Boundaries
|
||
| Boundary | Description |
|
||
|----------|-------------|
|
||
| basemap build → OpenFreeMap API | build.mjs fetches external style JSON across network |
|
||
</threat_model>
|
||
|
||
<verification>
|
||
- `npm run basemap --workspace theme` → 0 exit, liberty-rams.json regenerated
|
||
- No accent hex (#EB8008 / #ED3F1C / #736B1E) anywhere in layers' paint
|
||
- index.html renders the basemap via the preview script
|
||
- `grep -c "tokens.css" theme/basemap/build.mjs` ≥ 1 (derivation provenance)
|
||
</verification>
|
||
|
||
<success_criteria>
|
||
Rams basemap derived from (not decorated with) the token package renders in a browser at KL metro with muted grayscale grounding, Rams label fonts, and zero accent-color leaks into cartography. Route accent visually confirmed against the grayscale base (success criterion 3 of the roadmap).
|
||
</success_criteria>
|
||
|
||
<output>
|
||
After completion, create `.planning/phases/01-design-tokens-rams-foundation/01-03-SUMMARY.md`
|
||
</output>
|