@kahwee/sf-map-svg 4.0.1 → 4.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/CHANGELOG.md +36 -0
  2. package/README.md +9 -13
  3. package/dist/data/guide/coast.json +1 -1
  4. package/dist/data/guide/landmarks.json +1 -1
  5. package/dist/data/guide/neighborhoods-realtor.json +1 -1
  6. package/dist/data/stations.d.ts +2 -2
  7. package/dist/data/types.d.ts +8 -1
  8. package/dist/src/api.d.ts +1 -1
  9. package/dist/src/api.js +1 -1
  10. package/dist/src/appearance.d.ts +5 -0
  11. package/dist/src/appearance.js +54 -0
  12. package/dist/src/configuration.js +50 -25
  13. package/dist/src/controller-types.d.ts +53 -6
  14. package/dist/src/district-morph.d.ts +17 -0
  15. package/dist/src/district-morph.js +148 -0
  16. package/dist/src/explorer-core.d.ts +14 -2
  17. package/dist/src/explorer-core.js +233 -52
  18. package/dist/src/features.d.ts +6 -0
  19. package/dist/src/features.js +11 -1
  20. package/dist/src/guide-map.js +1 -1
  21. package/dist/src/guide-static.js +3 -3
  22. package/dist/src/layer-transition.d.ts +18 -0
  23. package/dist/src/layer-transition.js +102 -0
  24. package/dist/src/layers.d.ts +9 -7
  25. package/dist/src/layers.js +28 -18
  26. package/dist/src/map-core.d.ts +14 -0
  27. package/dist/src/map-core.js +11 -5
  28. package/dist/src/map.js +125 -20
  29. package/dist/src/overlays.d.ts +3 -4
  30. package/dist/src/overlays.js +5 -1
  31. package/dist/src/static-animation.d.ts +13 -0
  32. package/dist/src/static-animation.js +36 -0
  33. package/dist/src/static.d.ts +1 -1
  34. package/dist/src/static.js +31 -2
  35. package/dist/src/transit.js +2 -1
  36. package/dist/src/types.d.ts +33 -2
  37. package/dist/src/validation.js +2 -0
  38. package/docs/API.md +85 -0
  39. package/docs/api-audit.md +3 -1
  40. package/docs/consumer-integration.md +1 -1
  41. package/docs/developer-guide.md +139 -0
  42. package/docs/guide-bundle-report.md +4 -4
  43. package/llms-full.txt +1979 -0
  44. package/llms.txt +25 -0
  45. package/package.json +12 -5
@@ -33,6 +33,9 @@ export interface MapOverlay {
33
33
  label?: string;
34
34
  }
35
35
  export interface SFMapOptions {
36
+ /** Grouped presentation; supplied keys take precedence over flat compatibility options. */
37
+ layers?: StaticMapLayers;
38
+ appearance?: StaticMapAppearance;
36
39
  theme?: 'districts' | 'transit';
37
40
  width?: number;
38
41
  height?: number;
@@ -51,12 +54,25 @@ export interface SFMapOptions {
51
54
  roadLabels?: boolean;
52
55
  landmarks?: boolean;
53
56
  bartStations?: boolean;
54
- markers?: MapMarker[];
57
+ markers?: readonly MapMarker[];
55
58
  overlays?: readonly MapOverlay[];
56
59
  title?: string;
57
60
  idPrefix?: string;
61
+ /**
62
+ * Opt-in, self-contained CSS choreography for static SVGs: land fades in, the coast and
63
+ * lines draw, districts grow, then labels and points appear. It plays when the SVG is
64
+ * inserted into a page or loaded as an image, and stays still under reduced motion.
65
+ * `duration` (default 2400 ms) scales the whole sequence; `delay` offsets it.
66
+ */
67
+ animation?: boolean | {
68
+ duration?: number;
69
+ delay?: number;
70
+ };
58
71
  colors?: Partial<Record<'water' | 'land' | 'district' | 'neighborhood' | 'highway' | 'road' | 'park' | 'landmark' | 'bart' | 'label' | 'marker' | 'selected', string>>;
59
72
  }
73
+ /** Presentation supported by both the static and interactive renderers. */
74
+ export type StaticMapLayers = Omit<InteractiveLayers, 'neighborhoodLabels'>;
75
+ export type StaticMapAppearance = Pick<SFMapOptions, 'theme' | 'colors' | 'districtStyle'>;
60
76
  /** Serializable viewport in the interactive map's fixed 800 × 800 projected space. */
61
77
  export type MapViewport = readonly [x: number, y: number, size: number];
62
78
  export type ExplorerMode = 'neighborhoods' | 'districts' | 'basemap';
@@ -104,6 +120,21 @@ export interface MapFeatures {
104
120
  };
105
121
  northArrow?: boolean;
106
122
  scaleBar?: boolean;
123
+ /**
124
+ * Fade layers as `setLayers` and `setMode` switch them, crossfade neighborhood boundaries
125
+ * when `setSource` changes the definition, and crossfade district fills on
126
+ * `setDistrictStyle`. Reduced motion takes precedence.
127
+ */
128
+ layerTransitions?: boolean | {
129
+ duration?: number;
130
+ };
131
+ /**
132
+ * Morph district outlines from one map year to the next on `setDistrictYear`; pass
133
+ * `{ animate: false }` for an instant change. Reduced motion takes precedence.
134
+ */
135
+ districtMorph?: boolean | {
136
+ duration?: number;
137
+ };
107
138
  }
108
139
  export interface NeighborhoodExplorerOptions extends MapFeatures {
109
140
  mode?: ExplorerMode;
@@ -220,4 +251,4 @@ export interface NeighborhoodExplorerElement extends HTMLElement {
220
251
  export interface TransitAnimationElement extends HTMLElement {
221
252
  destroy(): void;
222
253
  }
223
- export type { MapAppearance, MapCamera, MapConfiguration, MapController, MapEvents, MapOptions, } from './controller-types.js';
254
+ export type { MapAppearance, MapCamera, MapCapabilities, MapConfiguration, MapConfigurationSnapshot, MapController, MapControls, MapEvents, MapOptions, MapSelectionChange, ResolvedMapConfiguration, } from './controller-types.js';
@@ -42,6 +42,8 @@ const optionKeys = [
42
42
  'attribution',
43
43
  'northArrow',
44
44
  'scaleBar',
45
+ 'layerTransitions',
46
+ 'districtMorph',
45
47
  'interface',
46
48
  'layers',
47
49
  'selectableNeighborhoods',
package/docs/API.md ADDED
@@ -0,0 +1,85 @@
1
+ # Map options and lifecycle
2
+
3
+ Start with the [static and interactive examples](../README.md). Exact option
4
+ and event types are exported by [src/api.ts](../src/api.ts) and defined in
5
+ [src/types.ts](../src/types.ts) and [src/controller-types.ts](../src/controller-types.ts).
6
+
7
+ ## Static maps
8
+
9
+ Static maps accept the same grouped presentation vocabulary as interactive maps:
10
+
11
+ ```ts
12
+ const presentation = {
13
+ layers: { landmarks: true, bartStations: true },
14
+ appearance: { theme: 'districts' as const, colors: { water: '#e7f0f3' } },
15
+ };
16
+ const { svg } = renderMap(staticMapData, presentation);
17
+ const map = createMap(fullMapData, presentation);
18
+ ```
19
+
20
+ Static `layers` supports every interactive layer switch except `neighborhoodLabels`. Static `appearance` supports `theme`, `colors`, and `districtStyle`; browser typography, area interaction styles, and screen-space marker sizing remain interactive settings. Unknown grouped keys are rejected. Supplied grouped keys override flat compatibility keys; omitted or `undefined` grouped keys preserve the flat value or renderer default. The two renderers retain their existing defaults. Marker arrays accept readonly inputs.
21
+
22
+ Flat compatibility options include `theme`, `width`, `height`, `padding`, `year` (2002, 2012, 2022), `districtLines`, `districtFills`, `districtStyle`, `districtLabels`, `neighborhoodLines`, `labels`, `highways`, `keyRoads`, `roadLabels`, `landmarks`, `bartStations`, `markers`, `overlays`, `title`, `idPrefix`, `colors`, and `animation`. Each optional layer is independent. User-supplied text and attributes are escaped in SVG output.
23
+
24
+ Custom district IDs must be finite numbers. An empty `labelPoints` array uses the district's primary `label` position.
25
+
26
+ `animation: true` (or `{ duration, delay }`) makes a static map draw itself with self-contained, scoped CSS: land fades in, the coast and lines draw, districts grow, then labels and points appear. It plays when the SVG is inserted into a page or loaded as an image, needs no JavaScript, and stays still under `prefers-reduced-motion`. Re-insert the markup to replay it. Default output is unchanged.
27
+
28
+ ## Interactive maps
29
+
30
+ `createMap` defaults to `mode: 'basemap'` and `appearance.theme: 'transit'`. Set the mode explicitly for neighborhood or district exploration. District and neighborhood layer defaults follow the mode; other supplied layers default on. The [developer guide](developer-guide.md) compares static and interactive defaults and explains configuration resets, coordinates, and cleanup.
31
+
32
+ Construction options include `mode` (`basemap`, `neighborhoods`, or `districts`), `source`, `neighborhood`, `year`, `labels`, `layers`, `controls`, `features`, `appearance`, `markers`, `overlays`, `legend`, `strings`, `attribution`, and `fitPadding`. Enable map touch gestures with the touch control or `map.setTouchNavigation(true)`. `features` holds motion, marker entrances, selected marker rings, clustering, north arrow, scale bar, layer transitions, and district morphs. `appearance` holds theme, color tokens, label and area styles, marker colors and sizes, and district styling. `layers` controls district fill/line/labels, neighborhood lines/labels, landmarks, BART, highways, key roads, and road labels. See the exported `MapOptions` type for exact values and the [Storybook examples](../stories/) for live controls.
33
+
34
+ The controller also supports marker, neighborhood, and district selection; district year and style changes; source and mode changes; labels and touch navigation; screen projection; and typed `markerchange`, `neighborhoodchange`, `districtchange`, `districthover`, `districtactivate`, `districtyearchange`, `overlayactivate`, `clusteractivate`, `viewportchange`, and `mapresize` events. `destroy()` releases browser resources; operations after destruction throw.
35
+
36
+ If a district callback destroys the map or selects a newer district, the interrupted activation stops without moving keyboard focus or sending a stale event.
37
+
38
+ `setMarkers()` reconciles by stable marker `id`: retained markers keep their DOM nodes, focus, and in-progress entrance animations; only new IDs animate in. Identical ordered marker updates are a visual no-op. Camera state and `viewportchange` events remain synchronous, while animated camera steps and their dependent label/marker layout commit in the same browser frame.
39
+
40
+ For a small guide, import `guideMapData` from `/guide/data` and pass it to `createMap`. The optional `/guide` entry also provides `createGuideMap`, `mountGuideMap`, and detailed-data loading for existing guide layouts. `/transit` provides the standalone schematic transit animation. See [examples](EXAMPLES.md) and [consumer integration](consumer-integration.md).
41
+
42
+ For the guide preset with the same controller API, use `createGuideController(options)` from `/guide` (or `/guide/map`). It accepts grouped `MapOptions` and returns `MapController`; `mountGuideController(shell, options)` enhances a `createGuideShell()` container. Both use the lightweight guide geography. Existing `createGuideMap` and `mountGuideMap` calls retain their element-based API.
43
+
44
+ Two opt-in features animate the map's lines and layers. `features.layerTransitions` fades layers as `configure({ layers })` and `setMode()` switch them, crossfades neighborhood boundaries when `setSource()` changes the definition, and crossfades district fills on `setDistrictStyle()`. `features.districtMorph` moves district outlines from one map year to the next on `setDistrictYear()`, then settles on the exact published geometry; pass `{ animate: false }` for an instant change. Both default off, own and cancel their animations, and yield to reduced motion.
45
+
46
+ District style callbacks are evaluated once per district at construction and on style or year updates. Hover, selection, and layer toggles reuse the prepared styles; call `setDistrictStyle()` again when external styling data changes. Invalid styles leave the current map unchanged. District fades respect reduced motion and are removed on interruption or destruction.
47
+
48
+ ## Live configuration and inspection
49
+
50
+ `map.configure({ appearance, mode, source, year, labels, layers, features, controls })` updates an existing map. Appearance updates preserve the camera, selections, retained marker nodes, and keyboard focus. Mode/source changes through `configure` preserve the camera; existing `setMode` and `setSource` convenience methods retain their reset behavior. Switching neighborhood source clears the neighborhood selection. Changing district year retains a selected district when it exists in the new year. Unsupported datasets, invalid appearance values, and invalid district-style callback results fail during preparation before commit. A reentrant styling callback can supersede the pending patch.
51
+
52
+ Appearance token objects (`colors`, `style`, `labelStyle`, `labelSize`, `areaStyle`) merge supplied keys; other appearance properties replace. Omitted keys retain their value, an explicit `undefined` property removes that override, and `appearance: undefined` resets the whole appearance group. Feature objects retain their existing replacement semantics. Mode/source/year/labels ignore `undefined`; reset them with an explicit value. Patch types explicitly permit these resets with TypeScript’s `exactOptionalPropertyTypes` enabled.
53
+
54
+ ```ts
55
+ map.configure({
56
+ appearance: { colors: { water: '#142d45' }, labelStyle: { fontFamily: 'Georgia,serif' } },
57
+ layers: { bartStations: false },
58
+ });
59
+ map.configure({ appearance: undefined }); // restore construction defaults, not initial overrides
60
+
61
+ const overrides = map.getConfiguration();
62
+ const effective = map.getResolvedConfiguration();
63
+ const available = map.getCapabilities();
64
+ ```
65
+
66
+ `getConfiguration()` returns detached feature, layer, control, and appearance overrides; district style callbacks retain their function identity. `getResolvedConfiguration()` adds the current mode, source, year and master labels and resolves **layer switches** through mode defaults, available data, and the master label switch. Appearance/control fields remain overrides. It does not claim that every label is visible: zoom and collision filtering still apply. `getCapabilities()` returns supplied `sources`, usable district `years`, and layer data availability for the current source/year, independent of visibility overrides. These reads throw after destruction.
67
+
68
+ ## Common selection event
69
+
70
+ `selectionchange` adds a consistent envelope without changing existing event payloads:
71
+
72
+ ```ts
73
+ map.on('selectionchange', (event) => {
74
+ // kind narrows current and previous to the corresponding selection type.
75
+ if (event.kind === 'marker') console.log(event.current?.id, event.previous?.id);
76
+ });
77
+ ```
78
+
79
+ `kind` is `marker`, `neighborhood`, or `district`. `current` and `previous` are detached selection snapshots or `null`. The event follows committed selection changes, including clearing and a changed district vintage; it does not represent every activation. Existing `markerchange`, `neighborhoodchange`, and `districtchange` events remain supported.
80
+
81
+ ## Coordinates and units
82
+
83
+ GeoJSON and static projection inputs use `[longitude, latitude]` in WGS84; markers use `{ lng, lat }`. Static `project` returns SVG units. `projectToScreen(lng, lat)` returns pixels relative to the mounted canvas. Camera viewports and pan deltas use the fixed 800 × 800 projected map space, independent of canvas size. Marker radius uses screen pixels in browser maps and SVG units in static output.
84
+
85
+ Explore presentation options in the [live map design playground](https://kahwee.github.io/sf-map-svg/playground.html). The page identifies its package version; released builds use compatible options for that installed version.
package/docs/api-audit.md CHANGED
@@ -54,7 +54,7 @@ exceptions are not transactional validation errors and do not roll back an alrea
54
54
  committed change. Direct mutation of the returned DOM can invalidate invariants;
55
55
  use the public methods and `overlayElement` extension point.
56
56
 
57
- The generic static renderer retains its existing broader option surface; strict
57
+ Static grouped layers and appearance reject unknown keys, while flat compatibility options retain their existing surface; strict
58
58
  interactive option-key validation is not a claim that all static options share the
59
59
  same schema. Host layout, delayed images, fonts, and application cards need their
60
60
  own CLS tests. The scale bar is an approximate local Mercator scale, not survey
@@ -76,3 +76,5 @@ public types change. Update this matrix when introducing a new interaction.
76
76
  - `pnpm test:stories:coverage` runs Chromium stories and reports browser coverage.
77
77
  - `pnpm demo`, `pnpm build-storybook`, `pnpm test:package`, and `pnpm test:visual` cover examples, rendered stories, packed consumer imports, and reviewed map screenshots.
78
78
  - The packed consumer asserts removed compatibility entry points are absent.
79
+
80
+ Live appearance patches prepare values, district styles and source replacement before committing; token objects merge by key. Styling callbacks that issue a newer presentation update supersede the pending patch. Resolved layer reads distinguish visibility settings from data availability and from zoom/collision label filtering. The additive selection envelope preserves existing event contracts.
@@ -1,6 +1,6 @@
1
1
  # Consumer integration
2
2
 
3
- Use the version 3 root controller and explicit geographic data. The [migration guide](migration-v3.md) maps removed imports to supported calls.
3
+ Use the root controller and import geographic data explicitly. The [migration guide](migration-v3.md) maps removed v2 imports to supported calls.
4
4
 
5
5
  For a compact browser guide:
6
6
 
@@ -0,0 +1,139 @@
1
+ # Developer guide
2
+
3
+ Choose the renderer and geography separately. `renderMap` produces a string without a DOM; `createMap` produces a browser controller whose `.element` you mount. Neither root function imports geography for you.
4
+
5
+ ## Choose an entry point
6
+
7
+ | Task | Renderer | Data |
8
+ | --- | --- | --- |
9
+ | Generate an SVG file or server-rendered image | `/static`: `renderMap` | `/data/static`: `staticMapData` |
10
+ | Explore districts or several neighborhood definitions | `/map`: `createMap` | `/data/full`: `fullMapData` |
11
+ | Embed a smaller neighborhood guide | `/guide/map`: `createGuideController` | Included lightweight guide geography |
12
+ | Use the guide data with the general controller | `/map`: `createMap` | `/guide/data`: `guideMapData` |
13
+ | Look up names without mounting a map | `/data/lookup` | Source-aware lookup helpers |
14
+
15
+ Imports starting with `/` in this table are package suffixes, such as `@kahwee/sf-map-svg/static`. Node 24+ is required for server rendering. Browser examples need a DOM and a bundler that supports JSON modules. This is an ESM package.
16
+
17
+ ## Write a static SVG
18
+
19
+ ```ts
20
+ import { writeFile } from 'node:fs/promises';
21
+ import { renderMap } from '@kahwee/sf-map-svg/static';
22
+ import { staticMapData } from '@kahwee/sf-map-svg/data/static';
23
+
24
+ const { svg } = renderMap(staticMapData, {
25
+ year: 2022,
26
+ landmarks: true,
27
+ bartStations: true,
28
+ title: 'San Francisco districts, 2022',
29
+ });
30
+ await writeFile('san-francisco.svg', svg);
31
+ ```
32
+
33
+ The SVG is self-contained. `project([lng, lat])` returns SVG coordinates; `viewBox` is `[0, 0, width, height]`. Use a different `idPrefix` for each inline SVG on the same page, or let the renderer generate one.
34
+
35
+ ## Mount and dispose an interactive map
36
+
37
+ Provide a host such as `<div id="map"></div>` before running this code. Run browser construction after mounting, not during server rendering.
38
+
39
+ ```ts
40
+ import { createMap } from '@kahwee/sf-map-svg/map';
41
+ import { fullMapData } from '@kahwee/sf-map-svg/data/full';
42
+
43
+ const host = document.querySelector<HTMLElement>('#map');
44
+ if (!host) throw new Error('Missing #map host');
45
+
46
+ const map = createMap(fullMapData, {
47
+ mode: 'neighborhoods',
48
+ source: 'realtor',
49
+ layers: { bartStations: true },
50
+ features: { motion: true },
51
+ appearance: { theme: 'districts' },
52
+ });
53
+ host.append(map.element);
54
+ const unsubscribe = map.on('neighborhoodchange', ({ name }) => {
55
+ // name is null when the selection is cleared.
56
+ console.log(name);
57
+ });
58
+
59
+ // Call from your framework's unmount hook or when removing the view.
60
+ function disposeMap() {
61
+ unsubscribe();
62
+ map.destroy();
63
+ map.element.remove();
64
+ }
65
+ ```
66
+
67
+ `destroy()` is idempotent, cancels owned work and subscriptions, and leaves host-owned DOM in place. Other controller operations after destruction throw. In React, construct inside an effect and return cleanup; in Vue or Svelte, use their mount/unmount hooks. Do not create a new map on every render: update the existing controller instead.
68
+
69
+ ## Know the defaults
70
+
71
+ | Setting | `renderMap` | `createMap` |
72
+ | --- | --- | --- |
73
+ | Mode | No mode option | `basemap`; set `neighborhoods` or `districts` explicitly |
74
+ | Theme | `districts` | `transit`; use `appearance.theme` |
75
+ | District layers | On | Follow `districts` mode |
76
+ | Neighborhood layers | Lines off | Follow `neighborhoods` mode |
77
+ | Parks, highways, key roads | Off | On when supplied |
78
+ | Road labels | Follow `keyRoads` | On when supplied, subject to zoom/collisions |
79
+ | BART stations | Off | On when supplied |
80
+ | Visible labels | On | On, subject to zoom/collisions |
81
+ | Motion features | Static animation off | Off; opt in via `features` |
82
+
83
+ A layer switch does not download missing geography. `fullMapData` has `.map` for static rendering and separate lookup collections for interactive use. `staticMapData` is already a static data object. Use `renderMap(guideMapData.map, options)`, not `renderMap(guideMapData, options)`.
84
+
85
+ SFAR realtor neighborhoods are the default when supplied. If only an alternative collection is supplied, the controller selects that available source. Choose `source` explicitly when comparing definitions. Sources retain separate identities; they are not interchangeable polygons.
86
+
87
+ ## Construction settings versus runtime updates
88
+
89
+ | Change | Call |
90
+ | --- | --- |
91
+ | Layers, controls, behaviors | `map.configure({ layers, controls, features, appearance, mode, source, year, labels })` |
92
+ | Map mode or neighborhood source | `map.setMode(mode)`, `map.setSource(source)` |
93
+ | District boundaries or choropleth | `map.setDistrictYear(year)`, `map.setDistrictStyle(callback)` |
94
+ | Master text visibility | `map.setLabels(visible)` |
95
+ | Markers or route overlays | `map.setMarkers(items)`, `map.setOverlays(items)` |
96
+ | Camera | `map.camera.set/pan/zoom/fit/reset(...)` |
97
+ | Palette, typography, control styling | `map.configure({ appearance })`; retains camera, selection and focus |
98
+
99
+ `configure` merges supplied keys; omitted keys keep their existing values. Set a layer or control key to `undefined` to remove its override. Set a whole group to `undefined` to reset that group. Set a feature to `false` to disable it. Explicit layer overrides keep winning after mode changes.
100
+
101
+ ```ts
102
+ map.configure({ layers: { neighborhoodLabels: false } });
103
+ map.configure({ layers: { neighborhoodLabels: undefined } }); // follow mode again
104
+ map.configure({ features: { motion: false } });
105
+ map.configure({ layers: undefined }); // clear all explicit layer overrides
106
+ ```
107
+
108
+ `getConfiguration()` returns a detached snapshot of these groups, including explicit layer overrides. It is not a full serialization of mode, source, year, camera, or selection. Appearance overrides are included. Use `getResolvedConfiguration()` for current mode/source/year/labels and effective layer switches, and `getCapabilities()` for supplied geography. Invalid configuration patches leave the current map unchanged.
109
+
110
+ ## Markers, routes and camera coordinates
111
+
112
+ Marker positions are `{ lng, lat }`. GeoJSON coordinates and the static projection use `[longitude, latitude]`, in WGS84. Camera viewports use `[x, y, size]` in the fixed 800 × 800 projected map space; they are not geographic bounds. `camera.pan` uses projected units. `projectToScreen(lng, lat)` returns pixels relative to the map canvas, plus visibility.
113
+
114
+ Marker and overlay IDs must be unique. Keep marker IDs stable across updates so retained markers preserve DOM nodes, focus and entrance animations. Use `null` to clear a selection; selection methods return a boolean. Selection events can contain null values. `map.on()` returns an unsubscribe function.
115
+
116
+ Reduced motion takes precedence over animations. Map touch gestures are opt-in; hiding a chooser requires an accessible alternative in your application.
117
+
118
+ ## Common integration errors
119
+
120
+ | Symptom | Check |
121
+ | --- | --- |
122
+ | No neighborhoods or district fills | Set `mode` explicitly; the default is `basemap` |
123
+ | An enabled layer does not appear | Supply its dataset; check master labels, zoom and collision filtering |
124
+ | A theme or motion option throws | Put styling in `appearance` and behaviors in `features`; flat interactive options are rejected |
125
+ | Source switch throws | Supply that source collection, or use `/data/full` |
126
+ | A geographic coordinate produces a surprising camera view | Use `camera.fit(geometry)`; `camera.set` takes projected coordinates |
127
+ | An event handler breaks after clearing selection | Handle null `id`, `name`, `marker`, or `district` |
128
+ | Updates throw after navigating away | Dispose once and stop calling the destroyed controller |
129
+ | An import from `/legacy`, `/custom-map`, `/explorer`, `/interactive` or `/interactive-data` fails | Those entries were removed in v3; follow the migration guide |
130
+
131
+ ## Versions and assistant-readable docs
132
+
133
+ The package declarations are the exact type contract for your installed version. Check the changelog before using features shown on a local preview: its Unreleased section can include functionality absent from npm.
134
+
135
+ The generated `llms.txt` index links to Markdown documentation; `llms-full.txt` combines the guides and type contracts into one file. Both are built from the same package selected for the site. Local previews are labeled as working-tree docs. Released builds use the installed npm package's docs and declarations.
136
+
137
+ See [API options](API.md), [worked examples](EXAMPLES.md), [consumer integration](consumer-integration.md), and [v3 migration](migration-v3.md).
138
+
139
+ Appearance token objects merge by key; `undefined` removes an override and `appearance: undefined` resets the group. Static maps accept grouped `layers` and the shared `appearance` keys (`theme`, `colors`, `districtStyle`) too. See [the API contract](API.md#live-configuration-and-inspection) and [the design playground](https://kahwee.github.io/sf-map-svg/playground.html).
@@ -1,12 +1,12 @@
1
1
  # Guide bundle size report
2
2
 
3
- Generated 2026-09-28 by `pnpm report:guide` with Vite production minification and gzip compression. Each emitted JS chunk is compressed independently. The report measures the explicit-data v3 root and the optional `@kahwee/sf-map-svg/guide` preset.
3
+ Generated 2026-10-01 by `pnpm report:guide` with Vite production minification and gzip compression. Each emitted JS chunk is compressed independently. The report measures the explicit-data root and the optional `@kahwee/sf-map-svg/guide` preset.
4
4
 
5
5
  | Entry | Initial JS, raw | Initial JS, gzip | Explicit detail JS, gzip |
6
6
  | --- | ---: | ---: | ---: |
7
- | v3 root (explicit data) | 89.7 KB | 26.3 KB | — |
8
- | v3 static renderer | 15.3 KB | 5.0 KB | — |
9
- | Guide preset | 479.5 KB | 114.9 KB | 473.7 KB |
7
+ | Renderer root (explicit data) | 99.3 KB | 29.1 KB | — |
8
+ | Static renderer | 18.2 KB | 5.8 KB | — |
9
+ | Guide preset | 489.1 KB | 116.8 KB | 467.0 KB |
10
10
 
11
11
  **500 KB target:** met.
12
12