[Unreleased]

Added

Changed

Fixed


[1.2.0] - 2026-05-30

Pure-admin v2.6.0 + v2.7.0 + v2.7.1 + v2.8.0 sync

Four upstream releases absorbed in a single library bump:

  • v2.6.0 (05b416b) — KPI showcase suite, framework token consolidation, Tailwind role palette, pa-stat--square redesign.
  • v2.7.0 (12b9d23) — pa-modal--banded, pa-gauge rebuild, CSS-variable consolidation sweep, link tokens, sidebar / btn-split / timeline / chip / live-card / outline-secondary fixes.
  • v2.7.1 (2754d24) — KPI showcases promoted from inline demo styles into permanent pa-kpi-* core components (8 SCSS partials, all kpi-* classes renamed to pa-kpi-*, per-component cascade vars namespaced to --pa-kpi-*).
  • v2.8.0 (2026-05-28) — the formerly-[Unreleased] post-2.7.1 commits graduated to a stable release (generic terminal tab strip, auto-fit cell-min grids on gauges + editorial, layout-ratio modifiers on hero + bento, composable --no-prev/--no-delta/--no-target toggles on numeric strip, --no-delta on sparkline list — all of which our Phase 2 already covered) plus one architectural fix (see below).

v2.8.0 architectural fix — CSS variable defaults at :root in the unthemed bundle

The bundled @keenmate/pure-admin-core/css (dist/css/main.css) now emits a complete neutral default for every --pa-* / --base-* token at :root. Before 2.8.0, only themes emitted :root { --pa-* } — consumers using the unthemed bundle standalone, OR any page during the FOUC window before its theme stylesheet finished loading, had var(--pa-positive) / --pa-success-bg / etc. resolve to invalid values. KPI sparklines and deltas rendered near-black via inherited text colour; web components fell back to hardcoded literals.

The fix lives in upstream's main.scss (not _core.scss_core.scss stays purely component CSS, consumed by BOTH the unthemed bundle AND themes via @import; emitting :root there would duplicate when a theme also emits its own). Themes are unaffected — they bypass main.scss entirely.

Impact for keen_pure_admin consumers: no wrapper change required. Bump @keenmate/pure-admin-core to ^2.8.0 and KPI sparklines / deltas now render with reasonable neutral colours even before a theme link resolves — or with no theme at all. Supersedes the 2.7.1-era partial fix that only emitted the 5-step sentiment scale at :root.

KPI component family — 9 new modules / 12+ new function components

Built one module per showcase (matching @keenmate/svelte-pure-admin 1:1 in component names and prop names), all on the v2.7.1 pa-kpi-* class surface.

  • PureAdmin.Components.Kpi (substrate)
    • kpi_tile/1 — base tile (head · label · value · prev row · sparkline slot · optional hover detail). Used by Terminal grid + standalone. Status pill is status_text + status_variant (built-ins warn / good / neutral); :head snippet overrides the whole head row. Sentiment variants on value and delta use the 5-step scale (very_positive / positive / neutral / negative / very_negative). variant (formerly spark_direction) colours the sparkline via currentColor. is_standalone for tiles outside a pa-kpi-terminal__grid.
    • kpi_detail/1 — popover element (pa-kpi-detail + __title). Auto-builds its <dl> from typed props (Current / Previous / Δ absolute / Δ percent / Target) on the host tile, or accepts raw markup via :inner_block. Replaces the previous kpi_tile_detail/1 slot scaffold.
    • kpi_sparkline/1 — opt-in convenience for the simple SVG polyline + trailing-dot pattern, on pa-kpi-tile__spark. Consumers using D3 / ApexCharts / Chart.js / Contex / etc. plug their renderer into the :chart slot instead.
  • PureAdmin.Components.KpiDetail — shared helpers (build_auto_rows/1, delta_to_sentiment/1, sentiment_class/1, dasherize/1) used by every tile / row component. Mirrors kpi-detail.ts from svelte-pure-admin.
  • PureAdmin.Components.KpiTerminalkpi_terminal/1 card wrapper with generic :pane tab strip (each pane has id, label_text, optional is_active); no panes → children wrapped in a single pa-kpi-terminal__grid--2col. :header_controls for custom toolbars between title and LIVE pill. The retired VALUE/Δ%/TREND view-mode toggle is replaced by the generic tab strip.
  • PureAdmin.Components.KpiSparklineListkpi_sparkline_list/1 + kpi_sparkline_row/1. is_no_delta drops the rightmost Δ% column; is_chart_first rotates the L→R order 90° at narrow widths.
  • PureAdmin.Components.KpiGaugeListkpi_gauge_list/1 + kpi_gauge/1. Default cell-min-driven auto-fit grid; switch via grid_layout="2col" or "max_2".."max_6". cell_min_width overrides --pa-kpi-gauge-cell-min. tick_position / tick_color knobs.
  • PureAdmin.Components.KpiHerokpi_hero_list/1 + kpi_hero_main/1 + kpi_hero_side/1. hero_split="2_3" / "3_4" shifts weight to the hero (default 1:1). Hero has :meta slot (or delta_text / period_text / target_text for the canonical pattern), :chart for the sparkline; rail is a :rail slot.
  • PureAdmin.Components.KpiBentokpi_bento/1 + kpi_bento_tile/1. Default 6-tile hero-left layout; bento_layout="hero_right" mirrors; bento_layout="5_tile" is hero + 4 supporting. row_height overrides --pa-kpi-bento-row-height. Set is_hero on the first tile.
  • PureAdmin.Components.KpiStripkpi_strip/1 + kpi_strip_row/1. Composable no_previous_value / no_delta_percent / no_target_bar toggles. Header row auto-generated from visible columns; override via header_labels (map keyed by column atom) or suppress via no_header or replace via :head slot. target_bar_percent drives the bar fill (capped at 100% visually); target_percent_text is the label below (may exceed 100).
  • PureAdmin.Components.KpiEditorialkpi_editorial/1 + kpi_editorial_tile/1. Cell-min-driven auto-fit grid; is_2_columns boolean shorthand or grid_layout="max_N" cap modifiers. target_text auto-renders as <em>tgt</em>{value} in the meta row.
  • Three JS hooks in lib/assets/js/hooks/:
    • PureAdminKpiTile — cursor-anchored Floating UI popover (virtual reference element); moves .pa-kpi-detail to <body> on mount, restores on destroyed. Auto-engaged whenever a tile / row has a popover.
    • PureAdminKpiSparkDot — converts SVG <circle> endpoints to .pa-kpi-spark-dot CSS spans so dots stay round under preserveAspectRatio="none".
    • PureAdminKpiTerminalTabs — client-side tab strip wiring for pa-kpi-terminal__tab / pa-kpi-terminal__pane. Scoped per terminal so nested terminals (if any) stay isolated.

Other v2.7.0 component reworks

  • Modal — new is_banded boolean. Emits pa-modal--banded alongside the existing :variant role modifier. Composes — <.modal variant="success" is_banded> produces pa-modal pa-modal--success pa-modal--banded. Buttons inside the bands auto-invert via the framework's CSS (light theme renders dark-on-pale; dark theme renders light-on-muted). No markup change beyond the new class.
  • gauge/1 rebuild — moved the label out of the donut so __inner holds only the value text. Label now renders as a sibling row alongside __min and __max below the gauge (matches v2.7.0 layout). New :size attr emits --pa-gauge-size inline (default upstream 12rem). The --value style declaration is unchanged. Existing apps render the label in its new position automatically when they upgrade to @keenmate/pure-admin-core ^2.7.0; no markup change required.
  • Stat — 5-step sentiment scale on hero deltas. change_direction now accepts very_positive / very_negative in addition to positive / negative / neutral. Internally converted to kebab-case (pa-stat__change--very-positive etc.) to match upstream's SCSS class names. Neutral colour shifted from --pa-text-color-2 (grey) to --pa-neutral — purely a visual change, no API impact.
  • Stat icon :danger — already exposed in the icon_variant enum (primary / secondary / success / info / warning / danger). The framework's previous omission of --danger was fixed in _statistics.scss; our wrapper already emitted the class so this becomes valid markup automatically when consumers upgrade.
  • Card --live-up / --live-down — already exposed via live_state="up" / "down". Upstream migrated the internal SCSS from rgba(...) over role colours to color-mix() over the 5-step sentiment scale; no API impact.
  • btn-split — verified the wrapper doesn't emit overflow: hidden on .pa-btn-split (the v2.7.0 chevron-corner fix relies on the container NOT clipping). Wrapper is correct as-is.
  • Timeline — v2.7.0 visual tweaks (simple-dot border-radius 50% → 30%, shadow opacity 0.3 → 0.5) are CSS-only with no wrapper change.

Demo app — /kpi section + non-KPI showcase updates

  • 8 new LiveViews under /kpi/* (sidebar entry "KPI", chart-line icon):
    • /kpi/dashboard — Combined dashboard exercising all 7 KPI components on one page (Hero + supporting + Terminal + Editorial + Sparkline list + Comparison gauges + Numeric strip + Bento). Useful for integration / spacing / theming verification.
    • /kpi/terminal-grid, /kpi/sparkline-list, /kpi/comparison-gauges, /kpi/hero-supporting, /kpi/bento, /kpi/numeric-strip, /kpi/editorial-minimal — each is a 1:1 port of upstream's demo/views/kpi-*.mustache: canonical card + layout-test stress sections (1×3 page-grid, 25/45 asymmetric, mixed grid modifiers) + per-page Usage Guide card + CSS Classes Reference card. The four chart-bearing pages (terminal grid, sparkline list, hero + supporting, bento) also include a Chart.js drop-in section demonstrating the library-agnostic :chart slot.
  • Chart.js drop-inchart.js@4.4.3 loaded via CDN in demo/lib/demo_web/components/layouts/root.html.heex. New PureAdminKpiChart LiveView hook (demo/assets/js/hooks/kpi_chart.js, ~130 lines) renders bar / line / area charts into any <canvas data-kpi-chart>. Reads currentColor from the slot's KPI sentiment cascade, re-renders on pa:theme-change. Tied to LiveView mount/destroy lifecycle (not a one-shot DOMContentLoaded scan).
  • Modals demo — new "Banded Modals · v2.7.0" section with 4 role variants (is_banded × success / warning / danger / info).
  • Stats demo — new "5-step sentiment scale · v2.7.0" card showing all five hero deltas (very_positive / positive / neutral / negative / very_negative) side-by-side.
  • Cards demo — new "Live-data direction · live_state" section with up / neutral / down tinted cards.
  • Data visualization demo — new gauge :size examples (8rem / 12rem default / 16rem / 20rem) + subtitle noting the v2.7.0 layout rebuild.

Theme system — per-developer disk overrides + manifest improvements

  • demo/pureadmin.json — declared all 15 themes (was 5). Team-wide, fetched via lockfile against pureadmin.io.
  • demo/.pureadmin.json (gitignored — per-developer override) — maps every theme slug to ../../pure-admin-themes/{slug} so themes load from a sibling checkout instead of the remote registry. Mirrors upstream pure-admin/.pureadmin.json.
  • DemoWeb.ThemePlug — dual-layout support. Themes are now served correctly from BOTH the registry layout (css/{name}.css — zip extraction) AND the local-build layout (dist/{name}.css — direct copy from a sibling pure-admin-themes checkout). valid_theme_dir?/2 probes either layout. New resolve_theme_file/3 transparently maps the public URL /themes/{slug}/css/{slug}.css to the actual on-disk file. Without this, the on-demand re-downloader silently overwrote local copies on every page load.
  • DemoWeb.PageContext.slim_color_variants — now passes through description per variant so the settings panel JS can use it as a <option title> tooltip.
  • PureAdminSettings JS hook — manifest-driven CSS path. _resolveThemeHref/1 reads manifest.colorVariants[0].file to build the stylesheet URL (handles both dist/ and css/). _applyThemeMode/1 is now pattern-aware via manifest.modeCssClass and clears every mode class declared by the manifest (was hardcoded light / dark only — important for themes declaring custom mode ids).

Tooling — Makefile + themes-install wiring

  • Makefile — new themes-install target runs npx @keenmate/pureadmin themes install from demo/ (defensive: skips silently if neither demo/pureadmin.json nor demo/.pureadmin.json exists). dev: and setup: now depend on themes-install, so make dev snapshots themes from .pureadmin.json disk overrides (or the remote-pinned lockfile) before booting the Phoenix server.

Fixed — KPI sparkline escape

  • Sparklines escaping their containers in Hero + supporting / Bento / Combined dashboard. PureAdminKpiSparkDot always wrapped the SVG in .pa-kpi-spark-wrap (which has no height declaration), even when the SVG's parent was already a tight positioned anchor with explicit height: 3rem (.pa-kpi-hero-main__chart-svg, .pa-kpi-bento-tile__chart-svg). The extra wrap broke the SVG's height: 100% chain; combined with preserveAspectRatio="none" + overflow: visible, the polygon stretched across the entire viewport. Ported upstream's tight-anchor check from pure-admin/demo/js/kpi-showcases.js: skip wrapping when getComputedStyle(parent).position !== 'static' AND parent.height ≈ svg.height (within 4px). Terminal grid (where the SVG IS the anchor with its own explicit height) was unaffected by the bug and remains unaffected by the fix.

Fixed — KPI tooltip popover rendered empty (black box)

  • Every KPI tile / row showed an empty black popover on hover when the host supplied detail_title_text + auto-built rows (the typical case). Phoenix's inner_block slot is always a non-empty list whenever the caller has open/close tags around the component — even if the only content between the tags is an empty <%= for d <- @detail do %>…<% end %> loop that produces no output. kpi_detail/1's has_inner? check (assigns.inner_block != []) treated that as "consumer provided custom popover content" → took the inner_block branch → rendered nothing → typed title_text + rows were silently ignored. The visible result was the popover chrome (pa-kpi-detail background + shadow) with no content inside.
  • Fix at every call site (8 tile/row modules: kpi_tile/1, kpi_sparkline_row/1, kpi_gauge/1, kpi_hero_main/1, kpi_hero_side/1, kpi_bento_tile/1, kpi_strip_row/1, kpi_editorial_tile/1): branch the call into <.kpi_detail>...</...> form when the consumer's :detail slot has content, and <.kpi_detail .../> (self-closing) form otherwise. Self-closing leaves inner_block == [], so kpi_detail/1's has_inner? check becomes a reliable signal. Auto-built rows render correctly; consumer's custom :detail slot still wins when supplied.
  • Defensive: no tooltip when none defined. has_detail? is computed as @detail != [] or @detail_title_text != nil — when both are absent, phx-hook="PureAdminKpiTile" isn't emitted, the <.kpi_detail> element isn't rendered, no .pa-kpi-detail exists in the DOM, and the JS hook's mounted() also has if (!this.detail) return as a safety net.

Added — pa:theme-change event dispatch in settings panel hook (canvas chart reactivity)

  • PureAdminSettings hook now dispatches pa:theme-change window events so consumer code that snapshots colours at draw time (Chart.js, ECharts, D3, custom canvas) can re-sample after a theme appearance change. Three event kinds:
    • { kind: "mode", mode } — fired at the end of _applyThemeMode after the pa-mode-* body class flips (light↔dark toggle).
    • { kind: "variant", variant } — fired at the end of _applyColorVariant after the pa-color-* body class flips (color variant picker).
    • { kind: "theme", themeId } — fired tied to the <link id="pa-theme-css"> element's load (or error) event after a theme stylesheet swap, so canvas re-sampling happens AFTER the new CSS is in effect — not before. One-shot listener that auto-removes itself; the next swap installs its own.
  • Early-return paths intentionally skip the dispatch — when the target class is already on <body> (nothing visual changed) the events don't fire, so consumers don't waste cycles re-drawing.
  • Matches upstream pure-admin demo's contract (demo/js/settings-panel.js dispatches the same event with the same kinds for mode / variant). Theme-swap dispatch is our addition — upstream doesn't fire on swap; the svelte side (1.8.0) covers this case with their separate chartColorSync action that listens for the link load independently.
  • Why this matters: SVG sparklines re-colour live via currentColor. Canvas charts (including the existing PureAdminKpiChart hook in the demo) cache getComputedStyle(canvas).color at draw time. Without this dispatch, the canvas froze on every theme/mode change while the SVG around it updated — visually broken. With this dispatch, the hook's _recolor() runs and the canvas re-paints in place.

Changed — PureAdminKpiChart demo hook gains stacked-bar + doughnut chart types

  • Two new data-kpi-type values for the demo/assets/js/hooks/kpi_chart.js hook:
    • data-kpi-type="stacked-bar" — multi-series stacked bar chart. data-kpi-points accepts array-of-arrays JSON ("[[120,140,160,180],[280,290,300,320],[60,70,80,90]]"); each inner array becomes a stacked series. Optional data-kpi-labels='["Q1","Q2","Q3","Q4"]' for x-axis labels (mostly cosmetic — axes stay hidden).
    • data-kpi-type="doughnut" — single-series doughnut. data-kpi-points is a flat array of slice values; cutout fixed at 62%, slice spacing 2px.
  • SERIES_OPACITY = [0.95, 0.65, 0.42, 0.25, 0.15] — multi-series and multi-slice colours derive from the host's resolved currentColor at decreasing alpha, so each series/slice reads distinctly while still inheriting the KPI sentiment + theme cascade. Both new types re-paint correctly on pa:theme-change.
  • readPoints/1 is now flatten-safe — when data-kpi-points is array-of-arrays (multi-series format) and a single-series chart type (line / bar) is requested, it returns the first inner array. So the same data attribute shape can drive either single-series or multi-series charts.

Changed — Dashboard rewrite (1:1 with @keenmate/svelte-pure-admin v1.8.0)

  • / (DashboardLive) overhauled to mirror svelte-pure-admin's docs/src/routes/+page.svelte. Diff vs. the previous version:
    • Old placeholder "Top Sales Products" card (an <i class="fa-chart-bar"> icon and the text "Chart Placeholder") replaced with a real kpi_sparkline_list carrying 5 product rows (Epsilon up-strong, Alpha / Delta / Beta up, Gamma down), each backed by a Chart.js area sparkline via <canvas data-kpi-chart>.
    • Old "Revenue Trend" section (hand-drawn double <polyline> SVG with hardcoded points) replaced with a kpi_hero_list hero_split="2_3" + a 13-point Chart.js area chart in the hero + 3 kpi_hero_side rail tiles (YTD Revenue, Q4 Actual, Forecast EOY).
    • Old separate "Revenue Trend & Traffic Sources" row removed — Traffic Sources moved into the right column of the new Top Sales row.
    • Timeline items in "Recent Activity" — first 3 (most-recent) items now carry is_filled to match svelte's isFilled. Filled markers signal "just happened"; the older two stay outlined.
    • New mount/3 assigns: top_sales (5 products with sentiment-ordered ranking + Chart.js data series strings) and revenue_trend_points (13-point string).
    • Everything else (4-up metric cards, KPI squares grid, Recent Orders table, Top Products / System Status / Quick Actions footer row) unchanged.

Changed — /kpi/dashboard extended with Chart.js stacked bars + doughnuts

  • New section: "Revenue by Segment · Q1–Q4 weekly"kpi_sparkline_list with 4 rows (Enterprise / SMB / Consumer / Marketplace) each showing a stacked-bar Chart.js chart (New + Renewals + Upgrades, 4 quarters). Each row carries the full popover prop set (Current / Previous / Δ absolute / Δ percent / Target) so the tooltip-fix is also exercised on this section.
  • New section: "Pipeline & Distribution · Chart.js mix"kpi_bento mixing both new chart types in one layout:
    • Hero: Pipeline by Stage (stacked bars, 8 weeks × 3 stages)
    • Customer Mix + Top Channels + Forecast Confidence (doughnuts at 4 / 5 / 2 slices)
    • Deals by Region + Stalled % (stacked bars, 6 categories × 2 series)
    • Sentiment cascade exercised: Stalled % is negative (red), Forecast Confidence is neutral (grey), the rest are positive / up_strong (green tones).
  • Integration test coverage: with all the recent fixes in place, this page exercises (a) the popover-rendering fix in 4 sparkline rows + 6 bento tiles, (b) the pa:theme-change event dispatch by re-colouring both inline-SVG sparklines (via currentColor) and Chart.js canvases (via _recolor()) on mode flip / theme swap, and (c) the new stacked-bar + doughnut chart types via the SERIES_OPACITY cascade.

Docs / demo / bookkeeping (catch-up pass)

  • docs/theming.md rewritten — full reference for the v2.8.0 token surface: canonical role tokens (--pa-success / -warning / -danger / -info), 5-step sentiment scale, text-contrast tiers (--pa-text-strong / -secondary / -tertiary), surface tints (--pa-surface-hover / -track), link tokens, chart-trendline tokens, detail-popover chrome, gauge-size, KPI namespaced tokens, plus a callout for the v2.8.0 :root defaults architectural fix.
  • pa-stat--square prefix-currency mode wired up. New is_prefix_symbol boolean on stat/1 (square variant only) flips DOM order to <symbol><number> for prefix currencies ($847K, ¥12.4M); default false renders <number><symbol> for suffix units (87%, 23°C). Matches v2.6.0's "markup order drives visual order" contract — no CSS modifier needed. Demo /components/stats gained a "Square stats — mixed units · v2.6.0" card showing all four cases side-by-side.
  • component-audit.md re-stamped to d49531c (v2.8.0). Seven existing rows re-anchored to 12b9d23 (v2.7.0): Modal (banded), Card (live-up/down sentiment), Stat (square redesign + 5-step), Timeline (visual tweaks), Button (btn-split chevron-corner fix), DataDisplay (fields-chips polish), DataViz (gauge rebuild). Nine new rows added under "no current snippet" for the KPI family (Kpi, KpiDetail, KpiTerminal, KpiSparklineList, KpiGaugeList, KpiHero, KpiBento, KpiStrip, KpiEditorial) — each anchored to its v2.7.1 SCSS partial plus the v2.8.0 commit that added the layout-modifier follow-ups.
  • package.json — declared peerDependencies: { "@keenmate/pure-admin-core": "^2.8.0" }. Previously the package made no machine-readable claim about which framework version it targeted; the CHANGELOG and README said "^2.8.0" but package.json was silent.

Pure-admin v2.5.0 sync

Re-anchored the alert component to pure-admin v2.5.0 (1f9d818). The framework rewrote the alert layout: structural children stack via flex-basis: 100% instead of inheriting the alert's flex row, the heading defaults to a compact look with the punchy treatment becoming opt-in, and the icon-vs-content alignment was inverted to centre by default.

  • Alert — drops the pa-alert__content wrapper when no :icon slot is supplied (Breaking). The framework's flex-wrap rules give structural children (__heading, __list, __actions, top-level <p>/<hr>) flex-basis: 100% so each lands on its own row inside .pa-alert directly. Wrapping them in __content "for consistency" moved them out of the > p / > hr selector reach, which broke the new layout. The wrapper is still emitted whenever an :icon slot is present (icon + non-icon content has to be the two flex children of the alert). Migration: apps that styled descendants via .pa-alert__content > selectors should switch to .pa-alert > selectors when the alert has no icon.
  • Alert — new heading_size attr (nil \| "lg"). v2.5.0 unified pa-alert__heading to default to the body font-size + semibold weight. heading_size="lg" adds the pa-alert__heading--lg modifier for the louder, deliberate-read presentation (blocking errors, system updates, quota warnings). Existing alerts that used <:heading> or heading_text will render visually smaller than before — pass heading_size="lg" to preserve the previous appearance.
  • Alert — new is_multiline attr. Adds pa-alert--multiline to opt back to align-items: flex-start. Use when an icon sits next to multi-line __content (heading + body + actions) so the icon stays at the top with the heading instead of centring against the whole stack. Default centred alignment is correct for icon + single-line content.
  • alert__actions, sizes, and padding scale — no markup change required on our side; rendering automatically picks up the new toast-style separator above __actions, the real --sm / --lg size scale, and the centred default alignment as soon as the consumer upgrades @keenmate/pure-admin-core to ^2.5.0.
  • component-audit.md — alert row re-stamped to 2ef8034 (2026-04-25, v2.5.0). Other components unchanged since v2.5.0 only touched _alerts.scss and supporting variables.

Demo /components/alerts page gained a "Header style: compact vs. punchy" card (same Validation failed / Saved messages rendered both ways), an explicit "Sizes" stack (sm / default / lg), and an "Icon with multi-line content" example showing is_multiline. The old "Compact Alerts in Grid" card was renamed to "Status strip layout" since it's a real-world layout pattern, not a sizes demo.

Component audit

Cross-checked every library component against the current @keenmate/pure-admin-core HTML snippets and recorded per-component audit status in the new component-audit.md at repo root. 30 snippets reviewed across two passes (anchor pure-admin commit cf75736):

  • First pass (2026-04-24, anchor e4f1cd6): 23 snippets that were upstream-audited at the time.
  • Second pass (2026-04-25, anchor cf75736): 7 newly-audited snippets that closed upstream's pending list and snippet gaps — modals.html, modal-dialogs.html, data-display.html, notifications.html, statistics.html, filter-card.html, detail-panel.html. None of the previously-audited snippets changed.

Drift fixes:

  • Popconfirm — server render now emits the initial pa-popconfirm--{bottom|top|start|end} class (previously only data-placement was set, so CSS rules keyed on the class rendered inconsistently before Floating UI ran). The client-side position helper in events/popconfirm.js now strips the logical start|end class pair on flip (was looking for physical left|right that never appeared) and maps Floating UI's physical result.placement back to our logical class via a physicalToLogical() helper — RTL collision-flipped popconfirms now render correctly.
  • Popover — title in .pa-popover__header now renders as <h4> (was <span class="pa-popover__title">) to match the snippet's semantic heading pattern and inherit the framework's heading-reset rules.
  • Callout.pa-callout__heading now renders as <h4> (was <div>) to match the snippet; picks up the shared heading margin reset instead of needing override rules.
  • Card — the :tools slot now emits <div class="pa-card__actions"> (was pa-card__tools, which has no CSS backing). Slot name kept for API stability.
  • Loader / spinnersize attr narrowed from [nil, "xs", "sm", "md", "lg", "xl", "2xl"] to [nil, "xs"]. The other sizes produced invalid class names (pa-spinner--lg, etc.) that don't exist in the SCSS framework — they all rendered at the default 16 px, which is confusing. Demo page updated to show only default + --xs. Breaking for apps passing those size values — drop the attr to fall back to default.
  • Modal — header close button class flipped from pa-btn pa-btn--primary pa-btn--icon-only pa-btn--sm to pa-btn pa-btn--sm pa-btn--icon-only pa-btn--secondary for the default modal and … pa-btn--light for themed modals (variant/header_variant set), matching the snippet's "secondary on neutral header / light on coloured header strip" pattern.
  • PureAdminDetailPanel JS hook — full rewrite to match the contract documented in detail-panel.html. Drag handle selector changed from .pa-detail-panel__handle (which never matched real markup) to .pa-detail-panel-resize; new width is written to --pa-local-detail-panel-width on <html> (was inline style.width on the panel, which only worked when the panel had no width-via-CSS-variable rule — i.e. never on real pure-admin markup); body picks up pa-detail-panel-resizing during drag to suppress text selection; handle picks up pa-detail-panel-resize--active; drag direction inverts in RTL; min-width clamped to 200 px. Breaking for apps wiring the old hook — they relied on a selector that didn't match the snippet anyway; switch the handle markup to <div class="pa-detail-panel-resize"> and the panel will resize correctly.
  • component-audit.md (new) at the repo root tracks every component's audit status, which snippet it was verified against, and the pure-admin commit hash at time of verification. Re-audits flip the row back to ⏳ when upstream ships a newer snippet.

Components with acknowledged gaps deferred to a later release (tracked in component-audit.md):

  • Code — still a Phase-2 stub (class naming / copy button / syntax tokens not yet modeled).
  • Profile — favourites subsystem (__favorite-item, __favorite-icon, __favorite-label, __favorite-remove, __favorites-add) not yet exposed as components; apps hand-roll the markup today.
  • Timeline — alternating-layout __date/__time logic is muddled and pa-timeline--single-column isn't exposed.

Security

  • Pager — dropped Phoenix.HTML.raw/1 on the icon_* attrs (audit finding #1, High). The four navigation icon attrs (icon_first, icon_previous, icon_next, icon_last) are now rendered HTML-escaped and default to Unicode chevrons («‹›») rather than HTML entity strings. Callers that need markup (e.g. Font Awesome, inline SVG) use the new :first_icon/:previous_icon/:next_icon/:last_icon slots. Breaking for apps passing HTML in the string attrs; migrate those to the slots.
  • PureAdminFlash hook — validate URL scheme in markdown links (audit finding #2, High). Markdown links of the form [text](url) previously let any URL — including javascript:alert(1) — flow into href=. URLs starting with javascript:, data:, or vbscript: (case- and whitespace-insensitive) are now replaced with #. Other schemes (http/https/mailto/tel) and relative paths pass through unchanged.
  • Strict-CSP support — removed every inline onclick= and <script> from components (audit findings #10 and #11). popconfirm/1, popover/1, badge_group/1, tabs/1 (scrollable), and field/1 (copy buttons) previously rendered inline event handlers and embedded <script> blocks that required 'unsafe-inline' on CSP script-src. Behaviour moved into a single delegated-events module (lib/assets/js/events/) and exposed via new initPureAdminEvents() export — components now emit data-pa-* attributes and the module's document-level click listeners dispatch on them. Apps can now run with script-src 'self' out of the box; the FOUC prevention script (<.fouc_prevention_script />) remains the single inline <script> and needs a per-request nonce in strict-CSP setups.
    • Migration: add initPureAdminEvents() alongside your existing initModalDialogs() call in app.js. No template changes required for library components.
  • desc_tablelabel_width validated as a CSS length (audit finding #4). The attr is now interpolated into style= only if it matches a single CSS length token (digits + %/px/rem/em/vw/vh/ch/cm/mm/in/pt/pc). Anything containing ;, whitespace, parens, or extra tokens is ignored rather than injected, closing a CSS injection path if the attr is bound to user input.
  • Flash / Toast — action buttons no longer round-trip through data-action (audit finding #5). push_flash / push_toast actions were previously serialised to JSON, attribute-escaped, then parsed back on click. Buttons are now built as DOM elements with the action object captured directly in a closure — no JSON in attributes, no bespoke _escapeAttr, and no attribute-escape attack surface. Removed the now-unused _escapeAttr helper from both hooks.
  • PureAdminProfilePanel — URL scheme check before navigation (audit finding #6). Favourite items previously assigned dataset.href directly to window.location.href. javascript: / data: / vbscript: / file: URIs are now rejected; everything else (http/https, mailto:, tel:, sms:, deep-link schemes like slack://, intent://, myapp://, relative paths) passes through.
  • PureAdmin.Helpers.safe_url/1 (audit finding #7). New helper that returns url if it uses a safe scheme, otherwise a fallback (defaults to "#"). Deny-list semantics — blocks only the four schemes a browser will execute (javascript:, data:, vbscript:, file:), so custom app schemes and non-HTTP protocols still work. Applied automatically to href={@href} in pa_link/1, button/1, navbar_nav_item/1, sidebar_item/1, and profile_nav_item/1, so apps binding user content to those attrs get defense-in-depth for free.
  • getPageContext() — robust parse (audit finding #8). The hidden-input JSON is now parsed inside a try/catch and validated to be a plain object; malformed or non-object payloads fall back to {} with a console.warn instead of throwing into the caller.
  • Settings / sidebar resize — localStorage inputs validated (audit finding #9). font-size and container-width settings are now allowlist-checked before being concatenated into a CSS class name. Sidebar width is integer-parsed and clamped to [180, 500] when restored from storage (and only the numeric value, no unit, is persisted).
  • PureAdmin.custom() modal — documented trust boundary (audit finding #12). The caller-supplied render(container, close) function writes directly to the DOM — the JSDoc now makes it explicit that this is a ⚠ XSS sink if user content is inserted via innerHTML.

Demo

  • Swap the Stored Submissions card for <.table_card is_scrollable> on the /phoenix/form-demo page so narrow viewports scroll the table horizontally inside the card instead of clipping the rightmost columns.

[1.1.0] - 2026-04-23 [PUBLISHED]

Added

  • :field attr on form componentsinput/1, textarea/1, select/1, checkbox/1, radio/1, and form_group/1 now accept field={@form[:x]} for one-line Phoenix form binding. Derives name, id, value (or checked), and error state from the Phoenix.HTML.FormField struct; explicit attrs still win. used_input?/1 is respected so unsubmitted fields don't show stale errors.
  • Auto-rendered field errors — when field= is set and the field has errors, input/textarea/select automatically render a form_help below themselves in the error variant. Opt out with show_errors={false}. form_group flips to validation="error" in the same condition.
  • PureAdmin.Components.Form.translate_error/1 — default %{key}-interpolating error formatter, overridable via config :keen_pure_admin, :error_formatter (MFA tuple or 1-arity function) for Gettext-aware apps.
  • PureAdmin.DateTime — new top-level helper with format/2 (styles: :short_date, :long_date, :full_date, :time, :long_time, :short_date_time, :long_date_time, :relative, or any raw strftime pattern) and relative/2 (buckets time diffs into now / seconds / minutes / hours / yesterday / days / weeks / months / years, past and future; accepts now: for deterministic tests). Accepts Date, NaiveDateTime, and DateTime. Month names, weekday names, and relative phrases all flow through PureAdmin.Translations.t/2.
  • Translation keys — 47 new keys under pureAdmin.datetime.* (connectors, relative phrases past/future, 12 month names, 7 weekday names).
  • push_flash/5 gained replace: true — wipes any existing alerts in the container before rendering the new one, so status messages don't stack.
  • PureAdmin.Components.Flash.clear_flash/2 — dedicated "clear without pushing" helper.
  • PureAdminFlash hook — honors both the replace payload flag and a new pa:flash-clear event.

Fixed

  • Getting Started page — resolved an outdented-heredoc compiler warning by moving a code block into a module attribute.

[1.0.0] - 2026-04-11 [PUBLISHED]

First stable release. Phoenix LiveView component library wrapping Pure Admin CSS framework into 35+ function components, 14 JS hooks, and 3 LiveComponents.

Highlights

  • Drop-in CoreComponents replacementuse PureAdmin.Components replaces the Phoenix-generated CoreComponents module
  • PureAdmin.Config — centralized app configuration (:app_name, :app_logo, :app_version, :copyright, :font_class). Components like navbar_brand/1 and footer/1 read from it automatically
  • package.json — enables import "keen_pure_admin" in esbuild for hex dependents
  • pureadmin create template — scaffold a full Phoenix LiveView app with PureAdmin layout via the PureAdmin CLI

What's included

  • 35+ function components: layout, navbar, sidebar, buttons, badges, cards, tables, modals, forms, tabs, tooltips, toasts, flash, pagers, loaders, timeline, code, stats, and more
  • 14 JS hooks: sidebar toggle/resize/submenu persistence, settings panel, profile panel, command palette, toast/flash, tooltip/popover (Floating UI), split button, char counter, checkbox, infinite scroll
  • 3 LiveComponents: CommandPalette, ToastLive, DialogService
  • Full BEM class support with build_classes/3 helper
  • i18n via runtime translation callback (~60 keys)
  • Page context system (server → JS, CSP-safe)
  • Persistent logging (PureAdmin.logging.enableLogging() survives page reloads)

Installation

{:keen_pure_admin, "~> 1.0"}

See the README for full setup guide or use the PureAdmin CLI:

npx @keenmate/pureadmin create my-app --template phoenix-liveview

Compatible with @keenmate/pure-admin-core v2.3.6 and Phoenix LiveView ~> 1.0.


[1.0.0-rc.2] - 2026-04-03 [PUBLISHED]

Added

  • PureAdmin.Config — application-level configuration system. Set :app_name, :app_logo, :app_version, :copyright, :font_class in config.exs and components read from it automatically
  • navbar_brand/1 — falls back to config :app_name and :app_logo when no inner content provided
  • footer/1 — falls back to config :copyright (start slot) and :app_version (end slot) when no slots provided
  • PureAdmin.Config.root_html_attrs/0 — returns %{class: font_class} for the <html> element, supports pa-font-responsive and granular pa-font-base-{9-12} / pa-font-mobile-{9-12} classes from pure-admin-core v2.3.6
  • Getting Started page — new demo page with installation, setup timeline, responsive font sizing, available themes, and component overview (mirrors Svelte demo structure)

Changed

  • Root layout — removed separate pure-admin.css link; theme CSS already includes the core framework
  • Dockerfile — switched theme download from broken curl + zip API to npx @keenmate/pureadmin themes --dir CLI, added CSS copy step for app.css

Bug Fixes

  • Sidebar — fix mobile toggle not working: toggle_sidebar() was hardcoded to dispatch to #sidebar, now accepts configurable target ID via navbar_burger target attr
  • LoggerenableLogging() now persists across page reloads via localStorage
  • Sidebar — optimize resize handler to only act on breakpoint crossings
  • Docker build — fix 404 for CSS assets (pure-admin.css, audi.css) — app CSS was never copied to priv/static and theme bundle API returned empty zip

[1.0.0-rc.1] - 2026-04-01 [PUBLISHED]

Flash Messages

  • PureAdmin.Components.Flash — new module with two approaches:
    • Standard flashflash/1 and flash_group/1 as drop-in replacements for CoreComponents, styled with pa-alert BEM classes. Works with Phoenix's built-in put_flash/3
    • Independent flash containersflash_container/1 + push_flash/5 for multiple independent flash groups on the same page. Each container receives messages independently via a JS hook
  • PureAdminFlash JS hook — client-side rendering of flash alerts. Supports markdown body (bold, italic, [links](url), lists, --- horizontal rules), action buttons with pushEvent callbacks, auto-dismiss, and dismissible close button
  • Markdown body — flash message text supports basic markdown rendered as proper pa-alert__content HTML
  • Action buttons — flash messages can include action buttons that push events back to the LiveView or dismiss the flash

Toast Updates (pure-admin 2.3.0)

  • Toast action buttonspush_toast/5 accepts :actions option with %{label, event, params, variant, dismiss} maps. JS hook renders pa-toast__actions with pa-btn--xs buttons inside pa-toast__content. Clicking an action fires pushEvent back to the server, then auto-dismisses
  • Toast progress bar:progress option renders pa-toast__progress bar that animates from 100% to 0% over the duration. :progress_color option overrides the bar color via inline style
  • Filled toasts via push_toast:filled option renders pa-toast--filled-{variant} class
  • max_width — custom max-width per toast (e.g. max_width: "50rem")
  • Width ratchet — container min-width tracks the widest toast shown, resets when container is empty
  • Click-to-dismiss — toasts without actions are click-to-dismiss (cursor: pointer). Toasts with actions require close button or action click

Command Palette v2

  • Multi-step commands (/prefix) — register commands with steps, each with prompt, placeholder, and optional free_text. Steps progress sequentially with selections displayed as locked tokens. Commands complete via handle_info({:command_complete, cmd_id, selections})
  • Search contexts (:prefix) — register scoped search contexts with shortcut and aliases. Typing :p laptop searches products for "laptop"
  • Global search — typing without a prefix searches across all data
  • 6 modesidle, command_list, command_step, context_list, context_search, global_search with full state machine transitions
  • Step filtering — typing in a command step filters the step options in real-time
  • Step back — Backspace at position 0 or Escape goes to previous step (or back to command/context list)
  • cp: event protocol — namespaced events (cp:toggle, cp:input, cp:select, cp:step_back, etc.) replacing command_palette_ prefix
  • cp:reset_input push_event — force-clears browser input on mode transitions (LiveView doesn't patch focused inputs)
  • Debounced search — 150ms debounce for context and global search, instant for command/context list filtering
  • Mode-aware keyboard — Escape goes back in step/context modes instead of closing. Footer hints update per mode
  • Two display stylesdisplay="inline" (default, Svelte-style: full sentence in input with command badge) and display="tokens" (original: colored token spans above a clean input). Switchable at runtime

Command Palette (pure-admin 2.3.3 / 2.3.4)

  • pa-command-palette__input-wrapper — new wrapper around input + context label for correct positioning
  • pa-command-palette__token-prompt — step prompt text between token badges (replaces __token--prompt)
  • Standard pa-badge — item badges now use pa-badge instead of custom pa-command-palette__item-badge
  • Token badges — step tokens in tokens mode use pa-badge pa-badge--primary for command name, plain pa-badge for values
  • Tokens &:empty hiding — tokens div hides automatically when empty via CSS
  • Home screen — idle state shows categorized list of commands (with Alt+key hotkey badges) and search contexts, all clickable
  • HotkeysAlt+D Deploy, Alt+A Assign, Alt+G Go to Page, Alt+T Switch Theme. Work globally and inside the palette
  • Global search includes commands/contexts — typing "deploy" finds the Deploy command alongside data results. Selecting a command/context enters that mode
  • Form codes/go page options have numeric codes (e.g., 24 for Alerts). filter_options matches on label, description, and exact code
  • pa-command-palette__home — home screen container with __home-section separators and __home-heading labels
  • pa-command-palette__shortcut — flex container for multi-key hotkey badge groups

Translations (i18n)

  • PureAdmin.Translations — new module with runtime translation callback system. Ships with ~60 English defaults under pureAdmin.* flat keys. Apps override via config: config :keen_pure_admin, translate: &MyApp.translate/2
  • t(key, params) — main translation function with %{param} interpolation. Falls back to English when callback returns nil or isn't configured
  • interpolate(string, params) — exported helper for app callbacks
  • All components updated — hardcoded English strings replaced with t() calls across command palette, pager, popconfirm, modal, alert, toast, flash, settings panel (~60 keys)

Components (pure-admin 2.3.1 / 2.3.2)

  • split_button/1 — chevron icon now points up (fa-chevron-up) for top-* placements, down for bottom-*
  • split_button/1 primary icon — new icon attr for Font Awesome icon on the primary button (e.g. icon="fas fa-download")
  • split_button/1 item icons:item slot now accepts icon attr (e.g. icon="fas fa-file") rendering as pa-btn-split__item-icon
  • split_button/1 inline action buttons:item slot accepts action_icon, action_event, action_value, action_variant attrs for inline action buttons beside menu items (e.g. delete/remove). The JS hook forwards clicks via pushEvent since the menu is moved to document.body
  • button/1 label wrapping — button text is wrapped in <span class="pa-btn__label"> when an icon is present, enabling proper centering with align="center"
  • split_button/1 menu structure — uses pa-btn-split__menu-inner wrapper and pa-btn-split__item-row BEM elements (replaces inline styles), matching pure-admin 2.3.2 two-container pattern
  • input_group/1:button slot now documented to use class="pa-input-group__button" on the button element

Demo

  • Phoenix / LiveView sidebar section — new section for framework-specific features (matches Svelte demo's "Svelte" section)
  • CoreComponents Migration page — migration table showing replaced vs manual functions, setup timeline
  • Flash Messages page — independent containers demo, variant showcase, markdown + action buttons demo, standard @flash compatibility
  • Toasts page — added progress bar demos (standard + filled), action toast demos (Undo, Retry, Update, Filled + Actions), theme color toasts with filled subsection
  • Buttons page — split buttons card moved after Responsive Direction to match pure-admin layout 1:1, consolidated into single card with subsections (Sizes, Upward Placement, Custom Icons)

Theme Cache Invalidation

  • ThemePlug auto-refresh — cached themes are validated against pureadmin.io using content_sha from the theme's checksums field. On first access after startup, the plug sends a conditional request (If-None-Match) to the API in the background. If the server returns 200 (sha mismatch), the theme is re-downloaded without blocking the current request. 304 means the cache is fresh. Freshness checks are throttled to once per 10 minutes per theme
  • make themes-clear — new Makefile target to force-clear the theme cache, triggering fresh downloads on next access

Documentation

  • Prerequisites section added to README and getting-started guide (mix phx.new --no-tailwind)
  • Main site link added to README (pureadmin.io)
  • Theme installation guide — actual zip structure from pureadmin.io API, self-contained relative paths. Three installation options: Pure Admin CLI (@keenmate/pureadmin), manual download, CI/CD Dockerfile
  • Creating custom themes — new section in theming docs referencing the CLI's init, build, pack, publish workflow
  • Theme customization via SCSS — variable overrides, custom fonts, baseline correction, complete example
  • make help — all Makefile targets now have ## description comments

Compatible with @keenmate/pure-admin-core v2.3.5.

v1.0.0-rc.1 (initial)

First release candidate. Consolidates all v0.x development into a stable API.

Highlights

  • 35+ function components covering the full Pure Admin CSS framework: layout, navigation, forms, tables, data display, modals, toasts, and more
  • 13 JS hooks for interactive features: settings panel, tooltips, popovers, split buttons, sidebar persistence, command palette, character counters, and more
  • Drop-in CoreComponents replacement -- use PureAdmin.Components gives you everything
  • Full BEM class support with build_classes/3 helper
  • RTL support for tooltips and popovers
  • Podman/Docker support for the demo app
  • Live demo at elixir.demo.pureadmin.io

Settings Panel

  • Dynamic theme manifests — settings panel JS now fetches /api/themes/manifests and dynamically populates theme selector (sorted alphabetically), replacing hardcoded themes prop
  • Color variants — new data-section="color-variant" section, shown/hidden based on theme manifest. Applies pa-color-{variant} CSS class to body. Supports per-variant mode lists
  • Mode per variant — mode selector updates when color variant changes, auto-hides if only one mode available, auto-applies single mode
  • Font detection from manifest — "Theme Default" option shows bundled font name (e.g., "Theme Default (Fira Sans Condensed)"), skips Google Fonts download if theme already bundles the font
  • Removed themes attr from settings_panel/1 — themes are now loaded dynamically, only default_theme attr remains

On-Demand Theme Downloads

  • ThemePlug — new Plug that serves /themes/:name.css with on-demand downloading from pureadmin.io. When a theme CSS is requested that isn't bundled at build time, it downloads from pureadmin.io/api/themes/:name/download, extracts CSS and theme.json manifest from the zip, and caches to disk
  • /api/themes/manifests — returns all available theme manifests as JSON (from both build-time priv/static/themes/ and on-demand cache)
  • /api/themes/:name/manifest — returns a single theme's manifest
  • Negative cache — failed downloads are cached for 10 minutes (ETS-based)
  • Slug validation — theme names must match ^[a-z0-9-]+$
  • Pure Erlang — uses :httpc and :zip for downloads, no external tools needed in runtime image
  • ?theme=cobalt2 — query param sets localStorage and swaps CSS link before paint, triggering on-demand download if theme not bundled

Dockerfile & Deployment

  • Dockerfile (demo/Dockerfile) — multi-stage build with elixir:1.18-slim builder and debian:trixie-slim runtime. Downloads theme bundles from pureadmin.io at build time (CSS + manifests). Configurable via THEMES_URL build arg
  • Makefile — added podman-build, podman-run, podman-stop, podman-restart, podman-logs, podman-clean, podman-deploy, podman-push targets matching pure-admin conventions. Registry: registry.km8.es
  • .dockerignore — excludes _build/, deps/, node_modules/, .git/
  • force_ssl — now opt-in via FORCE_SSL=true build-time env var (was always-on, breaking local container testing)

New Hooks

  • PureAdminInfiniteScroll — IntersectionObserver-based infinite scroll hook. Fires a LiveView event when a sentinel element scrolls into view. Configurable via data-event, data-has-more, data-throttle, data-root-margin. Throttled to prevent rapid-fire triggers.

Components

  • list_item/1 — added :meta slot for rich meta content (badges, icons) alongside existing meta_text string attr
  • timeline/1 — fixed alternating variant to use <div> container and correct BEM classes (pa-timeline__date + pa-timeline__icon instead of pa-timeline__time + pa-timeline__marker). Added align attr ("start", "end") and is_keep_layout for alternating layout control — replaces raw class="pa-timeline--start" usage
  • timeline_item/1 — auto-detects layout from props: block/alternating pattern when icon_text or :icon is provided, simple pattern otherwise
  • card/1 — added is_bordered attr for bordered card styling — replaces raw class="pa-card--bordered" usage
  • button_group/1 — added responsive attr ("sm-vertical", "md-horizontal", etc.) for responsive direction changes at breakpoints — replaces raw class="pa-btn-group--md-vertical" usage
  • grid/1 — added align="stretch" to allowed values
  • column/1 — added is_no_padding, is_grow, is_shrink props for flex layout control
  • values: validation — added compile-time value validation to all variant, color, theme_color, and level attrs across alert, badge, label, composite_badge, button, popconfirm, data_display, form, table, and typography components. Typos like variant="outine-danger" or color="10" now produce compile warnings
  • pure-admin 2.2.0 support — updated CSS to v2.2.0. Added theme_color attr to button/1, callout/1, and toast/1 for theme color slots 1-9. Added is_filled attr to toast/1 for full-color background toasts. Alert theme_color now uses proper pa-alert--color-{N} / pa-alert--outline-color-{N} classes (was pa-bg-color-{N})

Demo

  • 45 demo pages covering all Pure Admin CSS components (up from 34)
  • Dashboard — rewritten to match pure-admin reference 1:1: 4 hero stat KPIs, 6 square stats with color variants, revenue trend SVG placeholder, traffic sources table, timeline activity feed, recent orders with badges, top products, system status with badge meta, quick actions
  • Timeline pages — split into 4 pages matching pure-admin: Simple (color-coded, filled bullets), Block (alternating with layout modifiers: start/end/keep-layout), Feed (avatars, comments, date headers, load more, infinite scroll), Advanced
  • Design pages — new section: Colors (semantic + theme slots), Theme Variables (45 CSS custom properties), CSS Helpers (visibility, borders, overflow, cursor), Layouts (structure, navbar, sidebar, container width)
  • New pages — Components Overview (index with 27 component cards), Notifications (interactive list with filtering), Sizing & Layout (width/spacing/display utilities), Virtual Scroll (infinite scroll demo with PureAdminInfiniteScroll hook, virtual scroll planned)
  • Raw HTML consolidation — converted remaining raw pa- HTML across 10+ demo pages to use components
  • Root layout — theme CSS loaded via <link id="pa-theme-css"> with inline script that reads ?theme= from URL and swaps href before paint
  • Footer — updated version to v1.0.0-rc.1

README

  • Added Hex/GitHub/path installation options
  • Added full setup guide (CSS, Floating UI, Font Awesome, FOUC, toasts)
  • Added Podman build/run/deploy instructions with make targets
  • Updated component and hook tables

Compatible with @keenmate/pure-admin-core v2.2.0.


v0.3.4

New Components

  • comparison: New comparison_table/1, comparison_row/1, comparison_section/1, comparison_value/1 — comparison table components for two-column and three-column data diff patterns (version control, merge conflicts). :cell slot with is_changed, is_solid, is_conflict modifiers. comparison_value/1 includes copy-to-clipboard button with visual feedback (icon swap)

Layout

  • sidebar_submenu: Fix accordion behavior — opening one submenu no longer closes all others. Each submenu now uses scoped toggle_submenu/1 with {:closest, ".pa-sidebar__item"} and ID-targeted <ul>
  • sidebar_submenu: Add id attr for stable submenu identification, phx-hook="PureAdminSidebarSubmenu" for localStorage persistence of open/closed state across navigations
  • sidebar_submenu: Add FOAC prevention — fouc_prevention_script injects <style> tag from localStorage before sidebar HTML renders, eliminating flash of collapsed submenus
  • split_button: Close other open split buttons when opening a new one
  • split_button: Add on_click and action attrs to :item slot for LiveView event handling via hook pushEvent

JS

  • clipboard: Global kpa:clipboard-copy event listener — copy to clipboard via JS.dispatch, with visual icon feedback (clipboard → checkmark → revert). Works anywhere, no per-page setup needed
  • PureAdminSidebarSubmenu (new hook): Persists sidebar submenu open/closed state to localStorage. Restores state on mount, URL-active submenus always win over localStorage. Uses MutationObserver to detect JS command class changes
  • PureAdminSplitButton: Fix multiple open menus — opening a split button now closes any other open one

Demo

  • Add Comparison Tables demo (/tables/comparison) — two-column, three-column, solid background variants using table_card
  • Restructure routes to hierarchical paths (/components/buttons, /tables/standard, etc.) — enables String.starts_with? for sidebar submenu is_open derivation from URL
  • Add global toast service — <.toast_container> in app layout, PubSub-based handle_info hook in on_mount for cross-page toast delivery
  • Add Split Buttons demo section to buttons page with primary actions, dropdown items, sizes, and toast feedback
  • Background task toasts now use PubSub broadcast instead of direct send/2, so toasts arrive on whichever page is active

v0.3.3

Components (pure-admin 2.1.0 sync)

  • button: Add split_button/1 — split button with primary action + dropdown toggle via PureAdminSplitButton JS hook. :item slot with is_danger modifier, placement attr for Floating UI positioning, on_click for primary action
  • tooltip: Add is_keyword modifier — dotted underline + help cursor for inline term explanations (pa-tooltip--keyword)

Demo

  • Add Table Multi-Select demo with cross-filter selection, summary bar, expandable details, bulk actions
  • Update pure-admin CSS to v2.1.0

v0.3.2

Components

  • table: Add table_card/1 — card wrapper with header/footer, color variants (primary/success/warning/danger), theme colors (1-9), is_scrollable, is_plain
  • table: Add :foot slot for <tfoot> support (colspan, totals rows)
  • table: Add align attr on :col slot (start/center/end) for per-column text alignment
  • table: Add is_responsive_grid modifier for CSS Grid responsive collapse
  • table: Render :action column first (leftmost) to match pure-admin reference
  • table_container: Add is_panel mode with title_text, :header, :actions slots
  • table_container/table_card: Use <h3> for titles to match pure-admin CSS selectors (colored header text)
  • pager: Fix layout to match pure-admin: controls-left | info-center | controls-right (was all-controls then info)

  • pager: Add icon_first, icon_previous, icon_next, icon_last attrs for custom icon sets
  • pager: Change info format to / N pages (was Page ... of N)
  • data_display: Add is_value_end and is_value_center modifiers to banded/1 and desc_table/1
  • form: Add input_wrapper/1 — wraps input/select with optional clear (×) button (pa-input-wrapper + pa-input-wrapper__clear), has_clear, on_clear attrs
  • filter_card: New filter_card/1 component — expandable filter card with :filters, :advanced_filters, :actions slots, toggle/clear/refresh/apply buttons, is_expanded, is_loading, is_disabled states, matching Svelte FilterCard

Demo

  • Split tables into three pages: Standard Tables, Table Sizing, Responsive
  • Rewrite Standard Tables demo to match pure-admin reference 1:1 (same data, sections, structure)
  • Rewrite Table Sizing demo to match pure-admin reference 1:1 (same data, card structure with inline code headers, text action buttons with correct sizes per variant)
  • Rewrite Responsive Tables demo to match pure-admin reference 1:1 (How It Works grid, basic/product/orders tables with actions and badges, CSS Grid Custom Layouts with data-grid/data-span, HTML Implementation with grid advanced section, SCSS variables reference, Testing Tips, LiveView code examples)
  • Add Table Filters demo matching pure-admin reference (basic search filter, expandable filters with advanced section toggle, inline horizontal filters, active filter tags with composite badges)
  • Remove invented pa-page-title/pa-page-subtitle CSS classes from all demo pages — use plain <p> like the reference
  • Clean up demo.css — remove unused chart/activity/status classes

v0.3.1

New Components

  • popconfirm/1 — small confirmation dialogs anchored to trigger buttons via Floating UI, with message, placement (top/bottom/start/end, RTL-aware), icon_variant (danger/warning/info), is_compact, confirm_event/confirm_value for LiveView integration, click-outside-to-close, move-to-body positioning

RTL Support

  • tooltip/1 — position values renamed: rightend, leftstart (RTL-aware via document.dir)
  • popover/1 — placement values renamed: rightend, leftstart (RTL-aware via document.dir)
  • Tooltip JS hook — added resolveLogicalPlacement() that maps start/end to physical left/right based on document direction

Components

  • badge/1 — replaced width attr (pa-badge--w-Nx classes) with max_width attr using maxwr-N text-truncate utility classes
  • modal/1 — fixed popover alignment classes (pa-popover--center, pa-popover--end) to be copied to content element when moved to document.body
  • Tooltip JS — fixed theme color variants not applied to floating tooltips (regex only matched first class, which was always floating)

JS

  • modal_dialogs.js — new ES module wrapping pure-admin's programmatic dialog API (PureAdmin.confirm/alert/prompt/custom), exported via initModalDialogs()

Demo

  • Modal Dialogs page — new page with confirm/alert/prompt demos, position options, sequential dialogs, LiveView server integration, API reference tables
  • Modals page — rewritten to match pure-admin reference (grouped layout, settings modal, richer modal content)
  • Tooltips page — updated to use start/end position naming
  • Badges page — updated fixed-width section to use max_width utility approach, renamed "Left-Side Ellipsis" to "Start-Side Ellipsis"
  • Popconfirm page — new page with basic popconfirms (delete/archive/reset with icon variants), compact variant, table row delete confirmations with LiveView integration
  • Command Palette page — new page with Spotlight-style search overlay, Ctrl+K shortcut, context switching (/p, /o, /u, /i), keyboard navigation, pagination, LiveView server-side search
  • Data Display page — new page with pa-fields layouts: stacked, multi-column grid (cols-2/3/4), field groups, horizontal, table-style bordered, striped, compact, inline, row, relaxed, filled, color-coded borders
  • Data Display 2 page — new page with advanced patterns: Ant Design descriptions table (1/2/fixed columns), dot leaders (invoice style), property cards, banded rows
  • Data Visualization page — new page with CSS-only visualizations: progress bars (sizes/colors/striped/animated/rounded), stacked bars with legends, progress rings, dashboard gauges, data bars in tables, activity heatmaps, sparkline bars
  • Detail Panel page — new page with inline split-view and overlay modes, table row selection, field-group detail content
  • Fixed all compile warnings across demo (nested if parentheses, dynamic_tag name deprecation, undefined attributes, missing slots)

v0.3.0

Compatible with @keenmate/pure-admin-core v2.0.2.

Components

  • section/1 — added title_text attr that renders an <h3 class="pa-section-title"> heading
  • code/1 — fixed to render plain <code> without pa-code class, matching Svelte reference
  • card/1 — added :subtitle slot (rich HTML counterpart to subtitle_text)
  • card/1 — fixed subtitle_text to render with pa-text pa-text--secondary class matching Svelte reference
  • card/1 — fixed title rendering: plain <h3> without wrapper div when no icon is present, matching Svelte reference
  • card/1 — fixed non-inline tabs to render outside header as sibling (matching reference DOM structure)
  • card/1 — fixed inline tabs to render after title (not before), matching reference order
  • form_label/1 — added is_required attr that renders asterisk indicator
  • checkbox/1 — added :label_content slot, is_indeterminate (via PureAdminCheckbox hook), is_x_mark
  • checkbox_box/1 — added is_indeterminate support
  • tabs/1 — scrollable overflow now renders proper scroll buttons and scroll container
  • tab_item/1 — added deterministic id and :not() exclusion to prevent 2px flash on tab switch
  • switch_tab/3 — scoped tab/panel switching via tabs_id + content container id to prevent cross-group interference
  • label/1 — fixed outline to use pa-label--outline class (not pa-label--outline-{variant}), matching Svelte reference; added xs/xl size support
  • badge_group/1 — added limit, total, is_expanded, on_toggle, more_text, collapse_text attrs for expand/collapse with two modes: server-side (fires LiveView event for lazy loading) and client-side (CSS-based hiding with JS class toggle, survives LiveView DOM patching)
  • badge/1 — added width attr (pa-badge--w-{size} BEM class) and is_ellipsis_start for left-side truncation
  • composite_badge/1 — added is_interactive, on_label_click, on_button_click, label_variant, button_variant, button_text, :icon_content slot for full interactive support with separate label/button click events
  • checkbox_box/1 — new low-level checkbox (input + box) for tables and composite components
  • checkbox_list/1 — new container with variant (compact/bordered/striped) and layout (inline/grid/2col/3col)
  • checkbox_list_item/1 — new item with label, description, state (disabled/locked), and :actions slot
  • basic_list/1 — new component for styled <ul> with spacing, icon, bordered, striped, inline, unstyled variants
  • ordered_list/1 — new component for styled <ol> with numeric, roman, alpha styles
  • definition_list/1 — new component for styled <dl> with standard and inline layouts

JS Hooks

  • PureAdminCharCounter — new hook for textarea/input character counting with configurable max, translatable message templates via data-msg/data-msg-over with {count}/{max} placeholders
  • PureAdminCheckbox — new hook for syncing indeterminate property from data-indeterminate attribute (required for tri-state checkboxes)

Components (enhanced)

  • tooltip/1 — new CSS-only tooltip wrapper with position, variant, multiline, help cursor support
  • popover/1 — new click-triggered popover with title, placement, size, alignment, custom trigger slot
  • pager/1 — enhanced with page input, first/last buttons, configurable events, custom info text, :controls and :info slots
  • load_more/1 — enhanced with phx-click support via :global attrs
  • loader_center/1 — new centered loader container (flexbox centering)
  • loader_overlay/1 — enhanced to accept custom content via slot (not just default spinner)
  • toast/1 — added title_text, message_text, is_visible, on_close, :icon slot, close button with SVG icon
  • toast_container/1 — updated positions to use logical names (top-end, top-center, top-start, bottom-end, bottom-center, bottom-start)

Demo

  • Cards page — complete rewrite matching Svelte pure-admin reference (14 sections: same-height, basic, header three-part layout, colored, theme colors, bordered, ghost, underlined headers, statistics, statistics with trends, interactive, advanced features, data display, CSS classes reference)
  • Grid page — complete rewrite matching Svelte pure-admin reference (overview, basic usage, percentage columns, fraction columns, responsive grid, offsets, row alignment, no gutter, visibility utilities, nested grids, quick reference, code examples)
  • Buttons page — complete rewrite matching Svelte pure-admin reference (variants, sizes, outline, states, block, button groups with gap sizes, vertical alignment, responsive direction, text truncation, icon buttons, icon-only, fixed width, text alignment, ripple effects, loading states, usage guide, CSS classes reference)
  • Inputs page — new page matching Svelte pure-admin reference (text inputs with states/sizes/validation/theme colors, input groups with prepend/append/buttons/toggle mode, input types, select dropdowns, textareas, checkboxes & radios with sizes, width variations, CSS classes reference)
  • Validations page — new page matching Svelte pure-admin reference (10 validation patterns: inline field errors, summary block, combined summary+inline, border+icon only, right-side indicators, helper text transforms, toast notifications, validation timing strategies, multi-field/cross-field, progressive multi-step, CSS classes reference)
  • Tabs page — complete rewrite matching Svelte pure-admin reference (card header tabs, standalone, icons, fixed width, pills, vertical, boxed, sizes, badges, centered, full width, border-top, icon-only horizontal/vertical, standalone page-level, standalone vertical, bordered horizontal/vertical, long titles with wrap/collapse/scrollable, inline tabs in header)
  • Validations page — interactive demos: char counter with JS hook, validation timing strategies (real-time/blur/submit), cross-field validation (password match, date range)
  • Badges page — complete rewrite matching Svelte pure-admin reference (badge sizes reference table, basic badges, pill badges, badges with icons, label sizes reference, labels with outline, badge groups with expand/collapse, fixed-width badges with tooltips, left-side ellipsis, composite badges with mixed colors and interactive click handlers, usage examples)
  • Lists page — complete rewrite matching Svelte pure-admin reference (basic unordered lists with spacing variants, ordered lists with numeric/roman/alpha, definition lists with standard/inline, icon lists with success/danger/info/warning, bordered/striped, inline/unstyled, complex lists with avatars, implementation guide)
  • Checkbox Lists page — new page matching Svelte reference (tri-state checkboxes, select-all pattern, disabled states, checkbox lists with descriptions, item states, list variants, task list with actions, inline/grid/multi-column layouts, interactive table with row selection and select-all)
  • Alerts page — rewrite matching Svelte reference (basic alerts with strong labels, text icon alerts, dismissible alerts, rich content with heading/list/actions, outline alerts, compact alerts in responsive grid)
  • Callouts page — rewrite matching Svelte reference (basic callouts, headings, icons, lists, sizes, code, links, grid layout, callout vs alert comparison)
  • Loaders page — rewrite matching Svelte reference (spinner sizes/colors, inline spinners, centered loaders, loaders with text, card loading states, loader types, button loading states)
  • Pagers page — new page (basic pager, first/last buttons, alignment variants, custom info text, load more with loading state, pager in card footer, CSS classes reference)
  • Tooltips page — new page matching Svelte reference (positions, color variants, theme colors, button tooltips, icon-only tooltips, multiline, inline text, popovers with sizes/alignment/positions)
  • Toasts page — new page matching Svelte reference (6 position demos, 5 variant buttons, persistent toasts, action toasts, multiple stacking toasts, long-running background task demo with server push, architecture docs)
  • Sidebar — reorganized to match Svelte pure-admin layout (Components submenu with Grid, separate Tables and Timeline submenus, Forms as top-level item)

v0.2.0

  • navbar_brand/1 — brand/logo section with optional logo image
  • navbar_nav/1 — navigation link group (position="start" or "end")
  • navbar_nav_item/1 — nav link with optional has_dropdown and :dropdown slot
  • navbar_dropdown/1 — CSS-driven dropdown menu (supports is_level2 for nesting)
  • navbar_title/1 — page title in center section
  • navbar_search/1 — search widget with keyboard shortcut hint
  • navbar_profile_btn/1 — profile button with name and :icon slot

Notifications

  • notifications/1 — bell button with badge count and dropdown panel
  • notification_item/1 — individual notification with variant, title, text, time slots

Profile panel (enhanced)

  • profile_panel/1 — full slide-out panel with overlay, avatar, name/email/role, :nav, :tabs, :footer_ slots
  • profile_nav_item/1 — navigation item within profile panel
  • toggle_profile_panel/1, close_profile_panel/1 — JS commands
  • PureAdminProfilePanel JS hook — tab switching, favorites, click-outside-to-close

Settings panel

  • settings_panel/1 — floating settings panel (theme mode, layout width, sidebar, fonts, etc.)
  • fouc_prevention_script/0 — inline script preventing flash of unstyled content
  • PureAdminSettings JS hook — client-side localStorage-based settings management

Layout

  • Added id attr to layout/1
  • toggle_notifications/1 JS command

Demo

  • Navbar uses three-section layout (start/center/end) matching pure-admin reference
  • Components dropdown with nested "More ›" submenu
  • Notifications bell with sample items
  • Profile panel with tabs (Profile/Favorites), nav items, and footer actions

v0.1.0

  • Initial release
  • Phase 1: Foundation + 10 key components (button, badge, alert, card, table, modal, tabs, form, layout, grid)
  • BEM class builder helpers
  • JS hook scaffold
  • use PureAdmin.Components for bulk import