Releases: smm-h/tinymoon
Release list
v0.9.1
strictcli 0.29 compatibility — fixes tinymoon check crashing at startup
Context
strictcli 0.29.0 requires ctx-first handler signatures. Published tinymoon 0.9.0 crashed at import under current strictcli (handler missing parameter "dir"). This patch migrates all CLI handlers to the ctx-first signature, restoring compatibility.
Fixes
- Fixed
tinymoon checkcrashing at startup with strictcli 0.29+. The CLI now uses ctx-first handler signatures, restoring compatibility with current strictcli.
v0.9.0
Deep-link query parsing and async view factories land in the router; shortcut suppression now covers all light-dismiss overlays; a human visual review pass fixed tooltip cancellation on unrelated scrolls, transcript entry flashing, and demo pacing.
Context
The router features were originally deferred at the frozen core ceiling and became trivial after the ratchet; the shortcuts gap closed by consulting the dismissal registry that postdated it; the polish items came from the first human visual review of the gallery, which also surfaced a real framework tooltip bug.
Features
- Router deep-link queries and async view factories. Hash links may carry a query (
#/route/tail?a=1&b=2) parsed into an object delivered to views viactx.query/setQuery; a route's view factory may return a Promise, showing a loading placeholder while it resolves and an error block on failure.
Fixes
- Tooltips survive background scrolling. A hovered tooltip is no longer cancelled when an unrelated region scrolls (e.g. a live feed autoscrolling to its tail); it only closes when the scroll actually moves its anchor.
- Keyboard shortcuts suppress under transient overlays. While a popover, context menu, select menu, or non-modal drawer is open, ordinary shortcuts no longer fire (only
{global: true}do), matching the behavior under modal dialogs.
v0.8.0
The size guarantee becomes a standing ratchet (ceilings re-baseline to measured x1.25 when they bind; core re-baselined once to 147,500), with honest comparative positioning — a capability matrix in the README, a dated size survey in the gallery, and small consumer-reported fixes (renderStats instance pass-through, checker-limitation and vendoring documentation).
Context
The frozen core ceiling had reached 17 bytes of headroom and was forcing byte-archaeology on every core-file change; the ratchet keeps walls deliberately close (placement decisions stay forced) while ending the archaeology. Positioning was rewritten against a mid-2026 survey of the minimal-framework landscape — the differentiator stated as surface-per-byte plus enforcement tooling, with measured gzip numbers replacing the absolute "tiny" claim.
Breaking
- The size guarantee changed: the core JS byte ceiling is no longer frozen. Every tier ceiling (core included) now follows a STANDING RATCHET: it is set to the tier's current measured weight x1.25, and re-baselines to a new measured x1.25 in review whenever a legitimate addition makes it bind. The old "the core ceiling is never raised" promise is retired; the core ceiling re-baselined once, from 118,000 to 147,500 bytes. Walls are kept deliberately close so adding a capability still forces a real placement decision.
Features
- New README section: Scope and non-goals. Spells out that tinymoon is a fixed-identity, hand-coded, web-only framework and states its explicit non-goals (server-driven UI trees, per-deployment theming, cross-platform native rendering, and app-platform features), so readers can tell at a glance whether it fits their use case.
- New README positioning: a comparative size story and a capability coverage matrix. The docs now frame tinymoon by surface-per-byte rather than absolute smallness: honest measured numbers (raw ~396 KB, gzip ~128 KB for the whole kit), a metric-discipline note (tinymoon's gzip weight vs competitors' min+gzip), two hedged comparisons (about three Bootstraps; roughly 2x a hand-rolled no-build stack), and a durable capability matrix versus view microframeworks, Mithril, Alpine, htmx, Web Awesome, and Bootstrap.
renderStatsnow accepts already-builtcreateStatinstances, or any mix of instances and config objects, not only config objects. Passing an instance no longer throws "label is required" -- an entry carrying an.elis mounted as-is (preserving references, bindings, and a pre-set trend), and the row'sdestroystill tears it down.
Fixes
- Documented two conformance-checker details consumers rely on. The
external-urlrule matches inline URL literals only: a URL passed through a variable (const u = "https://..."; fetch(u)) is neither caught nor allowlistable, so keep external URLs inline at the load site. And vendored framework assets pass by hash-identity in a plain directory with no manifest, but underthird_party/the quarantine's pin requirement applies to them too.
v0.7.0
Consumer-driven patch wave: vendored-framework recognition by hash identity, fenced code blocks in doc markdown, put/patch/del net helpers, table row event hooks, 2+1/1+2 grid presets, a WCAG-verified danger button, and a Python-side theme boot snippet — plus checker false-positive fixes and a hard error for a silent route misconfiguration.
Context
every item traces to frictions reported by the four real consumer migrations completed on 0.6.1; the vendored-identity mechanism extends the provenance doctrine so no-backend consumers can vendor the framework without quarantining it; the danger-button ladder exists because the naive red fill fails WCAG in dark theme (measured).
Features
- createLoadMore now has a
load()method. Programmatically trigger the first (or next) page without synthesizing a button click. - createGrid gains
2+1and1+2presets. Asymmetric 3-slot layouts — a row of two above one full-width cell, and its mirror — that collapse to a single column on narrow screens. - createTable gains
onRowClickandonRowHover. Event-delegated row hooks —onRowClick(row, index, event)andonRowHover(row | null, index, event)— that survivesetRows()re-renders and never fire for the header or the "more rows" note. netgainsput,patch, anddel. JSON mutation verbs mirroringpost()exactly — ApiError on non-OK, the pre-throwonErrorhook, the auth-header hook, merged headers, and the abort signal;delsends no body.renderDocMdnow supports fenced code blocks. Triple-backtick blocks render verbatim into<pre><code>(an info string like ```js is accepted and ignored; no syntax highlighting). Tables and blockquotes remain out of scope; the inlinerenderMiniMddialect is unchanged.- createSwitch now has a
get()method. Read the switch's current on/off state directly, matchingcreateCheckbox/createRadio. - createFileInput now has an
open()method. Programmatically open the native file picker, the same as clicking the styled trigger. - Vendored tinymoon assets now pass the checker by identity. A file whose bytes sha256-match a packaged framework asset is recognized as framework-own wherever a consumer serves it from (no quarantine needed); editing it breaks the match and it is scanned normally.
- New
.btn.dangerbutton variant. A destructive-action button — solid red fill with a white label — backed by a new red token ladder (--red-solid,--red-solid-hi,--on-red,--red-glow) that clears WCAG AA (4.5:1) contrast in both themes. - The PyPI package now exports
THEME_BOOT_SNIPPET.from tinymoon import THEME_BOOT_SNIPPETgives Python servers the same pre-paint theme script as the JS export (kept byte-for-byte identical by a sync test), so a server-rendered page can inline it without importing the JS module. - New consumption docs. README now covers typed consumption when serving assets yourself (install
tinymoonas a devDependency purely for types), the vendored-asset identity story, badges as one-shot elements (mutate or replace, no update API), and the fixed-height virtual-list constraint;index.d.tsannotates each barrel export's real subpath module.
Fixes
- A route
viewthat is a plain object now hard-errors instead of rendering nothing. Passing a built view object wheremountShellexpects a factory previously failed silently; it now throws a message naming the fix (wrap it: () => view). - Two checker false positives fixed for consumers. HTML numeric character references (
',’) are no longer flagged as raw#RGBcolors; and the plain-object.titlefalse positive is now documented with its bracket-notation workaround (obj["title"] = x). Genuine hex colors and realelement.titlewrites are still detected.
v0.6.1
Date/time picker popovers join the centralized light-dismiss engine: toggle buttons close cleanly without reopen races, and pickers nested in other overlays dismiss correctly.
Context
Two prior releases (0.5.0, 0.6.0) were tagged but blocked from registries by load-dependent e2e races, both root-caused (a starved deferred listener registration; a focus no-op on hidden eager routes) and fixed. This release completes the overlay-family migration onto the dismissal engine, hardens the e2e suite against the race classes, and adds a stress preflight gate. It is the first registry-published release of the augmentation line.
Fixes
- Date/time picker dismissal. Date and time picker popovers now close cleanly when their toggle button is pressed again, and no longer conflict with an enclosing overlay's dismissal (e.g. a picker inside a drawer closes only itself).
v0.6.0
Light-dismiss engine and invoker contract: centralized outside-press dismissal with gesture-claim (registerLightDismiss, registerOverlayTrigger), fixing drawer dismissal reliability and toggle close-reopen races across all overlays.
Context
v0.5.0's registry publish was gated on a CI failure that exposed a real race (the drawer's deferred outside-dismiss listener registration could be starved on loaded machines, reproduced deterministically under CPU throttle). Rather than patching the timer, this release ships the already-designed structural replacement: light-dismiss centralized in one synchronous capture-phase listener beside the existing Escape stack, with per-gesture claim so a trigger press closes without re-opening; all five bespoke overlay implementations migrated onto it. Also hardens the e2e suite against a second CI-only flake class (axe scans sampling mid entrance-animation).
Features
- Overlay-trigger invoker + light-dismiss engine. New
registerOverlayTrigger(triggerEl, opener)andregisterLightDismiss(opts)(fromtinymoon/chrome) give overlays a framework-owned toggle button (aria-expanded, gesture-claim so a close-press cannot immediately reopen) and a centralized outside-pointer dismissal registry.
Fixes
- Reliable drawer dismissal. A non-modal drawer could intermittently fail to close on an outside click in slow or loaded environments; toggling a drawer via its trigger could close-then-reopen. Both are fixed — outside-pointer dismissal is now registered synchronously and a re-press on the trigger closes it for good.
v0.5.0
The augmentation release: forms, data display, state, realtime, and app-chrome primitive families; an isolation-boundary model (tm-embed, vendor quarantine) and expanded conformance enforcement; per-tier size budgets with subpath exports and typed barrels.
Context
A coordinated framework build-out driven by real consumer needs: the widget families (inputs, slider, table, virtual list, tree, filters, feed, charts, drawer, palette, view factory), the L2 state story (store/bind/reconcile), realtime transports, the identity-surface doctrine with loads-vs-navigations re-scoping, the tm-embed boundary and hash-pinned vendor quarantine, the native-control ban expansion now that every replacement factory ships, portable conformance artifacts for non-Python consumers, and the package restructure into budgeted tiers (core frozen, widgets/state/chrome/controls tiers, five stylesheets, tokens.json). Validated end-to-end against a real consumer migration before release.
Breaking
- Native-control ban expanded.
tinymoon checkand the runtime auditor now reject native<textarea>and native<input>of every type that has a shipped replacement: text/password/email/url/search/tel (usecreateInput), range (createSlider), number (createNumber), time (createTimePicker), date (createDatePicker), alongside checkbox/radio/file and<select>/<dialog>as before. A bare typeless<input>also fires (it defaults to text).type="hidden"andtype="color"stay legal. Consumer code using these raw natives now fails the checker -- migrate to the named factories.
Features
- Command palette, shortcuts, lazy mounting, async-state blocks. New
tinymoon/chromemodules: a fuzzy command palette (opt-in via installPalette), a keyboard-shortcut binder, IntersectionObserver-gated lazy mounting, and loading/empty/error state blocks with a renderAsync helper. - Toast action button.
toast(msg, kind, {action: {label, onClick}})adds one action button that keeps the toast on screen until the action is taken or it is dismissed; acting dismisses. - Tri-state theme. Theme now supports
"system"(follows the OS light/dark preference and re-resolves live when it changes), alongside explicit light/dark. Adds acycleTheme(store)helper and aTHEME_BOOT_SNIPPETinline no-flash boot script. - Documentation. New README and wiki sections cover the
tinymoon/chromebarrel (async-state blocks, lazyMount, shortcuts, command palette), the toast action button, and the tri-state theme with its no-flash boot snippet. - Shell views. New
createView(render)view factory, an eager-route flag to bypass lazy mounting, andshell.announce()for live-region route announcements. - Drawer primitive. New
openDrawer()overlay primitive plus swipe-to-close on the shell mobile navigation drawer. - Tab panels.
createTabPanelscomposes an APG tab bar with lazily-mounted panels. - Grid layout.
createGridCSS-first preset layout primitive. - Icon button.
iconButton— a stateful topbar icon button instance. - Data-display widgets, part 2. New primitives: keyboard-navigable tree, filter bar with chips, load-more, breadcrumbs, sparkline, chart container, and activity feed.
- Data-display widgets. New data-display barrel:
badge,createStat/renderStats, sortablecreateTable, and windowedcreateVirtualList, with their stylesheet. - Status color token. New
--goldwarn/caution status color with contrast-checked pairs in both themes. - Realtime, net, and format helpers.
tinymoon/extrasgains realtime (SSE/WebSocket with backoff),netApiError/auth/options, andfmtTime/relativeTime/liveRelativeTimeformatters. - Settings subscriptions.
settings.subscribe(key, cb)(ornullfor all keys) with store-contract parity. - State layer. New
tinymoon/statebarrel with an L2 state layer:store,bind, andreconcile. - Number stepper.
createNumberwraps a nativeinput[type=number]with stepper buttons. - Time picker.
createTimePicker— a 24-hour HH:MM picker with locale-aware display. - Combobox and multi-select. New
createComboboxandcreateMultiSelecttypeahead primitives. - Accordion.
createAccordion— grid-rows animated disclosure panels. - New icons.
plus,minus, andclockicons for steppers and the time picker. - Form controls. Styled-native
createInput,createTextarea,createField, andcreateSlider. - Embed isolation.
createEmbedisolation-boundary primitive with iframe and shadow-DOM modes. - Vendor quarantine. Conformance checker gains a hash-pinned vendor quarantine (
unpinned-vendorrule) for sanctioned third-party code. - Dialog ban. Conformance checker now statically flags consumer
<dialog>as a banned native control. - External-URL rule. The external-url check now treats
<a>/<area>hrefas navigation rather than a resource load, so hyperlinks are no longer flagged. - Conformance artifacts. Committed
rules.json, test corpus, andexpectations.jsonplus a generator, enabling cross-language conformance consumption. - Token export artifact. Committed
assets/tokens.jsonplus its generator, so consumers can read design tokens as data. - Subpath exports. Per-module subpath exports for every shipped JS module.
- TypeScript types for subpaths.
typesfield and per-exporttypesconditions wired intopackage.json. - TypeScript declarations. Type declarations for the
tinymoon/extrasbarrel. - Documentation. README documents the Phase 6A primitives and the chrome-js tier.
- Documentation. README documents the state and data-display widgets barrels and names all budget tiers.
- Documentation. README documents the Phase 3B control primitives and the controls-js tier.
- Documentation. README documents the
createInput,createTextarea,createField, andcreateSliderprimitives. - Documentation. New README section on running conformance from non-Python CI (portable artifacts, Go worked example, full-scan CLI path).
- Documentation. README documents subpath imports, the TypeScript types stance, and the per-tier size promise.
- Documentation. README documents the vendored third-party code quarantine mechanism.
- Documentation. README documents
widgets.cssas the optional fifth data-display stylesheet. - Documentation. README notes the static
<dialog>ban and the framework openModal exemption. - Documentation. README documents
copyButtonandkebabButtonas sanctioned one-shot utilities. - setPageSub is now exported from the core barrel (
import { setPageSub } from "tinymoon"), alongsideannounce. Views that do not go throughcreateViewcan set the shell's topbar page-subtitle without reaching into the DOM. - Table row and cell class hooks.
createTablegainsrowClass(row)and per-columncellClass(value, row)— return a class string to tint whole rows or individual cells from your own data. Classes are appended to the framework's owntr/tdclasses (never replacing them) and re-applied on everysetRows(). - Seek slider variant.
createSlider({variant: "seek"})is an invisible position scrubber for laying over app-drawn visuals (waveform/timeline canvases): the track and thumb go visually transparent while the full-area hit target, focus-visible outline, and slider ARIA stay intact. Your app draws the visuals underneath; the seek slider only scrubs.
Fixes
- Light theme. The accent highlight color is no longer illegible against the accent -- link and active-state highlights now stay clearly distinguishable in the light theme.
- Fix. The mobile hamburger now opens the drawer, the drawer closes on Escape, and the icon button has an accessible name.
- Fix. Small (10px) stat and table labels now use
--text-dimto keep 4.5:1 contrast when grain-composited. - Fix. The time picker popover is now positioned in viewport coordinates and hidden when closed.
- Fix. The hidden native input behind
createFileInputnow has an accessible name. - Fix. A focus-shown hovercard now stays glued to its trigger when focus scrolls it into view.
v0.4.0
The redesign release: unified primitive API, full accessibility, design token system, object-copy model, responsive mobile support, and a guarantee-grade conformance checker.
Context
A comprehensive external critique identified structural gaps in the framework:
no custom form controls, no ARIA semantics, hardcoded CSS values, no mobile
support, no RTL readiness, and a conformance checker that could be bypassed.
A 12-phase charter addressed every finding. The primitive API was unified
around factory functions returning {el, methods, destroy()}. A kernel module
centralized escape-stack and overlay management. Design tokens replaced all
raw literals. The barrel was split into core and extras for tree-shakeable
imports. Every primitive was rebuilt with APG-compliant keyboard and ARIA
patterns. A runtime auditor and expanded conformance checker close the loop
on charter guarantees.
Breaking
- Breaking. Unified primitive API:
toggleWidgetreplaced bycreateSwitch,Selectclass replaced bycreateSelect,segmentedreplaced bycreateSegmented/createTabs. All primitives now return{el, methods, destroy()}. - Breaking. Gallery migrated to
createTabs;Selectclass replaced bycreateSelectfactory with full APG combobox pattern; dead legacysegmented()function removed. - Breaking. Barrel split: core (
tinymoon) and extras (tinymoon/extras).createSettings,createWikiView,api,post,renderDocMdmoved to extras.fmtTimeremoved. Rootindex.jsre-export shim deleted. - Breaking.
cssVarmoved fromsettings.jstokernel.js. New kernel module centralizes escape-stack, overlay placement, root-element creation, and CSS variable access.
Features
- New primitives.
createCheckbox,createRadio,createFileInput,createDatePicker(APG grid pattern),createTabs-- all form-participating (except tabs), with full ARIA semantics. Tooltip split into plain-text tooltip and rich hovercard. - Accessibility. Modal rebuilt on native
<dialog>with focus trap, Escape,aria-labelledby. Toast getsaria-live, dismiss button, pause-on-hover, stack cap. Context menu getsrole=menuitem, Shift+F10 keyboard entry, focus restore. Shell gets skip link, route announcer,aria-current. - Design tokens. Rem-based type and spacing scales, duration tokens, z-index tokens, line-height and font-weight tokens. Single accent hue via
color-mix()derivation. All raw CSS literals replaced with token references. - Object-copy system. Focusable data objects with Ctrl+C support, automatic context-menu Copy entries, selection model (
.doc-body user-select: text), and print stylesheet. - Content-first. All semantic HTML styled by default with identity tokens. Declarative string-view path for the shell. Sidebar drawer JS toggle for mobile hamburger menu.
- Responsive and i18n. Full mobile support with drawer nav, dvh, 44px touch targets. Logical CSS properties for RTL. Non-Latin font fallback policy.
color-schemedeclarations for native UI matching. - Conformance checker. Template-literal URLs, importmaps, additional attributes, color functions, WebSocket schemes, encoding errors -- 6 bypass classes closed and 3 false-positive classes fixed.
- Runtime conformance auditor. Dev-mode module checking border-radius, native controls, and network loads at runtime.
- Selector hygiene. CSS rules scoped, deduplicated, and unified across all stylesheets.
- TypeScript declarations.
index.d.tsfor core barrel. - Top layer and input styling. Context menu and popover promoted to top layer via
popover=manual. Number and range input styling added. - Test infrastructure. Playwright + vitest + axe-core test suite. Zero-network, reduced-motion, responsive, RTL, and accessibility CI. Go embed/Handler tests. Characterization baselines and red-green bug tests.
Fixes
- Fix. Escape multi-close bug: pressing Escape in a Select inside a modal no longer closes both.
- Fix.
copyButtonnow awaits clipboard write before showing success state. - Fix.
icon()now throws on unknown name instead of silently returning empty string. - Fix.
registerCtxthrows on duplicate key;renderMiniMdrenders non-hash links as plain text;api()checksr.okandpost()no longer silently coerces bad JSON; router catches malformed percent-encoding in hash. - Fix. WCAG AA contrast:
--text-faint,--on-accent,--border-2adjusted to pass 4.5:1 / 3:1 in both themes. - Fix.
prefers-reduced-motionsupport: blanket suppression of transitions and animations, scroll-behavior override. - Fix. RTL layout: switch knob and toast
translateXflipped for:dir(rtl). - Fix.
ensureRootnow applies attributes even if element already exists. Wiki deep-link highlight no longer persists afterrefreshCurrent(). - Fix. Axe violations:
aria-labelon theme button,tabindex=0on scrollable main.