# leaflet-components
[Leaflet.js](https://leafletjs.com/) as native Web Components (Custom Elements). Each HTML element maps 1:1 to a Leaflet object with reactive attribute binding.
## Installation
```bash
npm install leaflet-components
```
Available on JSR as `@buddy/leaflet-components`.
## Usage
Import once to register all custom elements, then use them declaratively in HTML.
```html
Hello!
Hover me
I am a circle
```
## `leaflet-map`
The root component. All other components must be children of ``.
```html
```
### View state
These attributes stay in sync with the map as the user interacts with it — panning updates `lat`/`lng`, zooming updates `zoom`.
| Attribute | Default | Description |
|---|---|---|
| `lat` | `0` | Center latitude |
| `lng` | `0` | Center longitude |
| `zoom` | `2` | Zoom level |
| `min-zoom` | `0` | Minimum zoom level |
| `max-zoom` | — | Maximum zoom level. When unset, Leaflet uses the tile layer's own max zoom. |
### Interaction
Boolean options that default to `true` are controlled by the presence of a `disable-*` attribute. Handler-based options can be toggled at any time; the rest only apply at construction.
| Attribute | Controls | Live? |
|---|---|---|
| `disable-dragging` | Mouse/touch panning | ✓ |
| `disable-scroll-wheel-zoom` | Scroll-wheel zoom | ✓ |
| `disable-double-click-zoom` | Double-click zoom | ✓ |
| `disable-touch-zoom` | Pinch-to-zoom | ✓ |
| `disable-box-zoom` | Shift-drag zoom box | ✓ |
| `disable-keyboard` | Keyboard pan/zoom | ✓ |
| `disable-zoom-control` | Built-in zoom control | — |
| `disable-attribution-control` | Built-in attribution | — |
| `disable-close-popup-on-click` | Close popup on map click | — |
| `disable-track-resize` | Auto-resize on window resize | — |
| `disable-bounce-at-zoom-limits` | Bounce animation at min/max zoom | — |
| `disable-tap-hold` | Long-press context menu (mobile) | — |
Boolean options that default to `false` are enabled by adding the attribute:
| Attribute | Description |
|---|---|
| `prefer-canvas` | Render vector layers on Canvas instead of SVG |
| `world-copy-jump` | Pan to the original world copy when crossing the antimeridian |
### Animation
| Attribute | Default | Description |
|---|---|---|
| `disable-zoom-animation` | — | Disable CSS zoom animation |
| `disable-fade-animation` | — | Disable tile fade-in |
| `disable-marker-zoom-animation` | — | Disable marker zoom animation |
| `zoom-animation-threshold` | `4` | Max zoom delta for animated zoom |
### Inertia & panning
| Attribute | Default | Description |
|---|---|---|
| `disable-inertia` | — | Disable inertial panning |
| `inertia-deceleration` | `3000` | Deceleration rate (px/s²) |
| `inertia-max-speed` | `Infinity` | Maximum inertia speed (px/s) |
| `ease-linearity` | `0.2` | Pan easing linearity |
### Zoom behaviour
| Attribute | Default | Description |
|---|---|---|
| `zoom-snap` | `1` | Zoom snapping interval; `0` for continuous zoom |
| `zoom-delta` | `1` | Zoom step per keyboard/button press |
| `max-bounds-viscosity` | `0` | How much the bounds resist panning past them (`0`–`1`) |
### Scroll wheel
| Attribute | Default | Description |
|---|---|---|
| `wheel-debounce-time` | `40` | Debounce delay for wheel events (ms) |
| `wheel-px-per-zoom-level` | `60` | Pixels of scroll per zoom level |
### Keyboard & touch
| Attribute | Default | Description |
|---|---|---|
| `keyboard-pan-delta` | `80` | Pan distance per key press (px) |
| `tap-tolerance` | `15` | Max touch movement to trigger a tap (px) |
### Rendering
| Attribute | Default | Description |
|---|---|---|
| `transform-3d-limit` | `8388608` | Max CSS `translate3d` component value before a layer reset |
### Accessing the underlying map
```js
const el = document.querySelector('leaflet-map');
el.map; // L.Map instance (undefined before connected)
el.zoom; // current zoom (reads live from map, falls back to attribute)
el.scrollWheelZoom = false; // disable at runtime
```
All `leaflet-map` properties are two-way: reading returns the live map value; writing updates both the attribute and the map.
---
## Other components
### Tile layers
| Element | Key attributes |
|---|---|
| `leaflet-tile-layer` | `url`, `attribution`, `min-zoom`, `max-zoom`, `opacity`, `z-index` |
| `leaflet-tile-layer-wms` | `url`, `layers`, `styles`, `format`, `transparent`, `version` |
### Vector layers
Path style attributes (`color`, `weight`, `opacity`, `fill`, `fill-color`, `fill-opacity`, `dash-array`, `line-cap`, `line-join`) are shared by all vector layers.
| Element | Additional attributes | Notes |
|---|---|---|
| `leaflet-circle` | `lat`, `lng`, `radius` (meters) | |
| `leaflet-circle-marker` | `lat`, `lng`, `radius` (pixels) | |
| `leaflet-polyline` | — | Vertices via `` children |
| `leaflet-polygon` | — | Vertices via `` children |
| `leaflet-rectangle` | `bounds` (JSON) | |
| `leaflet-line` | `lat`, `lng` | Vertex only — not rendered directly |
### Markers & overlays
| Element | Key attributes |
|---|---|
| `leaflet-marker` | `lat`, `lng`, `title`, `alt`, `draggable`, `opacity`, `z-index-offset` |
| `leaflet-image-overlay` | `url`, `bounds` (JSON), `opacity`, `alt`, `interactive` |
| `leaflet-video-overlay` | `url`, `bounds` (JSON), `opacity`, `loop`, `autoplay`, `muted` |
| `leaflet-svg-overlay` | `bounds` (JSON), `opacity`, `interactive` — wrap an `