# UNDRR Mangrove > UI component library for UNDRR's disaster risk reduction websites (undrr.org, preventionweb.net, mcr2030.undrr.org). Provides both React components and vanilla HTML/CSS patterns. Built with Storybook. - Version: 2.0.0 - Package: @undrr/undrr-mangrove - License: Apache-2.0 ## Links - Storybook: https://mangrove.undrr.org/ - Repository: https://github.com/unisdr/undrr-mangrove - npm: https://www.npmjs.com/package/@undrr/undrr-mangrove - Release changelog (machine-readable): https://mangrove.undrr.org/releases.json - Project changelog (markdown): https://github.com/unisdr/undrr-mangrove/blob/main/CHANGELOG.md - v2.0 Release notes: https://mangrove.undrr.org/?path=/docs/getting-started-release-notes-v2-0--docs - Icons inventory: https://mangrove.undrr.org/ai-components/components-icons.json - Theme token dictionary: https://mangrove.undrr.org/tokens.json (theme tokens only; component custom properties are documented per component) - Editorial manual (capitalization, punctuation, numbers, abbreviations, italics, spelling, UNDRR terminology, disability inclusive and gender-inclusive language; each rule credited to its United Nations system or UNDRR source in the doc itself): https://mangrove.undrr.org/llms-editorial-manual.txt ## For AI agents The Storybook site is a single-page app, so fetching pages directly won't give you readable content. Use the JSON files below instead. Component index (all 84 components): https://mangrove.undrr.org/ai-components/index.json Release changelog & version history (~12 releases, machine-readable): https://mangrove.undrr.org/releases.json CSS utility class reference (~187 classes): https://mangrove.undrr.org/ai-components/utilities.json Theme token dictionary (~202 tokens with type, format & wrapping metadata): https://mangrove.undrr.org/tokens.json This dictionary covers theme tokens only — the --mg-* properties the theme stylesheets define from the tokens/*.yaml sources. Component-scoped custom properties are public API but are not in it. They are in each component's own entry instead: 113 properties across 22 components, under `customProperties` in ai-components/{id}.json, each with its type, its resting value and what it does. The index says which components have them and how many. 143 --mg-* properties are in neither list: they are global like a theme token, but declared straight in SCSS rather than generated from tokens/*.yaml, so the dictionary never saw them. Four groups — the legacy one-off brand colours (--mg-color-*), 10 of them, declared on :root by stories/assets/scss/_variables.scss, alongside the palette tokens.json does carry; the data visualisation palettes (--mg-dataviz-*), 114 of them, declared on :root by stories/assets/scss/_tokens-data-viz.scss; the five typography roles (--mg-font-family-*), 5 of them, declared on :root by stories/assets/scss/_variables.scss, re-pointed for Arabic in _fonts.scss, and described under "Brand guide → Typography" below; the Sendai Framework target ramps (--mg-sendai-*), 14 of them, declared on :root by stories/assets/scss/_tokens-data-viz.scss. The dictionary's own `scope` field says the same thing. Icons gallery: https://mangrove.undrr.org/?path=/docs/components-icons--docs Static icon inventory (machine-readable): https://mangrove.undrr.org/ai-components/components-icons.json ### Vanilla HTML quick start 67 of the 84 components work as plain HTML with CSS classes, no React needed. In the index, these have vanillaHtml: true. Each component's detail JSON includes a renderedHtml array with copy-pasteable HTML snippets. 1. Include the Mangrove CSS bundle (pick your theme): - UNDRR: https://assets.undrr.org/mangrove/2.0.0/css/style.css - PreventionWeb: https://assets.undrr.org/mangrove/2.0.0/css/style-preventionweb.css - MCR2030: https://assets.undrr.org/mangrove/2.0.0/css/style-mcr.css - IRP: https://assets.undrr.org/mangrove/2.0.0/css/style-irp.css - DELTA: https://assets.undrr.org/mangrove/2.0.0/css/style-delta.css - All themes in one bundle: https://assets.undrr.org/mangrove/2.0.0/css/style-all.css Each single-theme bundle carries only its own tokens. style.css contains no .mg-theme-* rules, so putting class="mg-theme-irp" on a page that loaded style.css does nothing — load that theme's bundle, or style-all.css if the page has to switch themes at runtime. 2. Fetch the component index and find what you need 3. Fetch the component's detailsUrl and use the renderedHtml examples ### React quick start 17 components require React (requiresReact: true in the index). These use D3, Leaflet, or complex state management. Import them from the package subpath, which is what the tarball ships: import { ComponentName } from "@undrr/undrr-mangrove/components/ComponentName.js". The package root is not an entry point — "@undrr/undrr-mangrove" on its own does not resolve on any published version. Each component's index and detail entry carries the exact `import` line to use; where one is absent, the component ships in no bundle and there is nothing to import. A component flagged `importRequiresDom: true` reads `document` as it loads: that import line is correct in a browser and through a bundler, and throws "document is not defined" if it is evaluated in Node or during server-side rendering — its detail file says what to do instead. Several React components support hydration on vanilla HTML pages via the createHydrator pattern. Check the component's `hydration` field (or `reactNote`) for details; components with a `hydration` field are flagged `hydration: true` in the index. A `vanillaModule` field (flagged `vanillaModule: true`) has the same shape but describes a plain ES module from `/js/`: it needs neither React nor `hydrate.js`. ### Vanilla JavaScript modules and dynamic DOM re-initialization Mangrove provides standalone vanilla JavaScript utilities under `/js/` (`https://assets.undrr.org/mangrove/2.0.0/js/*.js`, loaded with `type="module"`) that auto-initialize on `DOMContentLoaded`. When working with Single Page Applications (SPAs) or dynamically rendering content into the DOM (e.g. after AJAX fetches, client routing, modal dialogs), invoke the exported initialization functions manually: - **Tabs** (`js/tabs.js`): Call `mgTabs(scope)` to initialize or re-initialize `[data-mg-js-tabs]` tab containers within a container element. Call `mgTabsDestroy(scope)` on unmount. - **Show More** (`js/show-more.js`): Call `mgShowMore(scope)` to initialize `[data-mg-show-more]` content truncation toggles. - **Table of Contents** (`js/table-of-contents.js`): Call `mgTableOfContents(scope)` to generate an article TOC with scrollspy tracking. - **On This Page Nav** (`js/on-this-page-nav.js`): Call `mgOnThisPageNav(scope)` for in-page jump nav with horizontal scrolling. - **Switch pending** (`js/switch-pending.js`): Call `mgSwitchPendingInit(scope)` for new `[data-mg-switch-pending]` switches, or `mgSwitchPending(input, { save })` for one switch. Call `mgSwitchPendingDestroy(scope)` before removing them. - **Preview Access** (`js/preview-access.js`): Call `mgPreviewAccess(scope)` to initialize password gating for staging environments. - **Shared constants** (`js/undrr.js`): Key codes and breakpoints on `window.UNDRR`. It does not bundle the other modules. ### CSS utilities The utilities.json file lists ~187 utility classes grouped by category: layout containers, grid, responsive display, text utilities, accessibility, background colors, text colors, font sizes, animations, embed containers, interactive controls (switches and icon-only buttons), and show-more patterns. All use the mg- prefix. ### Releases and changelog To inspect changes between versions, tags, and pre-releases without parsing raw git commits: - **Machine-readable releases endpoint**: https://mangrove.undrr.org/releases.json Contains structured changelog objects for every release (version, release date, tag, PR references, categorization: Features, Bug fixes, Tooling, Security) plus component-level changelogs. - **Repository changelog**: https://github.com/unisdr/undrr-mangrove/blob/main/CHANGELOG.md Cross-cutting library release notes. - **v2.0 migration notes & breaking changes**: https://mangrove.undrr.org/?path=/docs/getting-started-release-notes-v2-0--docs Full breaking change catalogue, architectural shifts, and token migration recipes. - **Component-level changelogs**: Each component's detail file (https://mangrove.undrr.org/ai-components/{id}.json) contains a `changelog` array with granular version updates, dates, descriptions, and PR links. ### Z-index layers Use the --mg-z-index-* custom properties for global stacking contexts (fixed, sticky, portaled, or deliberately negative elements). One token per UI concept: --mg-z-index-behind, -nav, -sticky, -nav-toggle, -header, -drawer, -dropdown, -modal, -toast. Derive backdrops with calc(), e.g. z-index: calc(var(--mg-z-index-drawer) - 1). For local stacking within a component's own isolated stacking context (e.g. inside position: relative), use a raw value with a comment instead of a token. The navigation zone tokens (-nav through -header, values 10-22) are frozen; do not change their numeric values. These were $mg-z-index-* Sass variables before 2.0 and no longer exist in that form. See the "Design decisions/Z-index layers" Storybook page for the full layer table and philosophy. ### Design tokens Colour, spacing, radii and component tokens are CSS custom properties, so they are themeable at runtime from a `:root` block or a `.mg-theme-*` block. No rebuild of Mangrove is needed. Where they come from: - Compiled theme token dictionary: https://mangrove.undrr.org/tokens.json — theme tokens only; see its `scope` field - `tokens/mangrove.yaml` — the brand-neutral base: https://raw.githubusercontent.com/unisdr/undrr-mangrove/main/tokens/mangrove.yaml - `tokens/undrr.yaml`, `preventionweb.yaml`, `irp.yaml`, `mcr.yaml`, `delta.yaml` — brand layers merged over the base, same directory. - `stories/assets/scss/_tokens-data-viz.scss` — the chart and map palette: https://raw.githubusercontent.com/unisdr/undrr-mangrove/main/stories/assets/scss/_tokens-data-viz.scss Those YAML files carry a `$description` on the tokens that need one, which is the reasoning behind the value. For automated and tooling integrations, fetch the machine-readable `https://mangrove.undrr.org/tokens.json` dictionary which includes token types, formats, descriptions, and rgb() wrapping requirements. `tokens.json` is a THEME token dictionary. It lists the `--mg-*` properties the theme stylesheets define from the YAML sources, and nothing else. A component's own properties live in its manifest entry instead, under `customProperties` in `ai-components/{id}.json` — 113 of them across 22 components, each with what it does and the value it holds at rest. They come in two kinds: `type: "default"`, which a plain, unconditional rule already gives a value (`--mg-empty-state-*`, `--mg-notice-*`, `--mg-status-label-*`, `--mg-tab-*`), and `type: "hook"`, which nothing unconditional defines — either no rule at all, or only a modifier or a media query — so it holds its fallback until a wrapper, an inline style or a React prop sets it (`--mg-switch-track-*`, `--mg-switch-size`, `--mg-card-border`, `--mg-icon-fg`, `--mg-cta-bg`, `--mg-legend-tick-pos`, `--mg-on-this-page-nav-offset`, `--mg-tree-guide-offset`). A record with `wrapInRgb: true` takes sRGB channels (`255 255 255`) rather than a colour, the same way tokens.json marks `format: "srgb-channels"`; a hex or a keyword there makes the declaration invalid and it drops with no warning. Restyle a component by setting these, not by writing rules against its classes: a class rule of equal specificity replaces what the component draws, and several components build their geometry on a property whose value your rule would then be fighting. What you actually write against is the compiled result: the `--mg-*` custom properties in the theme stylesheets above. Four things the sources do not make obvious: **1. The rgb() wrapping rule.** Most colour tokens are sRGB channel triples, not colours: ```css --mg-color-blue-900: 0 79 145; /* channels, not a colour */ color: rgb(var(--mg-color-interactive)); /* correct */ color: var(--mg-color-interactive); /* INVALID — silently dropped */ background: rgb(var(--mg-color-interactive) / 0.1); /* alpha for free */ ``` Getting it wrong produces a declaration the browser discards with no console error, so it fails by looking almost right. Full colour tokens must NOT be wrapped in `rgb()`, because they already evaluate to complete color expressions or keyword colours (e.g. `transparent`). Check `https://mangrove.undrr.org/tokens.json` for the authoritative, machine-readable list. As of 2.0.0, the tokens that must NOT be wrapped in `rgb()` are: - `--mg-border-color-button` - `--mg-border-color-button-primary` - `--mg-border-color-button-secondary` - `--mg-card-background` - `--mg-color-button-background` - `--mg-color-button-background--hover` - `--mg-color-hero-button-secondary-background` - `--mg-color-modal-scrim` - `--mg-color-tab-background` - `--mg-color-tab-background--hover` - `--mg-color-tab-background--inactive` - `--mg-color-tab-border` - `--mg-color-tab-border--active` - `--mg-color-tab-border--hover` - `--mg-color-tab-section-background` - `--mg-color-tabbar-background` - `--mg-color-text-tab` - `--mg-color-text-tab--hover` - `--mg-color-text-tab-active` - `--mg-color-text-tab-no-results` - `--mg-form-input-background` - `--mg-form-input-background--focus` - `--mg-form-input-border-color` - `--mg-notice-action-secondary` - `--mg-surface-raised` **2. Focus rings.** `--mg-color-focus-ring` is the ring colour (deliberately not a brand colour: a brand-coloured ring vanishes against the brand's own filled surfaces). `--mg-color-focus-ring-inverse` is for a ring painted on an already-dark surface — a button on a filled hero banner, the dark Card variants. Geometry is `--mg-focus-ring-width`, `-offset` and `-radius`. Components should `@include mg-focus-ring;` or `@include mg-focus-ring-inset;` (`stories/assets/scss/_mixins.scss`) rather than hand-rolling an outline: the mixin draws two bands so the indicator is legible on any surface, and keeps the ring as an `outline` so it survives forced-colors mode. **3. Sendai Framework colours.** `--mg-sendai-target-a` through `-g` are the seven Sendai targets, with matching `--mg-sendai-on-target-*` label colours (Target C is the one that takes a dark label). `--mg-dataviz-*` carries the chart palette: `categorical` for unordered series, `sequential` for ordered magnitude, `sendai-*` for the ten-stop target ramps, plus chart chrome. Do not mix those three jobs. **4. Deprecated: `--sendai-red|orange|purple|turquoise`** and their `.mg-u-background-color--sendai-*` / `.mg-u-color--sendai-*` utility classes. Named by colour rather than by Sendai meaning, three of the seven targets have no counterpart, and they are scheduled for removal in 2.1. Do not emit them in new code — use `--mg-sendai-target-*` for target semantics or the `--mg-color-*` palette for a plain accent. Breakpoints (`$mg-breakpoint-*`) and `$mg-tabs-border-bottom` remain SCSS-only build-time variables and are not available as CSS custom properties: `@media` and `@if` both need a compile-time value. The font *faces* (`$mg-font-face-*`) are Sass for a different reason — they are the compile-time source the role custom properties are built from, and overriding one before the import is the only way to introduce a typeface Mangrove does not ship; see "Overrides" under Typography below. Font *families* are not on that list — they are the five `--mg-font-family-*` role custom properties described under "Brand guide → Typography" below. Never hard-code a typeface; name a role. ### Conventions - CSS prefix: mg- - Naming: BEM (e.g., mg-card__title, mg-button--primary) - Themes: undrr, preventionweb, irp, mcr2030, delta - Locales: en, ar, my, ja (RTL supported) - Semantic HTML, WCAG accessible ### List semantics: do not add role="list" Do NOT put `role="list"` on a `