SemText

Platform

Extend the vocabulary

SemText is a vocabulary, not a framework: an element is a custom element with an agreed contract, and a namespace is a set of them plus a loader. Nothing in the core needs to change for <yelp-sem-org-chart> or <yelp-sem-system-status> to work in a served document. Two paths below — keep your vocabulary private, or propose it upstream.

Your own vocabulary, today

Five files, generated for your namespace below. Each step says what an agent should produce or check; the wizard fills in the artifacts.

Name the namespace and its elements

A namespace prefix keeps your tags out of the core's way: yelp-sem-org-chart, not sem-org-chart. Custom-element names must contain a hyphen and stay lowercase — the generator validates and prefixes for you.

Generate the scaffold

The vocabulary base (a block box per element before any definition, so the document presents without script), one light-DOM element per tag, the manifest, the loader, and a demo page. Copy each file out as generated.

The generator needs JavaScript. The shape it produces is the spec's hosted-lazy contract ( spec/conventions.md §7): a CSS base, one light-DOM element per tag, a manifest, and a loader that imports only the chunks the page uses.

Tags are validated as custom-element names and prefixed with the namespace unless they already carry it. Currently:<yelp-sem-org-chart> <yelp-sem-system-status>

yelp-sem.css · generated
/* yelp-sem.css — vocabulary base for the yelp-sem namespace.
* One rule per element: a block box before any definition, so the
* document presents before (and without) the loader — the same
* presentation-without-script contract as the core's semtext.css
* (spec/conventions.md §6).
*/
yelp-sem-org-chart:not(:defined) { display: block; }
yelp-sem-system-status:not(:defined) { display: block; }
/* Tokens a host theme can override. */
:root {
--yelp-sem-accent: #7c3aed;
--yelp-sem-surface: #0f172a;
--yelp-sem-ink: #e2e8f0;
}
elements/yelp-sem-org-chart.js · generated
/* elements/yelp-sem-org-chart.js — the <yelp-sem-org-chart> element.
* Light DOM on purpose: the authored content stays searchable, copyable
* and extractable — the same rule the core vocabulary follows
* (spec/conventions.md §4 rule 5).
*/
class SemYelpSemOrgChart extends HTMLElement {
connectedCallback() {
if (this.dataset.upgraded) return;
this.dataset.upgraded = "true";
// Read the authored children and render the widget in place.
// this.querySelector(...) / this.textContent ...
}
}
customElements.define("yelp-sem-org-chart", SemYelpSemOrgChart);
elements/yelp-sem-system-status.js · generated
/* elements/yelp-sem-system-status.js — the <yelp-sem-system-status> element.
* Light DOM on purpose: the authored content stays searchable, copyable
* and extractable — the same rule the core vocabulary follows
* (spec/conventions.md §4 rule 5).
*/
class SemYelpSemSystemStatus extends HTMLElement {
connectedCallback() {
if (this.dataset.upgraded) return;
this.dataset.upgraded = "true";
// Read the authored children and render the widget in place.
// this.querySelector(...) / this.textContent ...
}
}
customElements.define("yelp-sem-system-status", SemYelpSemSystemStatus);
yelp-sem-manifest.json · generated
{
"yelp-sem-org-chart": "./elements/yelp-sem-org-chart.js",
"yelp-sem-system-status": "./elements/yelp-sem-system-status.js"
}
yelp-sem-lazy.js · generated
/* yelp-sem-lazy.js — namespace loader for the yelp-sem vocabulary.
* The spec's "Hosted lazy" contract (spec/conventions.md §7) applied to a
* foreign namespace: scan the document for this namespace's tags, import
* only the chunks those tags need, mark what upgraded, and report a
* failed chunk as a role="alert" note beside its elements — never only in
* the console. Components not on the page never download.
*/
const MANIFEST = {
"yelp-sem-org-chart": "./elements/yelp-sem-org-chart.js",
"yelp-sem-system-status": "./elements/yelp-sem-system-status.js",
};
const MARK = "data-yelp-sem-upgraded";
function reportFailure(tag, error, first) {
const note = document.createElement("p");
note.setAttribute("role", "alert");
note.style.whiteSpace = "pre-wrap";
note.textContent = tag + ": upgrade failed — " + error.message;
first.parentNode.insertBefore(note, first);
}
async function upgrade(root) {
root = root || document;
for (const tag of Object.keys(MANIFEST)) {
const found = root.querySelectorAll(tag);
if (found.length === 0) continue;
try {
await import(MANIFEST[tag]);
for (const el of found) el.setAttribute(MARK, "");
} catch (error) {
reportFailure(tag, error, found[0]);
}
}
}
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", function () { upgrade(); });
} else {
upgrade();
}
demo.html · generated
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>yelp-sem demo</title>
<link rel="stylesheet" href="./yelp-sem.css">
<!-- The loader scans and imports only what this page uses.
Server-hosted only: dynamic import needs a server, so this page
is not the portable single-file form (spec/conventions.md §7). -->
<script src="./yelp-sem-lazy.js"></script>
</head>
<body>
<yelp-sem-org-chart>
…authored content…
</yelp-sem-org-chart>
<yelp-sem-system-status>
…authored content…
</yelp-sem-system-status>
</body>
</html>

Host it and load the loader

Serve the directory over HTTP(S) and put one <script src="./your-ns-lazy.js"> on the page. The loader scans the document for its namespace's tags and imports only the chunks those tags need — components not on the page never download, and a failed chunk degrades only its own elements to authored prose with a role="alert" note. That is the spec's hosted-lazy contract (§7) applied to your namespace.

Verify the degradation

Load the page with scripts off: every element shows its authored content, nothing hidden, the record intact. Then load it with scripts on and confirm the upgrade markers land. A vocabulary that fails either check is not done.

Propose it upstream

A vocabulary earns a place in the core when the need is shared. The proposal needs the element's name, a one-line summary, why the existing vocabulary cannot express it, its options, and a worked example — the drafter shapes that into a prefilled issue on noizu-labs-ml/npl-enhanced-content, the spec's repository.

The proposal drafter needs JavaScript. Proposals are vocabulary additions to the SemText spec — the element's name, what it is, why it cannot be authored with the existing vocabulary, its options, and a worked example.
Name, summary and motivation make the draft submittable. Blank issue instead

Submitting publishes the proposal to the review queue on proposals.semtext.dev; the GitHub-issue link stays as a fallback for anything the form cannot carry (very long examples, say).