NewYour coding agent can read the release notes before it upgrades.Set up the MCP server →
pub.dev
A Jaspr web framework port of the Arcane UI component library. Build beautiful web applications with Dart using familiar Arcane patterns.
Last release 14 days ago
23 Sep 2026
Release timing varies
gaps range from 1 weeks to 3 months
Some releases are documented
notes for 5 of 16 stable releases
Nothing withdrawn
no release was ever pulled
9 months old
16 releases · first in 2025
One column per month.
Slider onChanged and onRangeChanged callbacks receive the values the interaction runtime reports through its arcane:slider-change event. The callbacks
onChanged and onRangeChanged callbacks receive the values the
interaction runtime reports through its arcane:slider-change event. The
callbacks were never bound, so hydrated sliders moved without notifying the
application in any theme.Flutter-style State.widget and didUpdateWidget, with tests for updates, keyed replacement, setState, and disposal.
State.widget and didUpdateWidget, with tests for updates,
keyed replacement, setState, and disposal.Flex, Flexible, FlexFit, numeric Positioned constraints, and optional
children for empty layouts and SizedBox.expand/SizedBox.shrink.TextStyle, Text.data, textAlign, and softWrap for typed text authoring.IconButton.tooltip and semanticLabel provide native titles and accessible names.Row and Column use spacing. Column centers its cross axis
by default. Existing package and documentation layouts now specify their intended alignment.Wrap uses alignment, runAlignment, spacing, runSpacing,
and WrapCrossAlignment, with zero spacing by default and support for both axes.Text.style accepts TextStyle. Literal CSS uses cssStyle;
align becomes textAlign, and the text value is available as data.onChanged. Text input submission uses onSubmitted.DropdownMenuRenderBase action items route their background and text colour
through --arcane-menu-item-background / --arcane-menu-item-foreground
and mark destructive items with data-variant="destructive", so every
theme's stylesheet can style item states.onClose.onChanged.SizedBox axis.
Flex main-axis sizing now has the same behavior across themes.hidden, mirrors
data-state/aria-expanded, switches menus on hover while open, toggles
submenus from click and keyboard, and closes on outside click and Escape.onChange/onInput callbacks, select onSelect aliases,
and checkbox/switch onToggle aliases. Update callers directly.Win95: the stock bitmap cursor set, as CSS tokens. tool/bundle_win95_cursors.dart authors the classic cursors as ASCII pixel maps and encodes them int
tool/bundle_win95_cursors.dart authors the classic cursors as ASCII pixel maps and encodes them into packages/arcane_jaspr_win95/lib/src/win95_cursor_assets.dart as 1x RGBA PNG data URIs; the theme exposes them with their original hotspots and keyword fallbacks as --w95-cursor-arrow (0,0), --w95-cursor-wait, --w95-cursor-ibeam, --w95-cursor-crosshair (IDC_CROSS), --w95-cursor-no (IDC_NO, the circle-slash), --w95-cursor-move (SIZEALL), and --w95-cursor-ns / -ew / -nwse / -nesw. Windows 95 had no hand/pointer and no open/closed "grab" hand at all — those are IE and NeXT idioms — so every drag surface the theme owns (gallery captions, drag handles, the dragging state, the carousel track, the slider thumb) now resolves to the pixel-art arrow, edit wells take the I-beam, splitter grips take the matching double-arrow, and [aria-busy="true"] takes the hourglass. Host apps reference the same tokens instead of naming a modern keyword.#arcane-root.arcane-theme-win95 sets cursor: var(--w95-cursor-arrow), so the desktop, the maximized window, its caption and its entire client area inherit the bitmap arrow in one declaration — no universal selector, so the genuine exceptions still override it. The exceptions are the I-beam over every edit control (including read-only ones, whose text stayed selectable), the hourglass over [aria-busy], .arcane-loading, and a new #arcane-root[data-busy="true"] app-wide hook, the resize double-arrows over splitter grips, and IDC_CROSS over .arcane-map-picking. Buttons, labels, <summary>, and the non-text inputs name the arrow explicitly, because the browser's own stylesheet sets cursor: default on them and that outranks inheritance.--w95-cursor-hand, the one non-shell cursor. Internet Explorer's link hand, drawn as pixel art to match the rest of the set and scoped to actual hypertext — unadorned a[href], prose/doc links, and a.win95-breadcrumb-link[href]. Anchors dressed as buttons, cards, tiles, or navigation chrome keep the arrow, as does the breadcrumb's sibling <button>..arcane-gallery-drag-outline, mix-blend-mode: difference over a 4px white frame — the XOR look at SM_CXFRAME thickness) tracks the pointer while the tile itself stays put, and the move commits in one repaint on release. Escape or a cancelled pointer simply discards the wireframe. The threshold drops to Win95's SM_CXDRAG/SM_CYDRAG (4px, compared per axis instead of as a radius). There is one outline at a time, it is not animated, and it leaves no trail.data-w95-active), and the theme paints inactive captions in the Windows Standard scheme's COLOR_INACTIVECAPTION / COLOR_INACTIVECAPTIONTEXT pair (solid #808080 face, #c0c0c0 text). Tiles with no attribute stay active, so an untouched gallery is unchanged.tool/bundle_win95_loaders.dart now bundles the 2x and 3x sheets alongside the 1x for all three palettes (win95Loader<Palette>DataUri2x / 3x), and .arcane-loader selects between them with image-set() so a retina display gets the sheet drawn at its own device resolution instead of a bilinear upscale of the 26x26 art. The image-set() is written with literal URLs rather than through --w95-loader-image, so an engine without it simply keeps the 1x declaration above; --w95-loader-image itself is unchanged and still resolves to the 1x URI for host apps.border-radius in one rule; it now does the same for motion — #arcane-root.arcane-theme-win95 *, *::before, *::after { transition: none !important; animation: none !important; }. Windows 95 interpolated nothing, and the ~32 scattered per-component transition: none declarations could never reach the dozens of inline transition/animation styles written by core render bases and the interactivity scripts, because an inline declaration outranks any stylesheet rule that is not !important. Sidebar width slides, select and accordion hovers, prose and TOC easing, the mega-menu fade, the CTA-card entrance, the modal scale, the avatar pulse and the carousel marquee are all resolved by that single rule. Two things survive it deliberately: the hourglass is an animated background-image, not a CSS animation, and transform is untouched because tooltips and popovers use it for positioning. Anything a keyframe was making visible is pinned opaque alongside — .win95-cta-card emits opacity: 0 inline and is now pinned to 1.--w95-check, the checkbox tick as drawn geometry. The 7x7 Win95 mark — a two-pixel stroke descending right to the vertex, a second climbing right to the tip — authored one pixel per path run and painted through a mask so it follows --w95-field-text. It replaces content: '✔' (U+2714), which rendered at a different weight and vertical position in every font in the fallback stack; the Win95 checkbox renderer no longer emits its literal x text child either, so the box holds exactly one mark.Themeable navigation dropdown panels. ArcaneNavDropdown now exposes the
stable arcane-nav-dropdown-panel class and a scoped background token so
themes can style its transient panel without changing content surfaces.
Select popover contract. Shared select options now expose listbox option semantics and selected state, while triggers report their current expanded state instead of a fixed value.
Bounded theme geometry and elevation. Radius authoring now ends at the
8px md tier, renderer references use that tier directly, and palette
shadows are fixed neutral structural presets rather than injectable strings.
Runtime DOM safety. Calendar templates no longer expose literal control markup in the global script, and carousel clones recursively neutralize focusable descendants, remove duplicate IDs, and remain inert.
Accessible infinite carousel. The repeated track copy is now inert and hidden from assistive technology; auto-scroll pauses for keyboard focus and is disabled when reduced motion is requested. Command search inputs also expose an accessible name.
Bounded style overrides. ArcaneStyleData.backgroundCustom now accepts
flat colors and tokens and rejects literal gradient functions. Nested semantic
surfaces flatten through a shared data-arcane-surface contract across card
families instead of relying on theme-specific class pairs.
Visible-content hard break. CTA cards no longer delay or begin hidden, and FlexiCards no longer hide long text until hover.
Single-icon control hard break. Button now exposes one semantic icon
slot with a typed ButtonIconPosition; the arbitrary child, independent
trailing, and manufactured showArrow paths were removed. FeatureCard
likewise keeps one optional semantic icon and no longer manufactures a CTA
arrow.
Typed semantic-glyph hard break. Buttons, badges, cards, menus,
navigation, selectors, dialogs, alerts, and other one-icon component slots
now accept only the sealed ArcaneGlyph type. ArcaneIcon.customSvg keeps
multi-path brand artwork inside one SVG glyph without reopening a Widget
composition escape.
Executable design-language policy. The documentation browser audit now exercises static and interactive states at 375px, 768px, and 1440px and mutation-tests every banned family. Source and render contracts reject visible nested semantic surfaces and reader-facing AI citation or tracking leaks.
Local-font hard break. ArcaneStylesheet.externalCssUrls and ArcaneApp's
remote stylesheet injection were removed. Core, ShadCN, Neon, and
Neubrutalism now name only the committed Akzidenz Grotesk Pro, ITC Avant
Garde, and Hack site assets.
Design-language hard break. Core marketing surfaces now use flat, restrained geometry; the purpose-built glass, icon-backplate, chip/tag scripts, chip-radio, compact promo/card/pricing badges, FancyIcon, accent-button alias, text-glow, promo-pill, glow-theme, gradient-decoration, pin-glow, hover-lift, backdrop-blur, and animated/default-icon badge APIs were removed, with contract tests preventing their return.
Win95: the timing tokens are zeroed at their source. The theme root now declares --transition-fast/-/-slow/-slower: 0s and the four --arcane-transition-* aliases alongside --radius: 0. The aliases have to be named explicitly: a custom property substitutes var() on the element it is declared on, so --arcane-transition: var(--transition) had already resolved to 150ms ease at :root and inherits that computed value regardless of what the theme scope does to --transition. The theme's button arrow, menubar item, dropdown item, form buttons, slot counter, and flexi-cards now emit no interpolation, and Win95FlexiCards ignores transitionDuration while keeping the CSS-Grid expansion as a layout mechanism.
Win95: the scripted tooltip is the classic info box. TooltipScripts builds .arcane-tooltip — a different element from the .win95-tooltip the theme already styled — with an inline 8px radius, a blurred shadow, a dark surface colour and a 150ms cross-fade, plus an 8x8 box rotated 45 degrees as a callout tail. Under the theme it is now #ffffe1 with a 1px flat black border, no shadow, no radius and no fade, and .arcane-tooltip-arrow is hidden: a Win95 tooltip was a plain rectangle with no tail. Its inline transform: translateX(-50%) is centring, not motion, and is left alone.
Win95: caption buttons are drawn geometry at the right size. The live close buttons on dialogs, drawers and sheets were 18x18 (22x18 for dialogs) with a thin bevel and the render base's U+2715 rendered as a font character, while the decorative captions elsewhere in the sheet already did it correctly. All three are now 16x14 raised control faces carrying the --w95-ctl-close mask, pressed to --w95-pressed with a 1px down-right glyph nudge. font-size: 0 collapses the text node no theme can remove, so the mark can no longer change weight or baseline with the font fallback.
Win95: the engraved disabled label, everywhere. DrawState/DSS_DISABLED drew every greyed caption twice — the text in COLOR_GRAYTEXT over a COLOR_BTNHILIGHT copy offset one pixel down-right. Only buttons, cycle buttons and toggle buttons had it; calendar days, date/time triggers, select options, pagination, menu, dropdown and menubar items, disabled selects and textareas set flat grey. They now share one color: var(--w95-shadow); text-shadow: 1px 1px 0 var(--w95-hilite) recipe, tokenized so the dark scheme inverts it.
Win95: focus rectangles land on the caption. The checkbox render base puts the tabindex on the 15px check well, so the single global outline-offset: -3px rule drew a ~9px dotted square inside the well itself. The well's own rect is now suppressed when there is a caption block to move it to (and only then, so an unlabelled checkbox keeps a focus mark), and the rect is drawn around the caption instead. Standard radios get the rect around the whole <label> rather than inset through their circle, and tabs pin the inset explicitly so the active tab's asymmetric bevel cannot clip it.
Win95: elevation is a bevel, not a glow. win95DecorationStyles mapped Elevation.md and up onto 0 0 18px/0 0 36px coloured halos — the neon theme's idiom, pasted in — so any component with ArcaneDecoration(elevation: …) wore a soft neon glow on a silver panel. The scale now resolves to --w95-raised-thin (xs/sm) and --w95-raised (md and up), and shadowColor is ignored because Win95 had no shadow to tint.
Win95: one inactive-caption colour, not two. --w95-title-inactive-b existed only so a gradient could be built across the pair, which is the same Windows 98 assumption the caption gradient was; it is deleted, and the surviving pair is --w95-title-inactive-a plus a new --w95-title-inactive-text (COLOR_INACTIVECAPTION / COLOR_INACTIVECAPTIONTEXT), which the inactive gallery caption now reads instead of a literal.
Win95 title bars are solid, not gradients. --w95-title-bar now resolves to var(--w95-title-a) alone — the navy-to-cyan caption gradient is a Windows 98 feature — and every caption in the theme (gallery tiles, dialogs, drawers, sheets, the knowledge-base topbar and landing mock) draws from that one token. --w95-title-a-in / --w95-title-b-in still re-tint captions at runtime, and a host that wants the 98 look redeclares --w95-title-bar as a gradient across the two.
Core drag cursors resolve through a theme seam. The gallery drag runtime, InfiniteCarousel's track, and the carousel dragging state now emit cursor: var(--arcane-drag-cursor, grab) / var(--arcane-drag-cursor-active, grabbing) instead of the bare keywords, so a theme can re-point them wholesale. Unset — which is every theme but Win95 — they resolve to the same grab/grabbing pair as before.
Win95 no longer shows the modern hand, circle-slash, or grab cursors. All 44 cursor: pointer / not-allowed / default keywords in the theme sheet were replaced with var(--w95-cursor-arrow): buttons, cards, menu and command items, tabs, the Start button, calendar cells, date/time pickers, combobox triggers, disclosure summaries, breadcrumbs, drawer and sheet closes, toasts, and every disabled state. A greyed Win95 control showed the plain arrow — its engraved label was the affordance — and IDC_NO was reserved for a refused drag-drop, which is what --w95-cursor-no is now exposed for. The theme also reclaims the three unscoped core rules that hard-code the hand (.code-copy-button, .sidebar-theme-toggle, .sidebar-summary).
Win95 renderers no longer decide cursors inline. Inline pointer cursors across form and flexi-card renderers were removed so the theme sheet is the single place a cursor is chosen; the splitter handle emits var(--w95-cursor-ew) / -ns instead of col-resize / row-resize.
Two more core inline-cursor seams are themeable. The number-input spinner script writes var(--arcane-step-cursor[-disabled], …) instead of hard-coding pointer/not-allowed, and the resizable splitter resolves --arcane-resize-cursor-ew / -ns from the dragged container before parking the value on <body> (which sits outside the themed root, so var() cannot reach it there). The map coordinate-picking script toggles an .arcane-map-picking class rather than writing an inline crosshair. Every seam falls back to the previous keyword when a theme leaves it unset, so shadcn, neon, and neubrutalism are unchanged.
Win95 progress meters read the Win95 way. The trough is now the silver control face (--w95-face) inside its sunken bevel rather than the white edit-field colour, so the gutters between the navy blocks read #c0c0c0 the way every Win95 copy/install dialog did. The blocks themselves changed from a phase-drifting repeating-linear-gradient (10px block, 2px gap, gaps punched to transparent) to a fixed 16px block on an 18px stride anchored to the left inner edge, so the segment pitch no longer shifts with the control's width.
Win95 has no marquee progress mode. An indeterminate ArcaneProgressBar under the theme now renders the hourglass where the meter would be and drops the trough entirely, instead of the static, permanently-full navy bar it used to paint. Win95 had no indeterminate meter at all — that arrived with the XP-era common controls — and its answer for work of unknown length was the hourglass.
Win95 circular progress shows its value. Win95CircularProgress.ringStyles now emits the swept angle as --w95-gauge-pct, and the theme draws the ring as a hard-stop conic gauge (navy on the silver face, masked into a chunky ring so the numeric readout stays legible in the hole) inside the existing sunken frame, with no easing. Win95 shipped no circular control, so this is the closest period-plausible reading of one; previously the ring was a static bezel and 5% and 95% rendered identically.
Win95 loaders snap to the pixel grid. The hourglass is a 26x26 bitmap and callers ask for 16px, 20px, 24px and 40px — every one a fractional scale that shears rows out of the art rather than crisping it. Under the theme --arcane-loader-size is now snapped DOWN to the nearest whole multiple of 26 with 26px as the floor (max(26px, round(down, …, 26px)), with a plain 26px pair ahead of it for engines without round()), so loading buttons, toasts, selects and auth guards all draw integer-scaled frames. image-rendering: pixelated is now !important like every other declaration in the block, preceded by -moz-crisp-edges / crisp-edges for older engines.
Win95 skeletons are dithered wells, not grey paint. .win95-skeleton swapped its flat #a0a0a0 fill for the classic 2x2 checkerboard of white and silver — the same 50% dither the scrollbar track uses — on a sunken well, and Win95Skeleton.defaultGeometry no longer authors a 9999px radius for the circle shape (the theme's blanket corner reset was the only thing hiding it). Win95 had no skeleton-placeholder concept; the empty dithered well is its stand-in for a region with nothing in it yet.
Win95 busy states take the hourglass, not the circle-slash. A control in data-state="loading" is marked disabled by the shared button base, which previously handed it the "unavailable" cursor — a mark that meant a drop would be refused, not that work was in progress. [data-state="loading"] (and its subtree) and .arcane-loader now join [data-busy], [aria-busy] and .arcane-loading on the --w95-cursor-wait rule.
ArcaneStyleData.raw, shadowCustom, filterCustom, and
animationCustom escape hatches that could recreate gradients, colored
glows, filters, or arbitrary hidden-state effects.FadeEdge, scroll-area
shadow fades, and carousel feather-gradient configuration. Intrinsically
round controls now own their internal geometry instead of exposing pill
radii to product surfaces.ToastScripts surface.GradientBuilder, ArcaneColorGradient.toGradient, and the pill-shaped ArcaneStylePresets.statusBadge shortcut.ArcaneSelect now renders a real sibling
chevron with a 14px end inset and a 40px value reserve instead of embedding
SVG artwork in background-image; shared prefix/suffix field shells own the
only visible focus perimeter while their inner inputs remain borderless.jaspr_content root
rules can no longer restore Open Sans or JetBrains Mono after the Arcane
stylesheet loads; documents and code now inherit only the committed
Akzidenz Grotesk Pro and Hack families.borderCustom rejects declaration
boundaries and directional border fragments, including semicolon injection
through a CSS variable fallback, so it cannot reconstruct a one-sided accent.button[aria-expanded] on the
page.Win95Progress.indicatorStyles returned an empty map, so the block-level indicator's width resolved to auto — 100% of the track — and every meter rendered at 100% regardless of props.value. It now emits the clamped percentage inline with transition: none, so the fill snaps in whole segments the way a Win95 meter did. A 20% meter now measures 20% of its trough..win95-button[data-variant="primary"] drew it as inset 0 0 0 1px var(--w95-dark) behind var(--w95-raised) — but box-shadows paint first-listed on top and --w95-raised already covers a full 1px ring on every side, so the ring was 100% occluded. It is now a real border: 1px solid var(--w95-dark) with the bevel inside it, keeping the outer footprint identical via box-sizing: border-box so button rows stay aligned. The destructive variant's equally invisible maroon ring got the same treatment.::-webkit-scrollbar blocks disagreed about the track dither; the earlier 16px one was entirely dead (the later block wins on source order and carries !important on every declaration) and has been deleted. SM_CXVSCROLL/SM_CYHSCROLL is 16 in Windows 95 and every part of the control is measured off that module, so the surviving definition — and the page-level html:has() copy — moved from 17px to 16px and the 16x16 arrow art lands on the pixel grid again. The in-app track and thumb also stopped hard-coding #ffffff/#c0c0c0 and resolve through --w95-field/--w95-face, so the dark scheme re-points the dither instead of glaring white.3px 0 10px rgba(0, 0, 0, 0.4) in all eight directional variants — a 10px gaussian, the most modern artifact in the sheet — and eight popup surfaces (dropdown, popover, select, combobox, date and time pickers, command dialog, dialog, menubar content, search results) added 2px 2px 0 rgba(0, 0, 0, 0.35). Windows 95 had no alpha compositing at all, and stock Win95 menus and dialogs cast no shadow whatsoever, so every shadow layer beyond var(--w95-raised) is gone; the 45%-black scrim already separates drawers and sheets from the page. The core mega-menu panel's inline 25px/10px blurred double shadow is overridden in theme scope for the same reason.ModalScripts writes transform: scale(0.95) on close and scale(1) on open; with the transition dead that meant the window snapped to 95% and sat there for the 150ms before the overlay was hidden. .win95-dialog now pins transform: none, because a Win95 dialog appeared and vanished at full size.Render bases expose a concrete-default decorationStyles(ArcaneDecoration?) hook (returns {}) so themes opt in without a breaking change; the merge sea…
type attribute
after the data-* attributes in rendered <button> markup. Attribute
reorder only — the rendered DOM is semantically unchanged.ArcaneGallery area controls. Packed galleries can now set
minimumTileArea and targetTileArea in grid cells (columnSpan × rowSpan).
The packer excludes undersized candidate spans when the floor is attainable
and uses one ratio-independent target area for every item. Galleries that
omit both controls retain the existing aspect-derived sizing behavior.Card rendered inside another arcane Card now drops its own frame — background, border, and shadow — so stacked panels no longer read as a "card-in-card" border-in-border, most visibly under Shadcn and Neubrutalism. The inner card re-asserts a distinct frame by opting out with decoration:/styles: (which set the data-arcane-decorated attribute the flatten rule excludes). Implemented as pure per-theme CSS (#arcane-root.arcane-theme-* .<theme>-card .<theme>-card:not([data-arcane-decorated])), scoped and !important so it beats both inline variant styles (shadcn/neubrutalism) and the theme's own !important surface rules (neubrutalism); golden-neutral (the render snapshot suite excludes theme CSS by design). Note: a layout container that a host app styles as a card via its own CSS is outside the library's reach — only real nested arcane Cards flatten.arcane_jaspr_kb. Split each theme's
*_kb_renderers (which import arcane_lexicon, a server-side dart:io/jaspr_content
docs package) out of the theme packages into a new opt-in arcane_jaspr_kb package. The
theme packages (arcane_jaspr_shadcn/_neubrutalism/_neon/_win95) no longer depend on
arcane_lexicon, so importing them in a Jaspr client app no longer drags dart:io into
the client entry (which silently dropped main.client.dart and shipped a blank app). Docs
sites now depend on arcane_jaspr_kb for the knowledge-base chrome renderers.ArcaneDecoration (core/decoration/arcane_decoration.dart) — a Flutter-BoxDecoration-shaped, theme-permeable surface with universal fields (color, gradient, borderRadius, border, padding, backdropFilter) plus an Elevation intent every theme maps to its own idiom (shadcn ambient blur, neubrutalism hard offset, neon glow) and a theme-specific shadowColor honored-or-ignored per theme. Button and Card now accept decoration: (semantic) and styles: ArcaneStyleData? (literal escape hatch that always wins). Render bases expose a concrete-default decorationStyles(ArcaneDecoration?) hook (returns {}) so themes opt in without a breaking change; the merge seam layers theme base -> variant -> size -> decoration -> literal styles.ArcaneColorOps extension on String — .opacity(double) and .on(base, double) emit color-mix() for any CSS color (hex, var(--x), ArcaneColor.*.css, runtime accents), replacing raw color-mix strings and the fragile ${color}30 hex-alpha hack (which produces invalid CSS for var() colors).ArcaneStyleData gained the typed fields that previously forced raw: maps: single-side padding (paddingTop/Right/Bottom/Left), per-side custom border strings (borderTopCustom/…, which can carry a runtime color or none), boxSizing (BoxSizing enum), backgroundClip (emits standard + -webkit-), and backdropFilterCustom.styles:/decoration: surface now covers all ~60 wired themed-visual components — every actionable component across buttons, cards, feedback (Alert/Avatar/StatusBadge/Toast/Kbd/Breadcrumbs/Progress/CircularProgress/Skeleton/LoadingSpinner/EmptyState), interactive (Accordion/Disclosure/Cycle/Toggle/ToggleGroup/ToggleSwitch/Checkbox/RadioGroup/Slider/Separator), overlays (Dialog/Sheet/Drawer/Command/Floating/ContextMenu/DropdownMenu/Menubar/Toolbar), inputs (TextInput/Select/NativeSelect/OtpInput/TimePicker/DatePicker/Calendar/FieldWrapper/InputGroup), tabs/pagination, and the promo chrome (TopAnnouncementBar/PromoModal/…/SlotCounter). For overlay components the override routes to the visible panel, never the scrim; for standalone theme renderers that don't extend the shared base the merge is applied directly; components with no core render base use the 2-spread form. Calendar's per-theme style member was migrated from dom.Styles to Map<String,String> to host the seam. Every addition is golden-neutral (rendered output unchanged when the new fields are unset).IconBadge. The card props + renderer-contracts that existed as dead code (StatCardProps, FeatureCardProps, CTACardProps, TestimonialCardProps, PricingCardProps) now have public widgets and per-theme renderers wired into all three themes: StatCard/StatCardRow, FeatureCard/IconCard, CtaCard, TestimonialCard/RatingStars, and PricingCard/PricingGrid. Each renders from theme CSS variables (adapts to any palette), is permeable (styles:/decoration:), and composes via a shared render base + thin theme subclasses. New IconBadge component makes the ubiquitous colored-icon-circle idiom first-class.cx(List<String?>) class-join helper (util/classes.dart, exported) collapses the _joinClasses idiom copy-pasted across sites; Container gained a semantic tag: ('nav'/'section'/'header'/…) so structural markup no longer drops to raw dom.* — the default div path is byte-identical.CtaCard gained accentColor (tints the icon chip + CTA) and isExternal (opens in a new tab with rel="noopener noreferrer" and a ↗ affordance); PricingCard gained highlighted (an accent wash + a solid, row-outranking CTA) and per-row SpecEntry.highlight. These are what let QualityNode's marketing pages migrate onto the library cards with zero visual change.Card.padding/Card.borderRadius are now typed EdgeInsets?/BorderRadius? (were raw-CSS String?), aligning Card with ArcaneStructuredCard. The classes: parameter on the arcane wrapper widgets (ArcaneDiv/ArcaneSpan/ArcaneSection/ArcaneParagraph/ArcaneHeading/ArcaneLink/ArcaneNav/… and the layout/HTML wrappers) now takes List<String>? (was String?), joined internally via cx — conditional class lists (['card', if (active) 'is-active']) no longer need a manual join. ArcaneStyleData.backgroundClip is now a typed BackgroundClip enum (borderBox/paddingBox/contentBox/text) instead of a raw String?.ArcaneGallery — themeable media masonry. New component/collection/gallery.dart + core/props/gallery_props.dart + shared core/rendering/base/gallery_render_base.dart, wired into ComponentRenderers via GalleryRendererContract, with a per-theme renderer in every theme package (packages/*/lib/src/renderers/gallery.dart). Each theme renders the SAME ArcaneGalleryTile{media(aspectRatio,src?),mediaChild?,title,meta,href/onTap,overlay,footer} list natively: win95 = titled windows on the teal desktop, shadcn = clean cards, neubrutalism = hard-shadow blocks, neon = glow. The CSS-grid base uses coarse rows (grid-auto-rows: minColumnWidth/2) + aspect-driven COLUMN spans (galleryColumnSpan: wide→2, panorama→3) + grid-auto-flow: dense for a variable-width mosaic, refined by an opt-in packing script (util/interactivity/scripts/gallery/gallery_scripts.dart, emitted via includeFallbackScripts). Media covers its cell (object-fit: cover).var(--w95-desktop-in, …) / --w95-title-a-in / --w95-title-b-in / --w95-selection-in (plus --primary/--accent/--ring), falling back to the active [Win95Theme] scheme when unset. A host app can inject those --w95-*-in variables (scoped to #arcane-root.arcane-theme-win95) to re-tint the desktop + title bars from a runtime account accent live, without a rebuild — while the silver #c0c0c0 control face and 3D bevels stay fixed across every accent.Rounded emphasis surfaces no longer render orphaned edge accents. Accent alerts, feature cards, testimonial cards, Neon cards, prose/knowledge-base callouts, and active rounded navigation now use a complete perimeter, fill, icon, or symmetric ring instead of a one-sided border or clipped stripe. The component docs teach the same invariant, the alert showcase exercises the accent variant, and a render/CSS contract prevents the crescent treatment from returning.
Confirm and alert dialogs are visible when rendered. Their shared
renderer now opens the underlying dialog surface instead of emitting it with
the default closed state and hidden attribute.
ArcaneGallery packed tiles can no longer overlap around fragmented
gaps. Placement now searches through the complete occupied board and always
includes the first wholly empty row. Its defensive fallback also appends
below occupied cells instead of forcing a multi-cell tile into the first
undersized gap.
Owner-sized ArcaneGallery packing no longer strands interior cells.
Each source-ordered tile anchors at the earliest unfilled cell while retaining
its independent ratio-scored row and column spans. Normal masonry gutters and
a ragged terminal edge remain unchanged; later tiles cannot skip an interior
cell, overlap earlier art, or fall below the configured minimum.
Shadcn: the dropdown panel no longer claims the open-trigger accent fill. .arcane-dropdown-menu[data-state='open'] sat in the shared "open/active trigger" selector list that paints background-color: var(--accent); color: var(--accent-foreground), so the popover surface was told to drop off the --popover/--popover-foreground pair every other floating surface uses. It was masked in practice by the renderer's inline background-color: var(--popover), but any consumer that overrode the panel's inline styles (styles:/decoration:) inherited an accent-tinted panel instead of a popover one. Only the trigger selectors remain in the list.
ArcaneGallery tile caption is now theme-opt-in. The render base only emits the title/meta header block when the theme opts in via showsTileHeader (win95 renders it as the window title bar). The card-style themes (shadcn/neubrutalism/neon) previously rendered the caption after the media, where a rounded tile clipped its bottom corners into a stray "caption strip"; they now render clean media-only tiles and expose the title to assistive tech via the tile's aria-label.
Permeability internals hardening. Rolled the layerStyles "literal-wins" helper out to 20 more single-root render bases (avatar, breadcrumbs, alert, skeleton, accordion, disclosure, toggle-group, kbd, menubar, toast, slider, radio-group, sidebar, field-wrapper/input-group, separator, time-picker, cycle-button, tab-bar, spinner, calendar) — byte-identical output, so a decoration shorthand can no longer out-emit a literal styles: longhand anywhere. Removed a dead decorationStyles spread from the checkbox/toggle-switch renderers (no theme mapped it; universal decoration: fields + literal styles: still apply). Renamed the Calendar renderer base's abstract styles getter to rootStyles to disambiguate it from the permeable props.styles. Neubrutalism decorated toasts opt out of the variant-border !important reset via data-arcane-decorated, matching the card opt-out.
Permeability precedence hardening (quality pass). The seam now guarantees literal styles: wins the cascade via a layerStyles helper (lib/core/rendering/base/style_layering.dart) that remove-then-reinserts override keys, so "later layer" == "later in the emitted CSS" — closing a Dart map key-position pitfall where a decoration shorthand (background) could beat a literal styles: longhand (background-color). Also fixed: trailing button-resets on tappable Card/anchor Button that clobbered styles:; StatusBadge applying its position/card-color mutations after the permeability merge; ArcaneStyleData.merge() silently dropping alignItems/justifyContent; ArcaneNativeSelect/TextArea/SlotCounter factory constructors not threading the new fields; and ArcaneColorOps.opacity quantizing to whole percent. Neubrutalism per-instance shadow recolor now bakes the color into the box-shadow (CSS custom properties resolve at the theme root, so the prior var(--nb-shadow-color) override never inherited), and decorated cards opt out of the theme's !important surface reset via a data-arcane-decorated attribute.
Form controls no longer throw on every interaction under dart2js (Dart 3.11). Event handlers read DOM properties via dynamic member access ((event.target as dynamic).value, .checked, .selectedIndex, .key, .stopPropagation()), which throws TypeError: <name> is not a function on package:web extension types — silently breaking every text input, textarea, OTP input, select search, and clear button (values never reached the callbacks). Added core/dom_value.dart (conditional web/stub helpers using explicit getProperty/callMethod interop) and routed text_input/field/text_input_render_base/otp_input_render_base/select_render_base through it. The helper is conditionally imported so styled components keep compiling during off-web style extraction (where dart:js_interop is unavailable).
ArcaneApp.head is now actually rendered. The head: List<Widget>? parameter was declared but never injected into the document; ArcaneApp now emits the supplied widgets through jaspr's cross-platform Document.head, so app-level <link>/<script>/<meta> widgets reach <head> during both SSR and client hydration. No change when head is null/empty.
Server-side rendering crash in the form-field widgets (ArcaneStringField, ArcaneBoolField, ArcaneColorField, ArcaneDateField, ArcaneTimeField, ArcaneEnumField). ArcaneField now guards its asynchronous value load to the client (kIsWeb), so SSR renders the loading placeholder instead of throwing a build-phase setState assertion. Client behavior is unchanged.
shadcn knowledge-base sidebar: pinned directly below the sticky top bar (was offset ~56px too low, leaving a gap above the nav) and made independently scrollable (max-height + overflow-y: auto) instead of requiring the whole page to scroll.
shadcn top bar: restored position: sticky (it was static, so it scrolled off-screen) with a solid background and bottom hairline, so it stays pinned while scrolling.
shadcn docs content layout: the content area now centers (capped at --container-2xl with auto margins instead of stretching edge-to-edge), and the right-hand table-of-contents column is only reserved when a TOC is actually present — TOC-less pages (e.g. component docs) now center the article instead of leaving an empty reserved column.
shadcn docs sidebar: nested nav items (sub-folder contents) now have vertical spacing — .sidebar-tree was missing a gap, so deeply nested items packed together; added gap: 0.25rem so they match the top-level spacing.
Neon docs sidebar: pinned directly below the sticky top bar (was offset by a doubled top, leaving a gap above the nav) and given its own scroll rail (max-height + overflow-y: auto) instead of scrolling with the page. The shadcn layout port had keyed this on the .shadcn-kb-sidebar class; the rule now targets .neon-kb-sidebar so it actually matches the Neon DOM.
Theme CSS is no longer re-materialized on stylesheet cache hits. ArcaneStylesheetCss.resolve now takes the component CSS as a thunk (String Function()) invoked only on a cache miss, so the large per-theme CSS string (e.g. the Win95 theme's ~5,600 lines) is built once and skipped entirely when the Expando cache is warm. Output is byte-identical.
Removed the last two dynamic public-API fields (strong-typing): TreeNodeData.data is now Object?, and the four FileUploadProps drag/input handlers are typed void Function(web.Event)? (the concrete type jaspr's events layer delivers) instead of void Function(dynamic)?.
style_presets.dart catalog (8 preset classes, zero references — the live preset vocabulary is the ArcaneStyleData statics) and 11 orphan *_props.dart files whose widgets already ship without them (tile, card_section, center_body, expander, glass, section, radio_cards, fab, icon_button, header, plus the dead SearchProps/BarProps classes). Live symbols trapped in dead files (the BarBackButtonMode enum, SearchResult) were preserved. Verified: every deleted symbol had zero references across lib/packages/test/tool/bin/docs, dart analyze stays 0-error, and the suite holds at 42 pre-existing golden failures.layerStyles cascade helper (test/unit/layer_styles_test.dart) — the core "literal styles: always wins" guarantee was only exercised indirectly through Card SSR cases; 7 tests now assert later-override-wins, key-moved-to-last, null-skip, and left-to-right multi-override behavior.*_props.dart files (lib/core/props/ went from 114 files to 67) — every Props class + RendererContract with zero references to any widget, render base, theme renderer, or dispatch aggregator (ComponentRenderers/LayoutRenderers). These were scaffolding for components that were never built. Verified by a per-symbol zero-reference scan (file_upload, tree_view, timeline, stepper, tracker, meter, hero_section, game_tile, carpet, marquee, switcher, and ~36 others, plus a footer_props/footer_column_props dead cluster). status_indicator_props.dart was stripped to just its live StatusType enum; live symbols trapped in otherwise-dead files were preserved. dart analyze stays 0-error and the suite holds at its 42 pre-existing golden failures.Loaders now have one theme-renderer source of truth. Standalone spinners,
loading buttons, authentication guards, loading toasts (including the
JavaScript fallback), and Shadcn loading selects all resolve through
ComponentRenderers.loadingSpinner, so a theme's loader updates every
loading surface.
Preset style field renamed to variant (breaking, no back-compat). The 8 components that exposed a preset-enum selector as style: (ArcaneAlert, ArcaneKbd, ArcaneEmptyState, ArcanePagination, InlineHeroBanner, CodeBlock, MutableText, ArcaneDropdownItem) now expose it as variant: — matching Card/PricingCard which already used variant, and eliminating the one-letter collision with the permeable styles: escape hatch (a typo between style: and styles: previously compiled and did the opposite thing). Enum type names (AlertStyle, KbdStyle, …) are unchanged; only the field/param. Rendered output is byte-identical (identifier-only change).
StatCard.icon is now Widget? (was String?, breaking). It matches FeatureCard/CtaCard/PricingCard — icons are Widgets, as in Flutter. The shared StatCardRenderBase now places the widget directly in the icon badge instead of emitting the string as text.
Dropped the onClick alias on the Card family (breaking). All nine Card/ArcaneStructuredCard/ArcaneImageCard constructors kept both onTap and onClick (_onTap = onTap ?? onClick); onClick is removed, leaving onTap (Flutter parity, no-alias rule). No call sites passed onClick to a card.
Internal: deduplicated the arcane_jaspr_shadcn, arcane_jaspr_neon, and arcane_jaspr_neubrutalism renderer implementations into shared base classes under lib/core/rendering/base/. Public renderer class names and rendered HTML output are unchanged (verified byte-identical across all three themes via golden snapshots). Removed the per-package control_styles.dart.
PricingCard layout tuned to match real plan/hosting cards. The spec table now renders above the feature list inside a bordered, rounded box (was plain rows below the features); a non-highlighted card's CTA is a neutral outline (foreground label, --border edge) so a highlighted card's solid accent CTA visibly outranks its row; and the badge pill is no longer force-uppercased — it renders the label exactly as passed.
Neon theme: PricingCard and TestimonialCard now lift on hover (whole-card translateY + shadow), matching the neon card feel. Delivered from the theme layer (neon_css.dart) so consumers get it for free instead of stapling it into site CSS.
arcane_jaspr_neon rebuilt as a dark-first "gamer" theme (replaces the prior neutralized skeleton). Distilled from the QualityNode aesthetic: the default NeonTheme.green is an emerald-to-cyan palette, with seven other neon variants. The palette is fully seeded from NeonTheme via lightSeed/darkSeed (like the shadcn and neubrutalism themes); the dark seed enables accentGlow, so shadows carry a neon glow. New NeonCss supplies a restrained, glow-and-gradient component layer (Oxanium display font, uniform accent frames, uppercase tracked badges, accent focus rings) — every rule scoped to #arcane-root.arcane-theme-neon, so the shadcn and neubrutalism themes are unaffected. NeonCss also styles the documentation knowledge-base chrome (top bar, sidebar, content grid, TOC) so the Neon docs render correctly (the bespoke Neon docs layout was retired in arcane_lexicon; Neon now uses the standard chrome). Neon is back in the golden snapshot tests.
package:arcane_jaspr/arcane_jaspr.dart now exposes a curated Flutter-shaped surface instead of re-exporting raw Jaspr and DOM APIs
Flutter-First Primary Surface
package:arcane_jaspr/arcane_jaspr.dart now exposes a curated Flutter-shaped surface instead of re-exporting raw Jaspr and DOM APIsWidget, StatelessWidget, StatefulWidget, State, InheritedWidget, BuildContext, Key, and the normal runApp path on the primary importSecondary Surface Split
package:arcane_jaspr/flutter.dartpackage:arcane_jaspr/html.dartpackage:arcane_jaspr/web.dartDeveloper Experience Reset
tool/check_primary_surface.dart to scan docs, demos, and templates for banned primary-surface examples such as old component base classes, htmlFor, and explicit generic angle-bracket usageNothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Your coding agent can read these notes before it upgrades. Set up the MCP server →