Source Reference

The repository keeps design values, semantic styles, optional components, and site adapters in separate files. The library build uses only Node.js built-ins. Documentation uses Hugo; the npm package still contains no runtime dependencies.

The independently installable adapters live in packages/sphinx, packages/quarto, and packages/hugo. scripts/adapters.mjs stages and checks the exact core CSS, version metadata, digest, and license notices. scripts/adapter-styles.mjs enforces style ownership without reading the token source. scripts/pack-adapters.mjs packages verified bundles, and scripts/check-adapter-artifacts.py checks the assets inside the distributions.

Run npm run build:adapters to build the core and prepare adapters, then npm run check:adapters to detect stale or modified bundles. These scripts propagate missing-file, style-contract, and packaging errors. Importing prepareAdapters({ directory, check }) or packAdapters({ directory }) has no build side effects; both default to this checkout and return Promise<void>.

Source Files

File Responsibility
src/tokens.json Canonical color, font, line-height, heading, spacing, border, shadow, and focus values
src/base.css Semantic element styles, native control states, keyboard focus, media sizing, and forced-color rules
src/components.css The six optional unbloat- appearance classes
scripts/build.mjs Token validation, CSS and Sass generation, output checks, and reproducibility checks
scripts/serve.mjs Loopback reference server with fixed routes and restrictive response headers
scripts/project-docs.mjs Per-project dev/stable builds, native Sphinx sources, package guide sites, and artifact manifests
scripts/docs.mjs Documentation asset preparation, verified Hugo setup, builds, and local servers
scripts/latex-tokens.mjs Validated CSS-to-TeX token translation, length mapping, and core digest for the LaTeX adapter
scripts/adapters.mjs Exact adapter CSS, core provenance, and license staging/checking
scripts/adapter-styles.mjs Ownership validation for authored CSS, compiled SCSS, and template styles
scripts/pack-adapters.mjs Verified wheel, source distribution, Quarto/LaTeX archives, and Hugo tarball creation
scripts/check-adapter-artifacts.py Read-only validation of core bytes, provenance, and licenses inside all five archives
scripts/docs-artifact.mjs Existing static artifact directory and deployment-prefix discovery
scripts/serve-docs.mjs Loopback serving of an existing artifact without rebuilding it
examples/index.html Reference markup for manual inspection and library browser tests
examples/example.css Reference-page layout and skip-link adapter
docs/layouts/ Hugo page shell, navigation, Markdown links, headings, and version footer
docs/assets/site.css Centered page, black masthead, navigation buttons, prose styles, preview spacing, and table scrolling
docs/assets/tabs.js Example tab selection, keyboard focus, and panel visibility

The Components pages describe the CSS selectors and their states, and Design Tokens documents the JSON schema and defaults.

Each adapter has its own documentation project, registered in docs/projects.json. npm run docs:project -- <project> <dev|stable> builds a publishable artifact; DOCS_ARTIFACT_DIR=.cache/project-docs/<project>/<channel> npm run test:project-docs validates its prefix, links, downloads, bundled core, and layout without rebuilding. See Release Setup for destinations.

Adapter APIs and Packages

All JavaScript build modules can be imported without running their CLI entry points. Async operations propagate validation, filesystem, or subprocess errors unless the HTTP handler converts them into responses.

API Contract
prepareAdapters({ directory, check } = {}) Returns Promise<void>. Reads the current core build, validates adapter styles and references, and stages CSS/provenance/notices. check: true compares existing files without writing.
assertAdapterStyle(source, { from, core } = {}) Returns undefined or throws a source-labelled PostCSS error. Rejects independent design values and core overrides. Optional built core text enables undefined-variable checks.
checkAdapterStyles({ directory, core } = {}) Returns Promise<void>. Reads authored adapter styles, compiles SCSS in memory, and checks inline template declarations. It skips generated core copies and writes no sources.
renderLatexTokens(css) Returns generated unbloated-tokens.sty as a string from the built core CSS. Validates the token block before emitting TeX. No filesystem side effects.
packAdapters({ directory } = {}) Returns Promise<void>. Checks bundles before invoking tar, npm, and Python. Writes five distributions to .cache/artifacts/; a later failure can leave earlier archives in place.

The adapter directory arguments default to this checkout’s root and accept directory URLs with trailing slashes. adapters.mjs also exports root and an adapters map containing each platform’s relative CSS asset directory. The ownership checker is a contributor check, not a CSS sandbox or accessibility certification. See the repository’s scripts/README.md for CLI and environment contracts.

pack-adapters.mjs requires tar, npm, and Python with the build frontend. Set PYTHON to an absolute executable path when selecting a virtual environment: Python packaging runs from packages/sphinx. The archive checker uses only Python’s standard library. It requires exactly one distribution of each supported type under .cache/artifacts/; remove old versions before validating a new set.

Package Source and Consumer Contract
packages/sphinx Python theme entry point, toctree navigation, Jinja templates, AutoAPI summaries, and version-feed enhancement. Requires Python 3.12 and Sphinx 9.0 or newer. get_autoapi_templates_dir() returns the installed template path; setup(app) registers the theme and returns Sphinx extension metadata.
packages/hugo Go layouts, shortcodes, search, metadata, discovery rules, and CSS processing. Requires Hugo Extended 0.144.1 or newer and a host PostCSS toolchain. Its README lists site parameters, every shortcode input, and optional CDN scripts.
packages/quarto Reveal.js format manifest, SCSS layout, native footer include, and a slide gallery. Its README covers local installation, generated assets, image limits, and output verification.
packages/latex LuaLaTeX adapter with generated CSS-derived TeX styles, exact core CSS, provenance, licenses, an example, and compilation checks. Its README documents TeX dependencies, inherited book options, theorem environments, listings, and build limits. Its class applies translated core colors, typography, spacing, heading sizes, and borders.

All adapters use generated core bundles; consuming projects do not need Node at runtime. LaTeX loads its generated TeX style, with mappings documented in its package guide. Adapter versions remain independent from the core version in provenance. The current Quarto archive filename uses the core version; read _extension.yml for its extension version. The monorepo workflows upload adapter packages without automatically publishing them to registries.

Build API

Import build functions from scripts/build.mjs. Importing the module does not write files.

import { build, checkBuild, render, validateTokens } from "./scripts/build.mjs";

await build();
await checkBuild();
const files = await render();

root

A URL pointing to the repository root. Build paths resolve relative to this URL, so generation does not depend on the shell’s working directory.

validateTokens(tokens)

Accepts parsed JSON and returns the same object after validation. It requires the eight supported groups, safe token names and values, and a zero border radius. It throws when a constraint fails. It does not clone the input or write files. See Validation Rules.

render()

Reads the canonical JSON and both CSS sources. It returns a Promise<Map<string, string>> with tokens.css, _tokens.scss, base.css, components.css, and unbloat.css. Every output includes the license banner. Unknown unbloat- token references in the shared CSS cause an error. Source-read and validation errors propagate to the caller. The function does not write files.

build({ directory } = {})

Writes the rendered files into directory, which defaults to dist/. It creates the directory if needed. After writing, it rejects any unexpected file in the output directory. It returns Promise<void>; validation, read, write, and output-check errors propagate to the caller.

await build({ directory: "/tmp/unbloated-output" });

checkBuild()

Builds twice in independent temporary directories and compares filenames and contents byte for byte. It returns Promise<void> and removes the temporary directory in a finally block. It throws if outputs differ or a build fails. It does not require or update an existing dist/.

The CLI accepts node scripts/build.mjs to write the distribution and node scripts/build.mjs --check to check reproducibility. Other arguments fail.

Reference Server

scripts/serve.mjs binds to 127.0.0.1:4173. It serves the reference page, its adapter stylesheet, and the four generated CSS files. Root and example-page aliases keep relative stylesheet links valid. Requests never become filesystem paths; a fixed route map resolves them.

Only GET and HEAD are accepted. Unknown routes and other methods return 404. Malformed URLs return 400 and the server continues handling requests. File-read failures return 500. Responses include X-Content-Type-Options: nosniff and a Content Security Policy that permits local styles and denies scripts, framing, and form submission. SIGINT and SIGTERM close the listener.

handlePreviewRequest(request, response) accepts Node’s incoming request and server response and returns Promise<void>. Importing it does not open a socket.

Documentation Runner

prepareDocs() builds the library and copies dist/unbloat.css to docs/assets/unbloat.css. It returns Promise<void> and propagates filesystem and build errors. The copy is generated and ignored by Git.

prepareDocs() also writes the package version and description to docs/data/package.json. Hugo fingerprints the CSS and tab script, so asset URLs change when their contents change. The footer displays the package version.

setupHugo() installs the locked Linux x64 or arm64 binary into .cache/hugo/. It fetches the official release archive, verifies the committed SHA-256, and extracts only hugo. Download, checksum, and extraction failures propagate. Other platforms use a manually installed binary.

runDocs(mode, extraArgs) accepts setup, build, serve, or preview. Setup installs Hugo. The other modes validate the Hugo version and prepare the stylesheet and package data. Build writes site/ and treats warnings as failures. Serve and preview run Hugo’s loopback server on ports 8000 and 4174. Serve watches Markdown and adapter assets with live reload; preview disables watching and live reload. Restart either command after changing shared CSS or tokens.

The function returns the child process exit code, or 1 when terminated. It rejects unsupported modes, version mismatches, and process-start errors. HUGO_BIN overrides the executable; otherwise the runner uses .cache/hugo/hugo, then Hugo on PATH. Commands use argument arrays without a shell. Additional arguments pass through to Hugo, including a deployment base URL:

npm run docs:build -- --baseURL https://example.org/unbloated/

The link render hook resolves Markdown file links to Hugo pages and rejects missing pages or heading fragments. Heading IDs remain available for links; headings have no permalink controls. Trusted repository HTML is enabled for live examples, and syntax highlighting adds no inline colors.

npm run test:docs serves the existing site/ artifact with scripts/serve-docs.mjs, without Hugo or source generation. It infers the deployment prefix from the built core stylesheet URL and serves files only under that prefix. DOCS_ARTIFACT_DIR selects another downloaded artifact directory. Both stable and versioned release artifacts are checked; docs:preview remains a source preview. The static server accepts GET/HEAD, returns 400 for malformed URLs and 404 for missing/outside-prefix paths, and preserves the uploaded bytes.

docsArtifact(artifactDirectory?) returns Promise<{ directory: URL, prefix: string }>; its argument defaults to DOCS_ARTIFACT_DIR or site and resolves relative to the caller’s working directory. Missing HTML or a missing fingerprinted core stylesheet reference rejects the promise. It does not check or regenerate source files.

documentationHandler({ directory, prefix }) returns an async Node HTTP request listener for trusted local artifact output without opening a socket. It preserves GET/HEAD bytes under that prefix, returns 404 for other methods or missing paths, and returns 500 for other file-read errors. It sends nosniff without the reference server’s script-blocking CSP.

The tab adapter in docs/assets/tabs.js applies automatic activation to the documentation’s marked tablist. It updates aria-selected, the focusable tab, and panel visibility together after validating all panels and registering handlers. Both panels remain visible if enhancement is unavailable. Click, Left Arrow, Right Arrow, Home, and End change the active tab. See Tabs for the markup contract.

Repository Configuration

Optional tool configs live in config/. See the repository’s config/README.md for hook migration, formatter editor settings, and output locations. Discovery files remain at the root for their tools and editors.

File Responsibility
package.json and package-lock.json Package exports, published files, npm scripts, and exact development dependencies
.npmrc and .nvmrc Disable implicit lifecycle scripts and select the Node.js development version
.editorconfig and config/prettierignore Text conventions and formatter exclusions
eslint.config.mjs Recommended JavaScript checks, Node and browser globals, and dynamic-evaluation restrictions
biome.json CSS lint rules for shared styles, examples, and documentation adapters
config/pre-commit.yaml File checks, formatting, linting, workflow validation, secret scans, builds, and push audits
config/gitleaks.toml Default secret-detection rules with dependency and generated-output exclusions
docs/hugo.yaml Hugo settings, output, trusted HTML, and code rendering
docs/data/social.json Header social destinations, accessible names, and local icon selection
docs/static/licenses/font-awesome-LICENSE.txt Attribution and license for the documentation icons
docs/data/navigation.yaml Main sections, overview pages, and child-page links
docs/hugo-lock.json Hugo version and verified Linux release archive checksums
config/playwright/browser.config.mjs Library browser projects and the reference server
config/playwright/docs.config.mjs Documentation browser checks and the built-site preview server
config/playwright/adapters.config.mjs Real-core Sphinx and Quarto consumer browser checks
config/playwright/projects.config.mjs Published project documentation artifacts and their preview server
.github/workflows/ Pull-request checks, daily checks, package builds, and tag-triggered publication
.github/dependabot.yml Dependency updates and seven-day cooldowns
.github/CODEOWNERS Repository review ownership
.gitignore Generated CSS, tarballs, test output, documentation output, and local environments

Verification

Command What It Checks
npm run format:check Prettier formatting for supported source and documentation files
npm run lint JavaScript and CSS linting, including documentation adapters
npm run build:check Token validation and independent reproducible builds
npm test Build output, unsafe tokens, source-only packaging, exports, licenses, Sass, and contrast
npm run test:browser Library styling, focus, native controls, mobile layout, and reference-server routes
npm run docs:build Hugo build, warnings, navigation, internal links, and heading references
npm run test:docs Built-site navigation, live component styles, tab keyboard behavior, and narrow layouts
npm run build:adapters Core generation, ownership validation, and adapter staging
npm run check:adapters Exact CSS bytes, provenance, licenses, and known core references
npm run test:adapters Bundle contracts and real distribution builders in isolated fixtures
npm run pack:adapters Validated adapter archives in .cache/artifacts/
python3 scripts/check-adapter-artifacts.py Exact bundled bytes and metadata inside all five archives
npm run test:adapter-browser Generated Sphinx and Quarto consumers; requires their builders, staged bundles, and browsers
npm run hooks:check Commit-stage file, style, workflow, secret, and build checks
pre-commit run --config config/pre-commit.yaml --all-files --hook-stage pre-push Push-stage file checks and npm dependency audit

Library unit tests live in tests/build.test.mjs and tests/package.test.mjs. Library browser tests live in tests/browser/style.spec.mjs. Documentation browser tests live in tests/docs/site.spec.mjs. Run npm run build before library tests and npm run docs:build before documentation browser tests.

Documentation Conventions

The repository’s src/README.md, scripts/README.md, and packages/README.md provide local entry points. Keep their ownership maps and package guides aligned with public exports, commands, and consumer behavior. Document token schema and defaults in Design Tokens, not JSON comments.

Use JSDoc for JavaScript module APIs and NumPy-style docstrings for Python APIs. Describe accepted inputs, results, side effects, and errors that callers need to handle. Keep implementation comments for constraints or decisions that the code does not explain. Document named shortcode inputs and trusted-HTML options at the consuming package boundary. Preserve licensed third-party templates, verbatim samples, and attribution.

Use Title Case headings, meaningful link labels, and executable examples with explicit working directories and prerequisites. Record incomplete acceptance checks in the contributor guide; documenting a feature does not establish passing regression coverage.

Change the Documentation

Edit the page layout in docs/layouts/ and docs/assets/site.css. The site uses a centered white page on a gray background, a black masthead and footer, and square navigation links. Configure the header’s Email, GitHub, LinkedIn, YouTube, and RSS links in docs/data/social.json. Their local SVG icons retain Font Awesome attribution; the generated site includes the license in licenses/font-awesome-LICENSE.txt.

Section overviews show their child buttons below the title. Child pages show a linked parent title and sibling buttons above the page heading, with the current page highlighted. A Back to link returns to the section overview, or Home from an overview. The markup works without JavaScript. Prose headings use uppercase black labels; these overrides exclude the live component previews, which use the library’s styles.

Each page has one h1 from its front matter title. Start Markdown sections with ## and nest subsections without skipping levels. Heading sizes use the matching library tokens from h1 through h6. Parent navigation labels are smaller than the page title, and inline code inherits the heading’s size and contrast.

Edit Markdown in docs/content/. Keep source comments, token tables, and component examples consistent with behavior changes. Run npm run docs:build to catch broken links and missing anchors, then npm run test:docs to check the live examples. The build writes site/; deploy that directory with your static host.

To update Hugo, change docs/hugo-lock.json with the version and checksums from the official release, run npm run docs:setup, and update the documented version. Use the same Hugo version locally and in CI. The template and layout adapter load the current library build; component previews retain the shared styles.