[Mappe·Shared] MetaLine — " · "-separated meta with optional icon (§7) #859

Open
opened 2026-06-16 10:53:52 +02:00 by marcel · 0 comments
Owner

Shared component · Story 4. Part of #853.

Context

Meta strings are built ad-hoc (formatXsMeta in personFormat.ts:57 joins with · but there's no rendering component). The prototypes render an icon + ·-separated spans.

Prop contract — decided

MetaLine takes items: string[] (an array of already-formatted, already-localized strings) plus an optional iconSrc: string (static asset path). It does not take a pre-joined string and does not absorb formatXsMeta's join logic. Callers remain responsible for building the item array (including running Paraglide lookups for unit labels like "Dokumente" / "Personen"). formatXsMeta continues to own composition; MetaLine owns only rendering.

Icon path convention — decided: icons must be served from /degruyter-icons/… paths so that the layout.css rule img[src*='degruyter-icons']:not(.invert) triggers dark-mode inversion. The .dgicon class does not have a corresponding CSS rule in the repo; do not use it and do not add it.

Scope

  • Create $lib/shared/primitives/MetaLine.svelte — renders ·-separated items in Montserrat 12px var(--c-ink-2), with an optional leading <img> icon 14px × 14px at opacity:.5 (never emoji, never {@html}).
  • Adopt on PersonDetail as the ≥1-page adoption criterion (AC#2).

Out of Scope

  • Rewiring formatXsMeta or any other caller beyond PersonDetail.
  • Backend changes, Flyway migrations, env vars, CI workflow changes.
  • Any new runtime dependency.

Acceptance

  • Renders items as ·-separated spans with Montserrat 12px, color var(--c-ink-2), gap 8px, items vertically centered (align-items:center). Matches DESIGN_RULES §7 / _AUTHORING_KIT.md §8.
  • Leading <img> icon is shown when iconSrc is supplied, absent (no empty box) when omitted. Icon is 14px × 14px, opacity:.5, alt="" (decorative — screen readers skip it). iconSrc must be a static asset path; it is never interpolated from user/import data.
  • All item strings render via Svelte default {…} escaping; {@html} is never used anywhere in the component (XSS guard — CWE-79).
  • Zero items: component renders nothing (no empty · line).
  • At 320px viewport the line wraps to multiple lines using flex-wrap: wrap; the · separators stay attached to the preceding item so no separator is stranded at line-start.
  • Dark-mode icon inversion: icon src must begin with /degruyter-icons/; layout.css:402 img[src*='degruyter-icons']:not(.invert) then applies filter:invert(1) automatically. Verify in both light and dark on PersonDetail.
  • Adopted on PersonDetail (replaces the ad-hoc meta string there). Confirm layout at 320 / 768 / 1440 px in light AND dark.
  • Caller-supplied strings are already-localized Paraglide output; MetaLine hardcodes no German (or any language) unit strings.
  • frontend/src/lib/shared/primitives/MetaLine.svelte.spec.ts (vitest-browser-svelte, --project=client) passes: renders N spans separated by ·; leading <img> present when iconSrc is set and absent otherwise; width:14px; height:14px; opacity:.5 on the img; font-size:12px; zero-items renders empty; alt="" on the img.

Depends on: none (consumes only existing tokens --c-ink-2 and the existing /degruyter-icons/ dark rule — can be parallelized with other #853 sub-issues). Refs: DESIGN_RULES §7, _AUTHORING_KIT.md §8, layout.css:402.

**Shared component · Story 4.** Part of #853. ## Context Meta strings are built ad-hoc (`formatXsMeta` in `personFormat.ts:57` joins with ` · ` but there's no rendering component). The prototypes render an icon + ` · `-separated spans. ## Prop contract — decided MetaLine takes **`items: string[]`** (an array of already-formatted, already-localized strings) plus an optional **`iconSrc: string`** (static asset path). It does **not** take a pre-joined string and does **not** absorb `formatXsMeta`'s join logic. Callers remain responsible for building the item array (including running Paraglide lookups for unit labels like "Dokumente" / "Personen"). `formatXsMeta` continues to own composition; MetaLine owns only rendering. **Icon path convention — decided:** icons must be served from `/degruyter-icons/…` paths so that the `layout.css` rule `img[src*='degruyter-icons']:not(.invert)` triggers dark-mode inversion. The `.dgicon` class does **not** have a corresponding CSS rule in the repo; do not use it and do not add it. ## Scope - Create `$lib/shared/primitives/MetaLine.svelte` — renders ` · `-separated items in Montserrat **12px** `var(--c-ink-2)`, with an optional leading `<img>` icon **14px × 14px** at `opacity:.5` (never emoji, never `{@html}`). - Adopt on **PersonDetail** as the ≥1-page adoption criterion (AC#2). ## Out of Scope - Rewiring `formatXsMeta` or any other caller beyond PersonDetail. - Backend changes, Flyway migrations, env vars, CI workflow changes. - Any new runtime dependency. ## Acceptance - [ ] Renders `items` as `·`-separated spans with **Montserrat 12px**, color `var(--c-ink-2)`, gap **8px**, items vertically centered (`align-items:center`). Matches `DESIGN_RULES §7` / `_AUTHORING_KIT.md §8`. - [ ] Leading `<img>` icon is shown when `iconSrc` is supplied, absent (no empty box) when omitted. Icon is **14px × 14px**, `opacity:.5`, `alt=""` (decorative — screen readers skip it). `iconSrc` must be a static asset path; it is never interpolated from user/import data. - [ ] All item strings render via Svelte default `{…}` escaping; `{@html}` is **never** used anywhere in the component (XSS guard — CWE-79). - [ ] Zero items: component renders **nothing** (no empty `·` line). - [ ] At 320px viewport the line **wraps** to multiple lines using `flex-wrap: wrap`; the `·` separators stay attached to the preceding item so no separator is stranded at line-start. - [ ] Dark-mode icon inversion: icon `src` must begin with `/degruyter-icons/`; `layout.css:402` `img[src*='degruyter-icons']:not(.invert)` then applies `filter:invert(1)` automatically. Verify in both light and dark on PersonDetail. - [ ] Adopted on **PersonDetail** (replaces the ad-hoc meta string there). Confirm layout at 320 / 768 / 1440 px in light AND dark. - [ ] Caller-supplied strings are already-localized Paraglide output; MetaLine hardcodes **no** German (or any language) unit strings. - [ ] `frontend/src/lib/shared/primitives/MetaLine.svelte.spec.ts` (vitest-browser-svelte, `--project=client`) passes: renders N spans separated by `·`; leading `<img>` present when `iconSrc` is set and absent otherwise; `width:14px; height:14px; opacity:.5` on the img; `font-size:12px`; zero-items renders empty; `alt=""` on the img. **Depends on:** none (consumes only existing tokens `--c-ink-2` and the existing `/degruyter-icons/` dark rule — can be parallelized with other #853 sub-issues). **Refs:** `DESIGN_RULES §7`, `_AUTHORING_KIT.md §8`, `layout.css:402`.
marcel added this to the Mappe Visual Redesign milestone 2026-06-16 10:53:52 +02:00
marcel added the P2-mediumfeatureredesign-mappeui labels 2026-06-16 11:06:21 +02:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: marcel/familienarchiv#859