<!-- GENERATED by scripts/build-llms.mjs from llms/viewers.md — do not edit this file. -->

# `lr-svg-viewer`

- **Import** `import '@aceshooting/lyra-ui/components/lr-svg-viewer.js';` (stable tag alias; registers the tag)
- **Class** `LyraSvgViewer`, also available unregistered from `@aceshooting/lyra-ui/components/viewers/svg-viewer/svg-viewer.class.js`
- **Family** `components/viewers/` — see `llms/index.md` for its siblings
- **Status** `stable` since `4.0.0` — see the maturity and deprecation policy in `llms/shared.md`
- **Release history** [CHANGELOG.md](../../CHANGELOG.md)
- **Deprecations** none
- **Optional peers** `dompurify` — see `llms/peers.md`
- **Themeable via** 17 parts, 7 custom properties — see this component's own `@csspart`/`@cssprop` list below
- **Library-wide behavior** (events, form association, `locale`/`strings`, tokens, TS types): `llms/shared.md`

---

## `lr-svg-viewer`

Fetches an SVG document, sanitizes it with the optional `dompurify` peer, and renders it inline.
The shared `passive-svg` profile (the same post-sanitization engine used for embedded viewer markup)
removes author `<style>`/`style`, SVG animation elements, and external
resource or paint-server references before insertion, preventing fetched SVG content from escaping
the viewer's paint box or starting secondary requests. Local `url(#id)` paint servers and embedded
raster data remain available.

Fitting SVG content stays centered. In a capped nonzoomable viewer, overflowing content begins
inside the body's reachable scroll range, keeping both its top and bottom available.

Adopts `DocumentAnchorTarget` (the same shared mixin `lr-pdf-viewer`/`lr-csv-viewer` use): a `region`
anchor addresses one `highlights` entry, matched by reference or by structural equality of its `rect`
(and optional `page`). Assigning `anchor` or calling `scrollToAnchor()` scrolls the matching
`[part="region-highlight"]` into view and fires `lr-anchor-result`. No other anchor kind resolves
here — a sanitized SVG document has neither pages nor extractable text to quote, which is also why
its registry entry declares `capabilities: { anchors: ['region'], search: false, textSelect: false }`.

**Properties:** `src: string = ''`, `name: string = ''`, and `maxHeight: string = ''` (attribute
`max-height`). `maxHeight` caps the scrollable body; invalid CSS `max-height` values, declaration
breaks, and `url()` are ignored.
`zoomable: boolean = false` (reflected) — wraps the rendered content in an
internal `<lr-pan-zoom>`. `false` (the default) preserves the exact pre-`zoomable` DOM — an
inline thumbnail (e.g. in a chat stream) must not unexpectedly grow a focusable zoom-chrome viewport;
an inspection surface opts in. `anchor: LyraAnchor | string | null = null` (attribute: false) —
declaratively jump to an anchor (a `LyraAnchor` object, or a `highlights` entry's `id`). Assigning it
calls `scrollToAnchor()` and fires `lr-anchor-result`; re-assigning the same value re-triggers the
scroll, it is not reference-gated. `highlights: readonly LyraHighlight[] = []` (attribute: false) —
display-only `region` highlights painted over the rendered SVG; unchanged behavior, now inherited
from `DocumentAnchorTarget` rather than declared locally. A region rectangle renders/resolves only
when `x`/`y`/`width`/`height` are finite numbers and both dimensions are nonnegative.
`activeHighlightId: string | null = null`
(attribute `active-highlight-id`) — the `highlights` entry, if any, currently treated as active
(`data-active` on its `region-highlight`). `anchorKinds: readonly LyraAnchorKind[] = ['region']`
(this viewer's supported `LyraAnchor.kind` values for the shared anchor-target contract).

**Methods:** `scrollToAnchor(target): Promise<boolean>` — scrolls the `highlights` entry matching
`target` (a `region`-kind `LyraAnchor`, matched by reference or by structural equality of
`rect`/`page`; or a `highlights[].id` string) into view, honoring `prefers-reduced-motion`. Resolves
`true` when a match was found and scrolled, `false` otherwise, and always fires `lr-anchor-result`
carrying the same boolean. Called before the SVG has finished loading it retries for up to 5s (real
timers) rather than failing immediately.

**Events:** `lr-render-error` with `detail.error` when fetching or sanitizing fails.
`lr-highlight-activate` (`detail: { highlightId }`) — a region highlight was clicked or activated via
Enter/Space. `lr-anchor-result` (`detail: { found: boolean }`) — fired after an `anchor` assignment
or a `scrollToAnchor()` call is applied, whether or not a match was found. `lr-text-select` is not
part of this viewer's event contract because sanitized SVG has no extractable text-selection
contract (`textSelect: false` in its registry capabilities).

**CSS parts:** `base`, `body`, `svg`, `spinner` (ordinary loading content; later transitions use the
shared document-level polite sink), `error` (ordinary visible text; later transitions use the shared
document-level assertive sink), `anchor-live-region` (an aria-hidden, non-live shadow mirror of the
latest anchor-jump message; the spoken copy is appended to the shared document-level polite sink
only while the viewer and its composed ancestors are exposed to the accessibility tree),
`highlight-layer` (wrapper around every
rendered region highlight), `region-highlight` (one region highlight, `data-tone`, `data-active`),
`region-highlight-target` (transparent activation geometry with an independent minimum hit area),
`highlight-actions` (non-overlapping actions for multiple highlights), `region-highlight-action`
(one action in that list),
`frame-viewport`/`frame-content`/`frame-controls`/`frame-zoom-in`/`frame-zoom-out`/`frame-reset`
(forwarded from the internal `<lr-pan-zoom>` while `zoomable`).
Passive loaded SVG content owns `role="img"`. Idle, loading, and error states instead use a named
`region`, keeping their descendant state text in the accessibility tree; zoom controls or
interactive region highlights likewise use a region. The spinner always includes visible
localized loading text alongside its decorative ring, and the ring stops under reduced motion.

**Themeable custom properties:** `--lr-svg-viewer-max-height` (default `none`) — maximum block size
of `[part="body"]`; also settable via the `max-height` property, which writes this token inline.
`--lr-svg-viewer-active-border` (default `var(--lr-color-warning, var(--lr-color-brand))`) — the
border color of the `[part='region-highlight']` matching `activeHighlightId`, distinct from the
resting highlight border so the active region can be recolored without touching the rest. It is an
inline `var()` fallback at the point of use rather than a `:host` declaration, so it can be set on
the element _or on any ancestor_: `::part(region-highlight)[data-active]` is invalid CSS — Shadow
Parts forbids an attribute selector after `::part()` — so re-pointing a shared `--lr-color-*` token,
and repainting everything else reading it, was previously the only way. Unset, it falls back to
exactly the tokens the rule used before. The tone-specific resting border and hover tint use
`--lr-svg-viewer-highlight-accent-color`, `--lr-svg-viewer-highlight-success-color`,
`--lr-svg-viewer-highlight-warning-color`, `--lr-svg-viewer-highlight-danger-color`, and
`--lr-svg-viewer-highlight-neutral-color` (defaulting respectively to the matching
brand/success/warning/danger/neutral color tokens).

**Optional peer dependency:** `dompurify`.

Remote resources are capped at 25 MB; exceeding it surfaces the localized
`documentPreviewResourceTooLarge` message instead of the graphic.
