[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--squareredesign. - v2.7.0 (
12b9d23) —pa-modal--banded,pa-gaugerebuild, 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 permanentpa-kpi-*core components (8 SCSS partials, allkpi-*classes renamed topa-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-fitcell-min grids on gauges + editorial, layout-ratio modifiers on hero + bento, composable--no-prev/--no-delta/--no-targettoggles on numeric strip,--no-deltaon 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 isstatus_text+status_variant(built-inswarn/good/neutral);:headsnippet overrides the whole head row. Sentiment variants on value and delta use the 5-step scale (very_positive/positive/neutral/negative/very_negative).variant(formerlyspark_direction) colours the sparkline viacurrentColor.is_standalonefor tiles outside apa-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 previouskpi_tile_detail/1slot scaffold.kpi_sparkline/1— opt-in convenience for the simple SVG polyline + trailing-dot pattern, onpa-kpi-tile__spark. Consumers using D3 / ApexCharts / Chart.js / Contex / etc. plug their renderer into the:chartslot 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. Mirrorskpi-detail.tsfrom svelte-pure-admin.PureAdmin.Components.KpiTerminal—kpi_terminal/1card wrapper with generic:panetab strip (each pane hasid,label_text, optionalis_active); no panes → children wrapped in a singlepa-kpi-terminal__grid--2col.:header_controlsfor custom toolbars between title and LIVE pill. The retired VALUE/Δ%/TREND view-mode toggle is replaced by the generic tab strip.PureAdmin.Components.KpiSparklineList—kpi_sparkline_list/1+kpi_sparkline_row/1.is_no_deltadrops the rightmost Δ% column;is_chart_firstrotates the L→R order 90° at narrow widths.PureAdmin.Components.KpiGaugeList—kpi_gauge_list/1+kpi_gauge/1. Default cell-min-drivenauto-fitgrid; switch viagrid_layout="2col"or"max_2".."max_6".cell_min_widthoverrides--pa-kpi-gauge-cell-min.tick_position/tick_colorknobs.PureAdmin.Components.KpiHero—kpi_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:metaslot (ordelta_text/period_text/target_textfor the canonical pattern),:chartfor the sparkline; rail is a:railslot.PureAdmin.Components.KpiBento—kpi_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_heightoverrides--pa-kpi-bento-row-height. Setis_heroon the first tile.PureAdmin.Components.KpiStrip—kpi_strip/1+kpi_strip_row/1. Composableno_previous_value/no_delta_percent/no_target_bartoggles. Header row auto-generated from visible columns; override viaheader_labels(map keyed by column atom) or suppress viano_headeror replace via:headslot.target_bar_percentdrives the bar fill (capped at 100% visually);target_percent_textis the label below (may exceed 100).PureAdmin.Components.KpiEditorial—kpi_editorial/1+kpi_editorial_tile/1. Cell-min-drivenauto-fitgrid;is_2_columnsboolean shorthand orgrid_layout="max_N"cap modifiers.target_textauto-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-detailto<body>on mount, restores ondestroyed. Auto-engaged whenever a tile / row has a popover.PureAdminKpiSparkDot— converts SVG<circle>endpoints to.pa-kpi-spark-dotCSS spans so dots stay round underpreserveAspectRatio="none".PureAdminKpiTerminalTabs— client-side tab strip wiring forpa-kpi-terminal__tab/pa-kpi-terminal__pane. Scoped per terminal so nested terminals (if any) stay isolated.
Other v2.7.0 component reworks
Modal— newis_bandedboolean. Emitspa-modal--bandedalongside the existing:variantrole modifier. Composes —<.modal variant="success" is_banded>producespa-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/1rebuild — moved the label out of the donut so__innerholds only the value text. Label now renders as a sibling row alongside__minand__maxbelow the gauge (matches v2.7.0 layout). New:sizeattr emits--pa-gauge-sizeinline (default upstream12rem). The--valuestyle 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_directionnow acceptsvery_positive/very_negativein addition topositive/negative/neutral. Internally converted to kebab-case (pa-stat__change--very-positiveetc.) 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.Staticon:danger— already exposed in theicon_variantenum (primary/secondary/success/info/warning/danger). The framework's previous omission of--dangerwas 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 vialive_state="up"/"down". Upstream migrated the internal SCSS fromrgba(...)over role colours tocolor-mix()over the 5-step sentiment scale; no API impact.btn-split— verified the wrapper doesn't emitoverflow: hiddenon.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-radius50% → 30%, shadow opacity0.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'sdemo/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:chartslot.
- Chart.js drop-in —
chart.js@4.4.3loaded via CDN indemo/lib/demo_web/components/layouts/root.html.heex. NewPureAdminKpiChartLiveView hook (demo/assets/js/hooks/kpi_chart.js, ~130 lines) renders bar / line / area charts into any<canvas data-kpi-chart>. ReadscurrentColorfrom the slot's KPI sentiment cascade, re-renders onpa: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
:sizeexamples (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 againstpureadmin.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 upstreampure-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 siblingpure-admin-themescheckout).valid_theme_dir?/2probes either layout. Newresolve_theme_file/3transparently maps the public URL/themes/{slug}/css/{slug}.cssto 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 throughdescriptionper variant so the settings panel JS can use it as a<option title>tooltip.PureAdminSettingsJS hook — manifest-driven CSS path._resolveThemeHref/1readsmanifest.colorVariants[0].fileto build the stylesheet URL (handles bothdist/andcss/)._applyThemeMode/1is now pattern-aware viamanifest.modeCssClassand clears every mode class declared by the manifest (was hardcodedlight/darkonly — important for themes declaring custom mode ids).
Tooling — Makefile + themes-install wiring
Makefile— newthemes-installtarget runsnpx @keenmate/pureadmin themes installfromdemo/(defensive: skips silently if neitherdemo/pureadmin.jsonnordemo/.pureadmin.jsonexists).dev:andsetup:now depend onthemes-install, somake devsnapshots themes from.pureadmin.jsondisk 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.
PureAdminKpiSparkDotalways wrapped the SVG in.pa-kpi-spark-wrap(which has noheightdeclaration), even when the SVG's parent was already a tight positioned anchor with explicitheight: 3rem(.pa-kpi-hero-main__chart-svg,.pa-kpi-bento-tile__chart-svg). The extra wrap broke the SVG'sheight: 100%chain; combined withpreserveAspectRatio="none"+overflow: visible, the polygon stretched across the entire viewport. Ported upstream's tight-anchor check frompure-admin/demo/js/kpi-showcases.js: skip wrapping whengetComputedStyle(parent).position !== 'static'ANDparent.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'sinner_blockslot 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'shas_inner?check (assigns.inner_block != []) treated that as "consumer provided custom popover content" → took the inner_block branch → rendered nothing → typedtitle_text+rowswere silently ignored. The visible result was the popover chrome (pa-kpi-detailbackground + 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:detailslot has content, and<.kpi_detail .../>(self-closing) form otherwise. Self-closing leavesinner_block == [], sokpi_detail/1'shas_inner?check becomes a reliable signal. Auto-built rows render correctly; consumer's custom:detailslot 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-detailexists in the DOM, and the JS hook'smounted()also hasif (!this.detail) returnas a safety net.
Added — pa:theme-change event dispatch in settings panel hook (canvas chart reactivity)
PureAdminSettingshook now dispatchespa:theme-changewindow 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_applyThemeModeafter thepa-mode-*body class flips (light↔dark toggle).{ kind: "variant", variant }— fired at the end of_applyColorVariantafter thepa-color-*body class flips (color variant picker).{ kind: "theme", themeId }— fired tied to the<link id="pa-theme-css">element'sload(orerror) 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.jsdispatches 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 separatechartColorSyncaction that listens for the linkloadindependently. - Why this matters: SVG sparklines re-colour live via
currentColor. Canvas charts (including the existingPureAdminKpiCharthook in the demo) cachegetComputedStyle(canvas).colorat 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-typevalues for thedemo/assets/js/hooks/kpi_chart.jshook:data-kpi-type="stacked-bar"— multi-series stacked bar chart.data-kpi-pointsaccepts array-of-arrays JSON ("[[120,140,160,180],[280,290,300,320],[60,70,80,90]]"); each inner array becomes a stacked series. Optionaldata-kpi-labels='["Q1","Q2","Q3","Q4"]'for x-axis labels (mostly cosmetic — axes stay hidden).data-kpi-type="doughnut"— single-series doughnut.data-kpi-pointsis a flat array of slice values; cutout fixed at62%, slice spacing2px.
SERIES_OPACITY = [0.95, 0.65, 0.42, 0.25, 0.15]— multi-series and multi-slice colours derive from the host's resolvedcurrentColorat decreasing alpha, so each series/slice reads distinctly while still inheriting the KPI sentiment + theme cascade. Both new types re-paint correctly onpa:theme-change.readPoints/1is now flatten-safe — whendata-kpi-pointsis 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'sdocs/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 realkpi_sparkline_listcarrying 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 akpi_hero_listhero_split="2_3"+ a 13-point Chart.js area chart in the hero + 3kpi_hero_siderail 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_filledto match svelte'sisFilled. Filled markers signal "just happened"; the older two stay outlined. - New
mount/3assigns:top_sales(5 products with sentiment-ordered ranking + Chart.js data series strings) andrevenue_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.
- Old placeholder "Top Sales Products" card (an
Changed — /kpi/dashboard extended with Chart.js stacked bars + doughnuts
- New section: "Revenue by Segment · Q1–Q4 weekly" —
kpi_sparkline_listwith 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_bentomixing 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 isneutral(grey), the rest arepositive/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-changeevent dispatch by re-colouring both inline-SVG sparklines (viacurrentColor) and Chart.js canvases (via_recolor()) on mode flip / theme swap, and (c) the new stacked-bar + doughnut chart types via theSERIES_OPACITYcascade.
Docs / demo / bookkeeping (catch-up pass)
docs/theming.mdrewritten — 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:rootdefaults architectural fix.pa-stat--squareprefix-currency mode wired up. Newis_prefix_symbolboolean onstat/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/statsgained a "Square stats — mixed units · v2.6.0" card showing all four cases side-by-side.component-audit.mdre-stamped tod49531c(v2.8.0). Seven existing rows re-anchored to12b9d23(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— declaredpeerDependencies: { "@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" butpackage.jsonwas 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 thepa-alert__contentwrapper when no:iconslot 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-alertdirectly. Wrapping them in__content"for consistency" moved them out of the> p/> hrselector reach, which broke the new layout. The wrapper is still emitted whenever an:iconslot 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— newheading_sizeattr (nil\|"lg"). v2.5.0 unifiedpa-alert__headingto default to the body font-size + semibold weight.heading_size="lg"adds thepa-alert__heading--lgmodifier for the louder, deliberate-read presentation (blocking errors, system updates, quota warnings). Existing alerts that used<:heading>orheading_textwill render visually smaller than before — passheading_size="lg"to preserve the previous appearance.Alert— newis_multilineattr. Addspa-alert--multilineto opt back toalign-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/--lgsize scale, and the centred default alignment as soon as the consumer upgrades@keenmate/pure-admin-coreto ^2.5.0.component-audit.md— alert row re-stamped to2ef8034(2026-04-25, v2.5.0). Other components unchanged since v2.5.0 only touched_alerts.scssand 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 initialpa-popconfirm--{bottom|top|start|end}class (previously onlydata-placementwas set, so CSS rules keyed on the class rendered inconsistently before Floating UI ran). The client-side position helper inevents/popconfirm.jsnow strips the logicalstart|endclass pair on flip (was looking for physicalleft|rightthat never appeared) and maps Floating UI's physicalresult.placementback to our logical class via aphysicalToLogical()helper — RTL collision-flipped popconfirms now render correctly.Popover— title in.pa-popover__headernow 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__headingnow renders as<h4>(was<div>) to match the snippet; picks up the shared heading margin reset instead of needing override rules.Card— the:toolsslot now emits<div class="pa-card__actions">(waspa-card__tools, which has no CSS backing). Slot name kept for API stability.Loader/spinner—sizeattr 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 frompa-btn pa-btn--primary pa-btn--icon-only pa-btn--smtopa-btn pa-btn--sm pa-btn--icon-only pa-btn--secondaryfor the default modal and… pa-btn--lightfor themed modals (variant/header_variantset), matching the snippet's "secondary on neutral header / light on coloured header strip" pattern.PureAdminDetailPanelJS hook — full rewrite to match the contract documented indetail-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-widthon<html>(was inlinestyle.widthon 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 uppa-detail-panel-resizingduring drag to suppress text selection; handle picks uppa-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/__timelogic is muddled andpa-timeline--single-columnisn't exposed.
Security
Pager— droppedPhoenix.HTML.raw/1on theicon_*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_iconslots. Breaking for apps passing HTML in the string attrs; migrate those to the slots.PureAdminFlashhook — validate URL scheme in markdown links (audit finding #2, High). Markdown links of the form[text](url)previously let any URL — includingjavascript:alert(1)— flow intohref=. URLs starting withjavascript:,data:, orvbscript:(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), andfield/1(copy buttons) previously rendered inline event handlers and embedded<script>blocks that required'unsafe-inline'on CSPscript-src. Behaviour moved into a single delegated-events module (lib/assets/js/events/) and exposed via newinitPureAdminEvents()export — components now emitdata-pa-*attributes and the module's document-level click listeners dispatch on them. Apps can now run withscript-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 existinginitModalDialogs()call inapp.js. No template changes required for library components.
- Migration: add
desc_table—label_widthvalidated as a CSS length (audit finding #4). The attr is now interpolated intostyle=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_toastactions 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_escapeAttrhelper from both hooks. PureAdminProfilePanel— URL scheme check before navigation (audit finding #6). Favourite items previously assigneddataset.hrefdirectly towindow.location.href.javascript:/data:/vbscript:/file:URIs are now rejected; everything else (http/https,mailto:,tel:,sms:, deep-link schemes likeslack://,intent://,myapp://, relative paths) passes through.PureAdmin.Helpers.safe_url/1(audit finding #7). New helper that returnsurlif 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 tohref={@href}inpa_link/1,button/1,navbar_nav_item/1,sidebar_item/1, andprofile_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 aconsole.warninstead of throwing into the caller.- Settings / sidebar resize — localStorage inputs validated (audit finding #9).
font-sizeandcontainer-widthsettings 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-suppliedrender(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 viainnerHTML.
Demo
- Swap the Stored Submissions card for
<.table_card is_scrollable>on the/phoenix/form-demopage so narrow viewports scroll the table horizontally inside the card instead of clipping the rightmost columns.
[1.1.0] - 2026-04-23 [PUBLISHED]
Added
:fieldattr on form components —input/1,textarea/1,select/1,checkbox/1,radio/1, andform_group/1now acceptfield={@form[:x]}for one-line Phoenix form binding. Derivesname,id,value(orchecked), and error state from thePhoenix.HTML.FormFieldstruct; explicit attrs still win.used_input?/1is respected so unsubmitted fields don't show stale errors.- Auto-rendered field errors — when
field=is set and the field has errors,input/textarea/selectautomatically render aform_helpbelow themselves in the error variant. Opt out withshow_errors={false}.form_groupflips tovalidation="error"in the same condition. PureAdmin.Components.Form.translate_error/1— default%{key}-interpolating error formatter, overridable viaconfig :keen_pure_admin, :error_formatter(MFA tuple or 1-arity function) for Gettext-aware apps.PureAdmin.DateTime— new top-level helper withformat/2(styles::short_date,:long_date,:full_date,:time,:long_time,:short_date_time,:long_date_time,:relative, or any raw strftime pattern) andrelative/2(buckets time diffs intonow/ seconds / minutes / hours / yesterday / days / weeks / months / years, past and future; acceptsnow:for deterministic tests). AcceptsDate,NaiveDateTime, andDateTime. Month names, weekday names, and relative phrases all flow throughPureAdmin.Translations.t/2.- Translation keys — 47 new keys under
pureAdmin.datetime.*(connectors, relative phrases past/future, 12 month names, 7 weekday names). push_flash/5gainedreplace: 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.PureAdminFlashhook — honors both thereplacepayload flag and a newpa:flash-clearevent.
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 replacement —
use PureAdmin.Componentsreplaces the Phoenix-generatedCoreComponentsmodule PureAdmin.Config— centralized app configuration (:app_name,:app_logo,:app_version,:copyright,:font_class). Components likenavbar_brand/1andfooter/1read from it automaticallypackage.json— enablesimport "keen_pure_admin"in esbuild for hex dependentspureadmin createtemplate — 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/3helper - 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_classinconfig.exsand components read from it automaticallynavbar_brand/1— falls back to config:app_nameand:app_logowhen no inner content providedfooter/1— falls back to config:copyright(start slot) and:app_version(end slot) when no slots providedPureAdmin.Config.root_html_attrs/0— returns%{class: font_class}for the<html>element, supportspa-font-responsiveand granularpa-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.csslink; theme CSS already includes the core framework - Dockerfile — switched theme download from broken
curl+ zip API tonpx @keenmate/pureadmin themes --dirCLI, added CSS copy step forapp.css
Bug Fixes
- Sidebar — fix mobile toggle not working:
toggle_sidebar()was hardcoded to dispatch to#sidebar, now accepts configurable target ID vianavbar_burgertargetattr - Logger —
enableLogging()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 topriv/staticand 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 flash —
flash/1andflash_group/1as drop-in replacements for CoreComponents, styled withpa-alertBEM classes. Works with Phoenix's built-input_flash/3 - Independent flash containers —
flash_container/1+push_flash/5for multiple independent flash groups on the same page. Each container receives messages independently via a JS hook
- Standard flash —
PureAdminFlashJS hook — client-side rendering of flash alerts. Supports markdown body (bold, italic,[links](url), lists,---horizontal rules), action buttons withpushEventcallbacks, auto-dismiss, and dismissible close button- Markdown body — flash message text supports basic markdown rendered as proper
pa-alert__contentHTML - 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 buttons —
push_toast/5accepts:actionsoption with%{label, event, params, variant, dismiss}maps. JS hook renderspa-toast__actionswithpa-btn--xsbuttons insidepa-toast__content. Clicking an action firespushEventback to the server, then auto-dismisses - Toast progress bar —
:progressoption renderspa-toast__progressbar that animates from 100% to 0% over the duration.:progress_coloroption overrides the bar color via inline style - Filled toasts via push_toast —
:filledoption renderspa-toast--filled-{variant}class max_width— custom max-width per toast (e.g.max_width: "50rem")- Width ratchet — container
min-widthtracks 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 withsteps, each withprompt,placeholder, and optionalfree_text. Steps progress sequentially with selections displayed as locked tokens. Commands complete viahandle_info({:command_complete, cmd_id, selections}) - Search contexts (
:prefix) — register scoped search contexts withshortcutandaliases. Typing:p laptopsearches products for "laptop" - Global search — typing without a prefix searches across all data
- 6 modes —
idle,command_list,command_step,context_list,context_search,global_searchwith 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.) replacingcommand_palette_prefixcp:reset_inputpush_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 styles —
display="inline"(default, Svelte-style: full sentence in input with command badge) anddisplay="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 positioningpa-command-palette__token-prompt— step prompt text between token badges (replaces__token--prompt)- Standard
pa-badge— item badges now usepa-badgeinstead of custompa-command-palette__item-badge - Token badges — step tokens in tokens mode use
pa-badge pa-badge--primaryfor command name, plainpa-badgefor values - Tokens
&:emptyhiding — 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
- Hotkeys —
Alt+DDeploy,Alt+AAssign,Alt+GGo to Page,Alt+TSwitch 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 —
/gopage options have numeric codes (e.g.,24for Alerts).filter_optionsmatches on label, description, and exact code pa-command-palette__home— home screen container with__home-sectionseparators and__home-headinglabelspa-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 underpureAdmin.*flat keys. Apps override via config:config :keen_pure_admin, translate: &MyApp.translate/2t(key, params)— main translation function with%{param}interpolation. Falls back to English when callback returns nil or isn't configuredinterpolate(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) fortop-*placements, down forbottom-*split_button/1primary icon — newiconattr for Font Awesome icon on the primary button (e.g.icon="fas fa-download")split_button/1item icons —:itemslot now acceptsiconattr (e.g.icon="fas fa-file") rendering aspa-btn-split__item-iconsplit_button/1inline action buttons —:itemslot acceptsaction_icon,action_event,action_value,action_variantattrs for inline action buttons beside menu items (e.g. delete/remove). The JS hook forwards clicks viapushEventsince the menu is moved todocument.bodybutton/1label wrapping — button text is wrapped in<span class="pa-btn__label">when an icon is present, enabling proper centering withalign="center"split_button/1menu structure — usespa-btn-split__menu-innerwrapper andpa-btn-split__item-rowBEM elements (replaces inline styles), matching pure-admin 2.3.2 two-container patterninput_group/1—:buttonslot now documented to useclass="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
@flashcompatibility - 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
ThemePlugauto-refresh — cached themes are validated against pureadmin.io usingcontent_shafrom the theme'schecksumsfield. 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 thememake 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,publishworkflow - Theme customization via SCSS — variable overrides, custom fonts, baseline correction, complete example
make help— all Makefile targets now have## descriptioncomments
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
CoreComponentsreplacement --use PureAdmin.Componentsgives you everything - Full BEM class support with
build_classes/3helper - 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/manifestsand dynamically populates theme selector (sorted alphabetically), replacing hardcodedthemesprop - Color variants — new
data-section="color-variant"section, shown/hidden based on theme manifest. Appliespa-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
themesattr fromsettings_panel/1— themes are now loaded dynamically, onlydefault_themeattr remains
On-Demand Theme Downloads
- ThemePlug — new Plug that serves
/themes/:name.csswith on-demand downloading from pureadmin.io. When a theme CSS is requested that isn't bundled at build time, it downloads frompureadmin.io/api/themes/:name/download, extracts CSS andtheme.jsonmanifest from the zip, and caches to disk /api/themes/manifests— returns all available theme manifests as JSON (from both build-timepriv/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
:httpcand:zipfor 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 withelixir:1.18-slimbuilder anddebian:trixie-slimruntime. Downloads theme bundles from pureadmin.io at build time (CSS + manifests). Configurable viaTHEMES_URLbuild arg - Makefile — added
podman-build,podman-run,podman-stop,podman-restart,podman-logs,podman-clean,podman-deploy,podman-pushtargets matching pure-admin conventions. Registry:registry.km8.es - .dockerignore — excludes
_build/,deps/,node_modules/,.git/ force_ssl— now opt-in viaFORCE_SSL=truebuild-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 viadata-event,data-has-more,data-throttle,data-root-margin. Throttled to prevent rapid-fire triggers.
Components
list_item/1— added:metaslot for rich meta content (badges, icons) alongside existingmeta_textstring attrtimeline/1— fixed alternating variant to use<div>container and correct BEM classes (pa-timeline__date+pa-timeline__iconinstead ofpa-timeline__time+pa-timeline__marker). Addedalignattr ("start","end") andis_keep_layoutfor alternating layout control — replaces rawclass="pa-timeline--start"usagetimeline_item/1— auto-detects layout from props: block/alternating pattern whenicon_textor:iconis provided, simple pattern otherwisecard/1— addedis_borderedattr for bordered card styling — replaces rawclass="pa-card--bordered"usagebutton_group/1— addedresponsiveattr ("sm-vertical","md-horizontal", etc.) for responsive direction changes at breakpoints — replaces rawclass="pa-btn-group--md-vertical"usagegrid/1— addedalign="stretch"to allowed valuescolumn/1— addedis_no_padding,is_grow,is_shrinkprops for flex layout controlvalues:validation — added compile-time value validation to allvariant,color,theme_color, andlevelattrs across alert, badge, label, composite_badge, button, popconfirm, data_display, form, table, and typography components. Typos likevariant="outine-danger"orcolor="10"now produce compile warnings- pure-admin 2.2.0 support — updated CSS to v2.2.0. Added
theme_colorattr tobutton/1,callout/1, andtoast/1for theme color slots 1-9. Addedis_filledattr totoast/1for full-color background toasts. Alerttheme_colornow uses properpa-alert--color-{N}/pa-alert--outline-color-{N}classes (waspa-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
PureAdminInfiniteScrollhook, 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
maketargets - 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).:cellslot withis_changed,is_solid,is_conflictmodifiers.comparison_value/1includes 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/1with{:closest, ".pa-sidebar__item"}and ID-targeted<ul> - sidebar_submenu: Add
idattr for stable submenu identification,phx-hook="PureAdminSidebarSubmenu"for localStorage persistence of open/closed state across navigations - sidebar_submenu: Add FOAC prevention —
fouc_prevention_scriptinjects<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_clickandactionattrs to:itemslot for LiveView event handling via hookpushEvent
JS
- clipboard: Global
kpa:clipboard-copyevent listener — copy to clipboard viaJS.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 usingtable_card - Restructure routes to hierarchical paths (
/components/buttons,/tables/standard, etc.) — enablesString.starts_with?for sidebar submenuis_openderivation from URL - Add global toast service —
<.toast_container>in app layout, PubSub-basedhandle_infohook inon_mountfor 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 viaPureAdminSplitButtonJS hook.:itemslot withis_dangermodifier,placementattr for Floating UI positioning,on_clickfor primary action - tooltip: Add
is_keywordmodifier — 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
:footslot for<tfoot>support (colspan, totals rows) - table: Add
alignattr on:colslot (start/center/end) for per-column text alignment - table: Add
is_responsive_gridmodifier for CSS Grid responsive collapse - table: Render
:actioncolumn first (leftmost) to match pure-admin reference - table_container: Add
is_panelmode withtitle_text,:header,:actionsslots - 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_lastattrs for custom icon sets - pager: Change info format to
/ N pages(wasPage ... of N) - data_display: Add
is_value_endandis_value_centermodifiers tobanded/1anddesc_table/1 - form: Add
input_wrapper/1— wraps input/select with optional clear (×) button (pa-input-wrapper+pa-input-wrapper__clear),has_clear,on_clearattrs - filter_card: New
filter_card/1component — expandable filter card with:filters,:advanced_filters,:actionsslots, toggle/clear/refresh/apply buttons,is_expanded,is_loading,is_disabledstates, matching SvelteFilterCard
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-subtitleCSS 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, withmessage,placement(top/bottom/start/end, RTL-aware),icon_variant(danger/warning/info),is_compact,confirm_event/confirm_valuefor LiveView integration, click-outside-to-close, move-to-body positioning
RTL Support
tooltip/1— position values renamed:right→end,left→start(RTL-aware viadocument.dir)popover/1— placement values renamed:right→end,left→start(RTL-aware viadocument.dir)- Tooltip JS hook — added
resolveLogicalPlacement()that mapsstart/endto physicalleft/rightbased on document direction
Components
badge/1— replacedwidthattr (pa-badge--w-Nxclasses) withmax_widthattr usingmaxwr-N text-truncateutility classesmodal/1— fixed popover alignment classes (pa-popover--center,pa-popover--end) to be copied to content element when moved todocument.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 viainitModalDialogs()
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/endposition naming - Badges page — updated fixed-width section to use
max_widthutility 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
ifparentheses,dynamic_tagname deprecation, undefined attributes, missing slots)
v0.3.0
Compatible with @keenmate/pure-admin-core v2.0.2.
Components
section/1— addedtitle_textattr that renders an<h3 class="pa-section-title">headingcode/1— fixed to render plain<code>withoutpa-codeclass, matching Svelte referencecard/1— added:subtitleslot (rich HTML counterpart tosubtitle_text)card/1— fixedsubtitle_textto render withpa-text pa-text--secondaryclass matching Svelte referencecard/1— fixed title rendering: plain<h3>without wrapper div when no icon is present, matching Svelte referencecard/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 orderform_label/1— addedis_requiredattr that renders asterisk indicatorcheckbox/1— added:label_contentslot,is_indeterminate(via PureAdminCheckbox hook),is_x_markcheckbox_box/1— addedis_indeterminatesupporttabs/1— scrollable overflow now renders proper scroll buttons and scroll containertab_item/1— added deterministicidand:not()exclusion to prevent 2px flash on tab switchswitch_tab/3— scoped tab/panel switching viatabs_id+ content container id to prevent cross-group interferencelabel/1— fixed outline to usepa-label--outlineclass (notpa-label--outline-{variant}), matching Svelte reference; addedxs/xlsize supportbadge_group/1— addedlimit,total,is_expanded,on_toggle,more_text,collapse_textattrs 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— addedwidthattr (pa-badge--w-{size}BEM class) andis_ellipsis_startfor left-side truncationcomposite_badge/1— addedis_interactive,on_label_click,on_button_click,label_variant,button_variant,button_text,:icon_contentslot for full interactive support with separate label/button click eventscheckbox_box/1— new low-level checkbox (input + box) for tables and composite componentscheckbox_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:actionsslotbasic_list/1— new component for styled<ul>with spacing, icon, bordered, striped, inline, unstyled variantsordered_list/1— new component for styled<ol>with numeric, roman, alpha stylesdefinition_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 viadata-msg/data-msg-overwith{count}/{max}placeholdersPureAdminCheckbox— new hook for syncingindeterminateproperty fromdata-indeterminateattribute (required for tri-state checkboxes)
Components (enhanced)
tooltip/1— new CSS-only tooltip wrapper with position, variant, multiline, help cursor supportpopover/1— new click-triggered popover with title, placement, size, alignment, custom trigger slotpager/1— enhanced with page input, first/last buttons, configurable events, custom info text,:controlsand:infoslotsload_more/1— enhanced withphx-clicksupport via:globalattrsloader_center/1— new centered loader container (flexbox centering)loader_overlay/1— enhanced to accept custom content via slot (not just default spinner)toast/1— addedtitle_text,message_text,is_visible,on_close,:iconslot, close button with SVG icontoast_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 subcomponents
navbar_brand/1— brand/logo section with optionallogoimagenavbar_nav/1— navigation link group (position="start"or"end")navbar_nav_item/1— nav link with optionalhas_dropdownand:dropdownslotnavbar_dropdown/1— CSS-driven dropdown menu (supportsis_level2for nesting)navbar_title/1— page title in center sectionnavbar_search/1— search widget with keyboard shortcut hintnavbar_profile_btn/1— profile button with name and:iconslot
Notifications
notifications/1— bell button with badge count and dropdown panelnotification_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_slotsprofile_nav_item/1— navigation item within profile paneltoggle_profile_panel/1,close_profile_panel/1— JS commandsPureAdminProfilePanelJS 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 contentPureAdminSettingsJS hook — client-side localStorage-based settings management
Layout
- Added
idattr tolayout/1 toggle_notifications/1JS 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.Componentsfor bulk import