Zero-dependency vanilla JS/CSS UI framework with a conformance checker CLI
Project description
tinymoon
A content-first web framework: you bring plain, semantic content and small view objects; tinymoon brings the app -- shell, typography, widgets, motion. Everything ships as native ES modules and plain CSS with zero dependencies, zero build steps, and zero network loads.
tinymoon's palette is its identity. Consumer CSS must not redefine framework tokens.
Install
npm (core + extras):
npm install tinymoon
The npm package exports two barrels: "tinymoon" (core primitives) and "tinymoon/extras" (wiki, networking, settings). Assets are available at "tinymoon/assets/*".
PyPI (assets + conformance checker CLI):
pip install tinymoon
tinymoon.assets_path() returns the directory containing css/, js/, and fonts/.
Go (embedded filesystem):
import "github.com/smm-h/tinymoon"
// tinymoon.Assets — embed.FS rooted at the repo root
// tinymoon.FS() — fs.FS rooted at the assets directory
// tinymoon.Handler() — http.Handler serving the assets
Quick start
Link the four CSS layers (tokens first) and import the ES modules -- no build step, no bundler:
<link rel="stylesheet" href="assets/css/tokens.css">
<link rel="stylesheet" href="assets/css/base.css">
<link rel="stylesheet" href="assets/css/shell.css">
<link rel="stylesheet" href="assets/css/primitives.css">
<script type="module">
import { mountShell, toast } from "./assets/js/index.js";
import { createSettings } from "./assets/js/extras.js";
const settings = createSettings({
storageKey: "my-app",
defaults: { theme: "dark" },
});
settings.load();
settings.applyTheme();
const shell = mountShell({
root: document.body,
brand: {
name: "myapp",
logoHTML: '<div class="wordmark">my<b>app</b></div>',
},
routes: {
home: {
title: "Home",
icon: "library",
view: () => HomeView,
},
},
defaultRoute: "home",
});
</script>
With npm, use bare specifiers by adding an import map:
<script type="importmap">
{ "imports": { "tinymoon": "./node_modules/tinymoon/assets/js/index.js",
"tinymoon/extras": "./node_modules/tinymoon/assets/js/extras.js" } }
</script>
Primitives
Core (tinymoon)
Shell and DOM:
mountShell(opts)-- mount the app shell with sidebar, topbar, router, and footer slotel(tag, cls?, text?)-- element factory$(sel, root?)-- querySelector shorthand$$(sel, root?)-- querySelectorAll (returns array)
Controls:
createSwitch(opts)-- role="switch" toggle button (not form-participating)createCheckbox(opts)-- hidden-native checkbox facade (form-participating)createRadio(opts)-- hidden-native radio facade (form-participating)createFileInput(opts)-- hidden-native file input facade (form-participating)createSegmented(opts)-- segmented control with hidden radios (form-participating)createTabs(opts)-- tab bar (not form-participating)createSelect(opts)-- custom dropdown selectcreateDatePicker(opts)-- calendar date pickercopyButton(getText, tip?)-- one-click clipboard copy buttonkebabButton(itemsFn, tip?)-- three-dot menu button
Overlays:
toast(msg, level?, opts?)-- toast notification ("ok", "err", or plain)setToastErrorHook(fn)-- mirror error toasts into a custom hookopenModal(opts)-- modal dialog (returns close function)openPopover(anchor, builder)/closePopover()-- positioned popoverregisterCtx(key, provider)/registerCtxFooter(fn)-- context menu regionsshowCtxMenu(x, y, items, anchor?)/hideCtxMenu()-- programmatic context menuensureTooltip(el, text)/hideTip()-- tooltip lifecycleensureHovercard(el, md)/hideHovercard()-- rich hovercard with markdown
Data and utilities:
ICONS-- built-in icon set (26 icons)icon(name)-- render an icon as an SVG stringregisterIcons(map)-- merge consumer icons (collisions are hard errors)renderMiniMd(text)-- inline markdown to DOM fragment (bold, code, links)cssVar(name)-- read a computed CSS custom property valueensureRoot()-- ensure the root element existsplaceBelow(anchor, el)-- position an element below an anchorregisterCopyable(el, fn)/unregisterCopyable(el)-- register elements for the copy systemgetCopyData(el)-- retrieve copy data from a registered element
Extras (tinymoon/extras)
api(path)-- GET JSON from a same-origin pathpost(path, body, onError?)-- POST JSON to a same-origin pathcreateSettings(opts)-- localStorage-backed settings store with schema validationcreateWikiView(opts)-- wiki view factory with table of contents and deep-linkable sectionsrenderDocMd(md)-- block-level markdown to DOM (paragraphs, subheadings, lists)
Identity
The visual identity is enforced by constraint, not offered as options:
- Sharp corners everywhere --
border-radiusis 0; no exceptions. - Three-font system -- brand headings (Space Grotesk), UI body (IBM Plex Sans), monospace for data (IBM Plex Mono). All vendored, no network loads.
- Glow language -- accent glows on active cards, focused inputs, and modals. Restrained and consistent.
- Grain -- a subtle SVG noise overlay on the background.
- Motion timing -- all transitions are 100--180ms one-shot eases. The spinner is the only continuous animation.
- No native browser controls -- checkbox, radio, select, file input, and date picker are all custom-drawn with hidden native elements for form participation and accessibility.
- AA contrast -- every text-on-background token pair passes WCAG AA 4.5:1. Enforced by CI.
- Reduced motion --
prefers-reduced-motion: reducesuppresses all animation and transition durations to near-zero. Enforced by E2E tests.
Design tokens let you re-theme and re-accent; they do not let you opt out of the identity.
Conformance checker
tinymoon check scans .html, .css, and .js files and enforces the framework's non-negotiables as hard errors:
- external-url -- no network loads (no
http://,https://, or//hostURLs in HTML, CSS, or JS) - native-control -- no native
<select>,<dialog>, or<input type=checkbox|radio|file> - title-attr -- no
title=attributes (use the tooltip primitive) - border-radius -- no
border-radiusother than0/0px - raw-color -- no color literals outside
:root/html[data-theme]token definitions
uvx tinymoon check --dir ./web
One line per violation, exit non-zero on any finding. No --skip, --ignore, or warning mode. Exempt specific URLs by adding them to tinymoon-allowlist.txt at the scanned directory root.
App model
Copy system. registerCopyable(el, fn) marks any element as copyable -- clicking it copies the value returned by fn() to the clipboard, with a toast confirmation. The copyButton helper wires a standalone copy button. getCopyData(el) retrieves registered copy data programmatically.
Selection model. Elements declare tooltips via data-tooltip (plain text) and hovercards via data-hovercard (markdown with bold, code, links). The framework manages hover intent, positioning, and the hover bridge automatically.
View contract. Routes map to view objects {root, built, build(), refresh(), setSub?}. The shell's router owns the lifecycle: build() constructs the DOM once (idempotent), refresh() runs on every visit, setSub(sub) receives deep-link tails. A string value instead of a view factory activates the content-first path -- plain HTML styled automatically with zero framework classes.
Gallery
The gallery is a complete tinymoon app that documents every design token and primitive. Serve the repo root with a static server and open /gallery/:
python3 -m http.server
# open http://localhost:8000/gallery/
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file tinymoon-0.4.0.tar.gz.
File metadata
- Download URL: tinymoon-0.4.0.tar.gz
- Upload date:
- Size: 204.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d700649c858ed0a3bdb7a5ae60efa0f0271da90e9ac79a0b7bf242ade460d883
|
|
| MD5 |
20853b0aca55f08cf06f7c2dd8deaf2d
|
|
| BLAKE2b-256 |
4098d95809dbc989d0777f7e70266855de55376f545e205d715bdb237e3c83f3
|
Provenance
The following attestation bundles were made for tinymoon-0.4.0.tar.gz:
Publisher:
publish.yml on smm-h/tinymoon
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
tinymoon-0.4.0.tar.gz -
Subject digest:
d700649c858ed0a3bdb7a5ae60efa0f0271da90e9ac79a0b7bf242ade460d883 - Sigstore transparency entry: 2172737340
- Sigstore integration time:
-
Permalink:
smm-h/tinymoon@34106720188cc6f4161a369a3926b0de2413febd -
Branch / Tag:
refs/tags/v0.4.0 - Owner: https://github.com/smm-h
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@34106720188cc6f4161a369a3926b0de2413febd -
Trigger Event:
release
-
Statement type:
File details
Details for the file tinymoon-0.4.0-py3-none-any.whl.
File metadata
- Download URL: tinymoon-0.4.0-py3-none-any.whl
- Upload date:
- Size: 171.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d5ad023631e5bdb2cac0f1ce3de2c68dd7337eea6f26d01514e524d81f0bfcf5
|
|
| MD5 |
3c537552d794297e0eceaadcf0487ace
|
|
| BLAKE2b-256 |
241f68b9503d7c9b9bc069ffceb1ed8e322ad2357d9dbb222ba5091570fd068d
|
Provenance
The following attestation bundles were made for tinymoon-0.4.0-py3-none-any.whl:
Publisher:
publish.yml on smm-h/tinymoon
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
tinymoon-0.4.0-py3-none-any.whl -
Subject digest:
d5ad023631e5bdb2cac0f1ce3de2c68dd7337eea6f26d01514e524d81f0bfcf5 - Sigstore transparency entry: 2172737386
- Sigstore integration time:
-
Permalink:
smm-h/tinymoon@34106720188cc6f4161a369a3926b0de2413febd -
Branch / Tag:
refs/tags/v0.4.0 - Owner: https://github.com/smm-h
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@34106720188cc6f4161a369a3926b0de2413febd -
Trigger Event:
release
-
Statement type: