The interactivity ladder
| Rung | What | How | Costs |
|---|---|---|---|
| 1 · Static | Report and landing pages | Astro + PatternFly React rendered server-side + SVG charts | Zero client JS from PatternFly; the page script is Astro’s prefetch |
| 2 · Island | An interactive chart, a filter, a small map inside a report or doc | <Chart interactive> or any React component with client:visible | That component’s JS only; PatternFly React components import their own CSS |
| 3 · Page-app | A micro-tool at /tools/<name> | client:only="react" React app on an Astro page (see /tools/example) — same chrome, theme, bundles, deploy | React runtime + your app; state in the URL hash for deep links |
| 4 · Standalone | A tool that needs its own lifecycle, auth or SSR | apps/tools/<name> Vite + React + PatternFly React consuming @csdr/theme/chrome — deferred to v2 | A second deployable |
Writing an island
Section titled “Writing an island”// src/components/tools/MyIsland.tsx — allowlisted PatternFly components onlyimport { Toolbar, ToolbarContent, ToolbarItem } from '@patternfly/react-core/dist/esm/components/Toolbar/index.js';import { Slider } from '@patternfly/react-core/dist/esm/components/Slider/index.js';export default function MyIsland() { … }---import MyIsland from '../components/tools/MyIsland';---<MyIsland client:visible />For charts inside islands import echarts dynamically and register the themes: const { registerCsdrThemes, themeName } = await import('@csdr/charts/theme'), then echarts.init(el, themeName(mode, document.documentElement.dataset.palette)) so the chart follows the page’s theme and palette (data-theme / data-palette on <html>, D13). ChartIsland in @csdr/charts does this for you when you already have an option object.
Deep links
Section titled “Deep links”Static hosting has no server routes under /tools/<name>/, so tools keep their state in the hash (#from=1990&to=2026) or use run_worker_first in wrangler.jsonc if a tool must own path segments.