@tonycodes-ui/mortar 0.11.0 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/AGENTS.md CHANGED
@@ -7,4 +7,4 @@
7
7
  - Fix shared problems centrally in `src`, with the fixture in `previews` and the contract in `manifests`. Do not copy components or override their styles in a consumer.
8
8
  - Run `npm run check` first. If it reports M007 or M008, review what changed in your diff, then run `npm run catalog`. It prints which components' fingerprints and contracts changed. Never run `npm run catalog` just to make a failing check pass.
9
9
  - Run `npm run test:browser` and `npm run test:visual` before opening a PR, and report the actual results.
10
- - Never approve, merge, publish or bump the version on your own initiative, and never weaken a protected check, policy or workflow to make a change pass.
10
+ - Releases are automatic: the release workflow computes the version from conventional commits when a change merges to main, so never publish, tag or edit the version by hand. Never approve or merge on your own initiative. Never weaken a protected check, policy or workflow to make a change pass.
package/CHANGELOG.md CHANGED
@@ -2,62 +2,31 @@
2
2
 
3
3
  Mortar follows [semantic versioning](https://semver.org/). Before 1.0, a minor release may contain breaking changes, and it says so. Each release is published from its `v<version>` tag to [npmjs.com](https://www.npmjs.com/package/@tonycodes-ui/mortar) with npm provenance. Its GitHub release carries the exact archive and a `provenance.json` with the commit and SHA-512 integrity.
4
4
 
5
- ## Unreleased
5
+ ## 0.13.0 (2026-11-05)
6
6
 
7
7
  ### Added
8
8
 
9
- - **Surfaces in the brand contract.** A brand can set `paper`, `surface` and `sunken` for each appearance (`--m-brand-l-paper|surface|sunken`, `--m-brand-d-paper|surface|sunken`), for example a navy dark page. Omitted, they keep today's values, and inverse bands take them from the other appearance. Mortar's text and status colours stay fixed, so check them against tinted surfaces: the brand builder's contrast table now does ([#59](https://github.com/tonycodes/mortar/issues/59)).
10
- - **Accent gradients.** `accentGradient` per appearance (`--m-brand-{l,d}-accent-gradient`) takes a `linear-`, `radial-` or `conic-gradient()` (or a `repeating-` form) whose stops are colours. It is painted only where a component opts in: Button and ActionLink `variant="accent"`, and `tone="accent"` on CallToAction, Hero and Banner, all with `onAccent` text. Without a gradient these paint the flat accent. The appearance exposes the fill as `--m-accent-fill` (an image, painted over `background-color: var(--m-accent)`) and `--m-inv-accent-fill` for inverse bands. On hover and active the fill takes a white or black wash chosen from the on-accent text (`--m-accent-wash`), so contrast rises instead of falling; browsers without relative colours keep the plain fill. The brand builder checks on-accent text against every stop at rest, hover and active.
11
- - **Heading font.** Headings and component titles (page, section, hero, card, settings, sign-in, auth, post, calendar and stacked-list titles, and prose headings) read `--m-font-heading`. It is unset by default, so headings keep the font they inherit; nothing changes until you set it. A brand can set `fonts: { body, heading }` (`--m-brand-font`, `--m-brand-font-heading`), which apply wherever the brand does, including a container-scoped ThemeProvider. Tailwind gains a `font-heading` utility, and `font-sans` follows a brand's body font.
12
- - `isBrandColour`, `isBrandGradient` and `isBrandFont` in `@tonycodes-ui/mortar/brand` check a value before you save it. Fonts accept bare `var(--name)` entries, as framework font loaders such as `next/font` hand out.
13
- - The brand builder checks strong lines (`--m-line-strong`, control borders) at 3:1 on page, surface and sunken.
9
+ - **DataTable column priority and pinning for narrow screens** ([#77](https://github.com/tonycodes/mortar/issues/77)). `priority: "medium"` hides a column when the table is narrower than 480px and `priority: "low"` below 768px. This is the table's own width (a container query), so a table in a narrow panel collapses as it would on a phone. The first column always shows. A table with priorities keeps its truncated columns shrinking on narrow screens (down to 5rem) so the remaining columns fit instead of scrolling. `pin: "end"` keeps a column, such as row actions, at the trailing edge while the table scrolls sideways; its background follows the row's stripe, hover and selection. Tables without these props are unchanged.
10
+ - **`mortar audit` flags raw HTML elements that a Mortar component replaces** (`raw-element`, [#76](https://github.com/tonycodes/mortar/issues/76)). In an app that uses Mortar, a raw `<table>` (use DataTable or Table), `<select>`, `<textarea>`, `<dialog>`, `<progress>` or `<input>` is an error, and a raw `<button>` is a warning. Each finding names the component to use and gives its file and line. Inputs are matched by `type`, so `hidden`, `file`, `range`, `color` and `image` inputs are not reported. HTML inside strings and template literals is ignored, including concise arrow returns, `case` and `throw`, and nested templates inside `${…}`. A backtick quoted inside `${…}` does not hide a later element. The mapping is read from a new `replaces` field on each component's contract in the catalogue, so new components extend it.
11
+ - **Top navigation for application screens.** All additions are optional, and default markup is unchanged.
12
+ - **Dropdown groups.** `Navbar` `items` and `StackedShell` `navigation` take a labelled group, `{ label, items }`, beside plain links. A group opens a panel of links (each with an optional description, icon and `current`), using the same flyout as MarketingHeader. A group holding the current page is marked current. On narrow screens it opens inside the folded menu, and choosing a link closes the menu.
13
+ - **Bar tone.** `tone` on Navbar and StackedShell can be `"inverse"` (the other appearance, through the inverse remap) or `"accent"` (the brand's accent fill, or its gradient when it defines one). An accent bar has on-accent links and focus, an inverted primary action and outlined secondary ones. Dropdown panels keep the page's colours.
14
+ - **Page tabs.** StackedShell `tabs` (`{ label, items }`) adds a second row of page tabs under the bar, rendered as a TabNav.
15
+ - **Template.** A new application template, "Dashboard, top navigation" (`?recipe=stacked-dashboard`), composes StackedShell with all three.
16
+ - **Split landing template** (`?recipe=split-landing`, [#68](https://github.com/tonycodes/mortar/issues/68)). A one-screen landing page composed only from public components: MarketingPage, a MarketingHeader with a tagline and the consumer's logo in the logo slot, a split Hero with an emphasised title, a Notice as supporting feedback, a NetworkIllustration, and an inline Footer. The CLI and MCP list it with the components it composes. Its browser test checks 320px reflow, 200% zoom, long text, keyboard order and visible focus in both themes, and that the emphasised phrase uses the green brand's link colour in light and dark. It also has a visual baseline.
17
+ - **Docs: marketing surfaces and illustrations** ([theming](docs/theming.md#marketing-surfaces-and-illustrations)). Shows which existing tokens a branded landing page and its SVG read (canvas, text, borders, emphasis, accent and focus), and lists the supported extension points and what is not supported. No new tokens: every role already had one. [Mortar for coding agents](docs/agents.md#the-audit) and the agent skill now say that a clean audit is not proof that a page uses only Mortar components.
14
18
 
15
19
  ### Fixed
16
20
 
17
- - **Brand values are checked as colour syntax.** `brandToStyle`, `brandToCss`, `registerBrand` and `brandTokens` accepted any value made of letters, digits and brackets, so `accent: "linear-gradient(…)"` or `url(…)` passed and then failed silently in the browser. A colour is now hex, a colour name (including `transparent` and `currentcolor`), or `rgb()`, `rgba()`, `hsl()`, `hsla()`, `hwb()`, `lab()`, `lch()`, `oklab()`, `oklch()`, `color()`, `color-mix()`, `light-dark()` or `var()`, with nested functions checked the same way. Anything else throws an error that names the key, such as `dark.accent`.
18
- - **The audit no longer flags brand colours as `raw-colour`.** Colours inside a `brandTokens={…}` value (and a `const` it references in the same file), the arguments of `brandToCss`, `brandToStyle` and `registerBrand`, objects typed as or satisfying `BrandDefinition`, and `--m-brand-*` values are where colours belong. Other colours in the same file are still reported.
21
+ - **Pinned DataTable cells keep the forced-colours highlight.** A selected or hovered pinned cell was specific enough to paint its own background over `Highlight`, so the text stayed `HighlightText` on `--m-selected`. Those backgrounds now sit below the forced-colours rule, and still cover the stripe in ordinary colours.
22
+ - **Switch and Checkbox keep keyboard focus when their description comes and goes** ([#78](https://github.com/tonycodes/mortar/issues/78)). A description set in only one state (for example "Showing all 4 keys" when on) swapped the layout and remounted the input, so Space stopped working. Once a choice has had a description or error it keeps the described layout, and focus follows if the layout changes.
23
+ - **MetricGroup no longer clips long values on phones** ([#81](https://github.com/tonycodes/mortar/issues/81)). Below 560px metrics still sit two to a row, but a value too wide for half a row (such as "Operational" at 320px) takes a row of its own, and a value wider than the row wraps.
24
+ - **SegmentedControl no longer pushes the page sideways** ([#80](https://github.com/tonycodes/mortar/issues/80)). It is never wider than its container: options scroll inside the control, the selected option is scrolled into view on mount and on change, and the focus ring isn't clipped. Apps can drop local overflow wrappers.
25
+ - **Accent navbar sunken contrast.** `Navbar` `tone="accent"` sets `--m-sunken` from `--m-accent-wash`, the same direction as an accent button's hover, so current items, avatars and hovers on the bar stay at least 4.5:1. A wash towards the on-accent text dropped the built-in green light bar to 4.2:1.
19
26
 
20
27
  ### Changed
21
28
 
22
- - **`--m-line-strong` in light is `#8b8b90`** (was `#8e8e93`), and `--m-inv-line-strong` in dark with it. The old value was 2.99:1 on the default sunken surface, just under the 3:1 that control borders need.
23
-
24
- - **Packages are published to npmjs.com.** Install with a plain `npm install @tonycodes-ui/mortar`; no registry configuration, account or token is needed. Releases are published from GitHub Actions with npm trusted publishing (OIDC, no long-lived token) and `--provenance`, so each version on npmjs.com links to the workflow run and commit that built it.
25
- - **The playground moved to GitHub Pages**, at https://mortar.tony.codes/playground/. The site root links the playground, GitHub, npm and the docs. The package `homepage` points there.
26
- - **Everything is free and in this repository.** The hosted Mortar Cloud preview (agent identities, OAuth MCP connections, hosted audits and planned paid plans) is retired. The kit, docs, playground and agent tooling are MIT licensed here, with no accounts, licence checks, telemetry or hosted service. GitHub maintainer review is the only review gate.
27
-
28
- ### Migration
29
-
30
- Versions up to 0.11 of the private-registry package `@tony-ui/mortar` were published only to the previous owner-hosted registry, `https://git.tony.codes/api/packages/tony/npm/`, which will be retired. New versions are on npmjs.com only. To upgrade, remove the scope override and install from npmjs.com:
31
-
32
- ```sh
33
- npm config delete @tony-ui:registry --location=project
34
- npm install @tonycodes-ui/mortar@latest
35
- ```
36
-
37
- A project that must stay on 0.11 or earlier keeps its scope override (`@tony-ui:registry=https://git.tony.codes/api/packages/tony/npm/` in `.npmrc`) while that registry remains available, or installs the archive attached to the version's [GitHub release](https://github.com/tonycodes/mortar/releases).
38
-
39
- ## Unreleased
40
-
41
- ### Added
42
-
43
- - **Open agent tooling in the package: an MCP server and the `mortar` CLI.** They read the installed version's catalogue, contracts, examples and guides, run locally, work offline and need no account. They add no runtime dependencies. See [Mortar for coding agents](https://github.com/tonycodes/mortar/blob/main/docs/agents.md).
44
- - `mortar mcp` is a stdio MCP server (`claude mcp add mortar -- npx -y @tonycodes-ui/mortar mcp`) with six tools: `search_components`, `get_component` (props with types and defaults, usage notes, dependencies, marker, tested states and a JSX example), `get_build_packet` (for component names or a free-text brief: the dependency closure, imports, CSS and ThemeProvider setup, design principles, API conventions and matching page templates), `get_guide`, `list_kits` and `audit_app`.
45
- - `mortar search`, `component`, `packet`, `audit`, `guide` and `kits` give the same answers on the command line, with `--json`.
46
- - `mortar audit [path]` is a read-only static audit of an application: version range, imports and props that the installed version does not have (with 0.6 rename suggestions), invalid prop values, CSS overriding `.m-*` classes or `[data-m-component]`, raw colours beside Mortar components, a missing `styles.css` import or ThemeProvider, deep imports and local copies of Mortar source. It exits 1 when it finds errors.
47
- - An agent skill, `skills/mortar/SKILL.md` (`npx mortar skill`), with the workflow defaults: search, packet, compose, audit, and contribute by pull request.
48
- - The package now also ships `AGENTS.md`, `CONTRIBUTING.md`, `TESTING.md` and the guides in `docs/`, which the tools read.
49
-
50
- ## Unreleased
51
-
52
- ### Added
53
-
54
- - **`ErrorSummary`** (Foundation › Form controls): the problems that stopped a form submitting, listed above the form with a link to each field. Props: `errors` (`{ message; href?; target? }[]`; renders nothing when empty), `title` (default "There is a problem"), `headingLevel` (default 2), `autoFocus` (default true), `submitCount`, `id` and `className`. It is a `role="alert"` region named by its heading with `tabIndex={-1}`: it takes focus when it appears and whenever `submitCount` changes, but not while a fixed field's error clears. A link focuses its field without changing the URL hash; for a group such as a RadioGroup the checked or first radio takes focus, and the label or legend scrolls into view.
55
- - **Checkbox `error`**: the message is announced when it appears (`role="alert"`), the box is `aria-invalid` and `aria-describedby` links the description and the error. `required` adds the decorative `*` after the label, as on Input and Select. Without these props the markup is unchanged.
56
- - **RadioGroup `error`, `hint`, `required` and `id`**: the hint or error sits under the legend and describes the fieldset; with an error each radio is `aria-invalid`, and `required` marks every radio and adds `*` to the legend. Without these props the markup is unchanged.
57
-
58
- ### Changed
59
-
60
- - The sign-up and other auth page templates use `ErrorSummary` and `Checkbox error` instead of a local summary and a hand-wired terms error. The terms checkbox is now `required`.
29
+ - **Brands under a strict Content Security Policy are documented** ([#82](https://github.com/tonycodes/mortar/issues/82)). `brandTokens` and `registerBrand` inject an inline `<style>`, which `style-src 'self'` blocks, silently dropping the brand. `docs/theming.md` now shows the CSP-safe path: generate the rule with `brandToCss` at build time, import it after Mortar's styles, and pass ThemeProvider the brand name only. It also covers the options for runtime tenant brands (`'unsafe-inline'`, a hash, or serving each tenant's rule as a stylesheet).
61
30
 
62
31
  ## 0.11.0 (2026-10-20)
63
32
 
package/CONTRIBUTING.md CHANGED
@@ -8,10 +8,22 @@ Use src for central implementations, previews for fixtures and manifests for con
8
8
 
9
9
  Baseline rules are versioned in baseline-policy.json. CI must use the same rules but contributors cannot authorize weakening protected checks; policy, workflow and dependency changes require maintainer review. Branch protection requires successful checks. The solo maintainer reviews and authorizes merges; a second GitHub approval is not required. Public PR runners must be isolated from release credentials and production networks.
10
10
 
11
- Releases are automatic and version-driven. To release, merge a PR that bumps the version with `npm version <x.y.z> --no-git-tag-version`. `npm run check` then fails with M008 until `npm run catalog` stamps the new version onto every catalogue entry (tests/versioning.test.mjs enforces they match). The same PR renames CHANGELOG.md's `## Unreleased` section to `## <x.y.z> (<date>)`. That section becomes the GitHub release notes; a test requires it, and a release without one stops before tagging. Breaking changes before 1.0 also get a migration guide in docs/, such as [Migrating to 0.6](docs/migrating-to-0.6.md). After the required checks pass on that merge commit, the Release package workflow (.github/workflows/release.yml) acts only if the version is neither tagged on another commit nor already on npmjs.com. Its credential-free prepare job builds the exact commit with npm run release:prepare and checks the provenance record. Its publish job runs in the protected npm-release environment and never checks out or runs repository code. It holds no registry secret: it authenticates with npm trusted publishing, exchanging a short-lived GitHub OIDC token (`id-token: write`) for a short-lived publish credential, and only a job in this repository, workflow file and environment can do that. It re-verifies the archive and creates the immutable v<version> tag on that commit. It then creates the GitHub release with the archive and provenance.json, publishes that exact archive to npmjs.com with `npm publish --provenance`, and confirms (allowing for registry propagation) that npmjs.com serves the integrity recorded in the provenance. The provenance attestation on npmjs.com links the version to the workflow run that built it. Merges that do not change the version publish nothing. A version is never overwritten: an existing tag on another commit stops the release, and a rerun publishes the archive already attached to the GitHub release. Prerelease versions such as 1.0.0-rc.1 are published under the next dist-tag. The maintainer's merge of the version bump is the release authorization; agents must not open or merge version bumps on their own initiative.
11
+ Every merge to main releases automatically after its contribution checks pass, unless the run is superseded or already released. Use conventional commit subjects: `feat` increments minor, and other changes increment patch. Breaking changes use `!` after the type or scope, or a `BREAKING CHANGE:` footer. Before 1.0, breaking changes increment minor; at 1.0 and above they increment major. Include a migration guide for breaking API changes.
12
+
13
+ Tags and GitHub releases are the sources of truth for released versions. The workflow never commits generated changes to main. Contributors add notes under `## Unreleased`. The `CHANGELOG.md` on `main` keeps a `## Unreleased` section by design: promotion to a version heading happens only in the prepare job and is never committed, so the file on `main` does not list releases made after 0.11 and the GitHub release notes are the record for those; the credential-free prepare job computes the version from the latest stable tag and full commit messages, updates package metadata and the catalogue, and promotes new changelog entries. If no new entries remain, it generates grouped notes from commits. It runs release checks and records the checked commit and archive integrity in provenance.json.
14
+
15
+ The serialized publish job uses the protected npm-release environment and npm trusted publishing through OIDC. It never checks out repository code. It verifies the prepared archive, creates the immutable tag and GitHub release, publishes the exact archive with provenance, and verifies registry integrity. A commit that is already tagged and published is skipped. If a commit is tagged but its version never reached npm, the run reuses that tag and publishes the missing version. A version that is on npm without a tag fails the run so it gets fixed. Superseded runs leave the latest main run to release the accumulated changes; runs queue one at a time and only the newest pending run is kept.
12
16
 
13
17
  Versions up to 0.11 were published to the previous owner-hosted registry (see the CHANGELOG.md migration note); new releases go to npmjs.com.
14
18
 
19
+ ### Manual recovery
20
+
21
+ The automatic release never rewrites or deletes a tag, and it never commits to main, so two failures need a maintainer.
22
+
23
+ **A stranded tag (publish failed and main moved on before the re-run).** The run creates the tag and GitHub release, then publishes to npm. If the publish fails and another commit lands on main before the failed run is re-run, the re-run is superseded (it is no longer the head of main) and does not resume. The next run on the newest main releases the accumulated commits as the following version (for example a stranded `v0.12.0` is followed by `v0.12.1`), so nothing is lost, but the stranded version has a tag and a GitHub release and no npm package. Nothing blocks releases, so the usual recovery is to edit the stranded GitHub release notes to say that the version was never published and point at the next one. Do not delete or move the tag. If the stranded version itself must exist on npm, check out its tag on a maintainer machine and publish the archive that `npm pack` writes for it (with 2FA), as in the bootstrap step below. To let the pipeline resume instead, re-run the failed run before anything else merges to main; while the tagged commit is still the head of main the re-run reuses the tag and publishes the missing version.
24
+
25
+ **A version already on npm with no release tag.** If the computed next version is on npm but its `v<version>` tag does not exist (for example someone published by hand), every run fails with `::error::Version <version> is already on npm but v<version> has no release tag`. The run stays red until someone tags it; nothing is published in the meantime. Find the commit that version was published from (the `gitHead` in `npm view @tonycodes-ui/mortar@<version> gitHead`, or the package provenance) and create the tag on it, either in the GitHub UI (Releases, Draft a new release, choose that commit and the tag `v<version>`) or with `gh api repos/tonycodes/mortar/git/refs -f ref=refs/tags/v<version> -f sha=<commit>`. Then re-run the failed workflow run: the planner sees the tag and the published version. After the recovery tag is pushed, a re-run releases the next version only if there are newer commits after the tagged one; otherwise it skips. Never force-move an existing tag.
26
+
15
27
  ### Maintainer setup
16
28
 
17
29
  This is one-time configuration in npmjs.com and the GitHub repository settings; no secret is created.
@@ -45,4 +57,4 @@ Search by component, kit or category name. On narrow screens, Menu opens the sam
45
57
 
46
58
  ## Automatic playground deployment
47
59
 
48
- Merges to `main` automatically deploy the GitHub Pages site at https://mortar.tony.codes/ (the repository's Pages custom domain; DNS is a CNAME from `mortar.tony.codes` to `tonycodes.github.io`, not proxied), with the playground at https://mortar.tony.codes/playground/, after the required checks pass on the merged commit. On a push to `main`, the checks workflow builds the site with no deployment credential: `site/index.html` at the root and the playground under `playground/`, built with a relative base (`--base ./`) so it also works on a custom domain. The separate Deploy playground workflow (.github/workflows/deploy-playground.yml) downloads that exact artifact, skips superseded commits, and deploys it with `actions/deploy-pages` in the `github-pages` environment; it never checks out or runs repository code. Failed checks leave the current site unchanged. This does not publish a package; only a merged version bump does (see above).
60
+ Merges to `main` automatically deploy the GitHub Pages site at https://mortar.tony.codes/ (the repository's Pages custom domain; DNS is a CNAME from `mortar.tony.codes` to `tonycodes.github.io`, not proxied), with the playground at https://mortar.tony.codes/playground/, after the required checks pass on the merged commit. On a push to `main`, the checks workflow builds the site with no deployment credential: `site/index.html` at the root and the playground under `playground/`, built with a relative base (`--base ./`) so it also works on a custom domain. The separate Deploy playground workflow (.github/workflows/deploy-playground.yml) downloads that exact artifact, skips superseded commits, and deploys it with `actions/deploy-pages` in the `github-pages` environment; it never checks out or runs repository code. Failed checks leave the current site unchanged.
package/DESIGN.md CHANGED
@@ -27,7 +27,7 @@ Every component follows these rules. New props must fit them, and the code revie
27
27
  - Tables align columns with `start | end`, not `left | right`.
28
28
  - **Tone.**
29
29
  - Status uses `Tone` (`neutral | info | success | warning | danger`).
30
- - Section surfaces use `tone: "default" | "muted" | "inverse"`. Promotional bands (CallToAction, Hero and Banner) also take `"accent"`: the brand's accent fill, which is its accent gradient when it defines one, with on-accent text. Button and ActionLink take the same fill as `variant="accent"`. Each component offers only the tones it styles.
30
+ - Section surfaces use `tone: "default" | "muted" | "inverse"`. Promotional bands (CallToAction, Hero and Banner) also take `"accent"`: the brand's accent fill, which is its accent gradient when it defines one, with on-accent text. Button and ActionLink take the same fill as `variant="accent"`. The application bar (Navbar, and StackedShell through it) takes `tone: "default" | "inverse" | "accent"`; it has no muted tone. Each component offers only the tones it styles.
31
31
  - A destructive action is `tone="danger"`, and a destructive item in a list is `danger: true`.
32
32
  - Tone renders as `data-tone`.
33
33
  - **State.** Busy work is `loading`. Empty content is `empty?: { title; description?; action? }`. Errors are `error` (string).
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  A shared design foundation and purpose-built Application UI, Marketing UI, Docs UI and Ecommerce kits for React 19 and Tailwind CSS 4. MIT licensed. No account, token, registry connection or subscription is required to use the kit, including use by coding agents.
4
4
 
5
- The package is [`@tonycodes-ui/mortar` on npmjs.com](https://www.npmjs.com/package/@tonycodes-ui/mortar). Releases are immutable, publicly installable without any account or token, and published with npm provenance that links each version to the GitHub Actions run that built it. Publishing is automatic and version-driven: merging a version bump to `main` publishes that version once the required checks pass ([release workflow](.github/workflows/release.yml), [process](CONTRIBUTING.md)). The current version is listed on the [GitHub releases](https://github.com/tonycodes/mortar/releases) page. Source, issues and contributions live at [github.com/tonycodes/mortar](https://github.com/tonycodes/mortar).
5
+ The package is [`@tonycodes-ui/mortar` on npmjs.com](https://www.npmjs.com/package/@tonycodes-ui/mortar). Releases are immutable, publicly installable without any account or token, and published with npm provenance that links each version to the GitHub Actions run that built it. Publishing is automatic: every merge to `main` auto-releases the next version, worked out from conventional commits, once the required checks pass ([release workflow](.github/workflows/release.yml), [process](CONTRIBUTING.md)). The current version is listed on the [GitHub releases](https://github.com/tonycodes/mortar/releases) page. Source, issues and contributions live at [github.com/tonycodes/mortar](https://github.com/tonycodes/mortar).
6
6
 
7
7
  [Explore the live playground](https://mortar.tony.codes/playground/) · [Contribute](CONTRIBUTING.md) · [Free and open](#free-and-open)
8
8