Plain JS typography template for the MTE plugin. A set of tiny, pure functions that turn props into HTML strings for newsletters.
Repo: _trying_simple_tags · Package global (browser): newsletterLayoutsTypographyPlainJS
More details: template docs
If you are an AI coding agent (Claude Code, Codex, Cursor, Copilot, etc.), read this section before touching anything.
| Fact | Value |
|---|---|
| What it is | Library of pure functions: props -> HTML string |
| Language | Plain JavaScript (ESM source), with tsc --noEmit typechecking |
| Test runner | Vitest (no Jest, no Babel) |
| Bundler | tsup (CJS + ESM + IIFE) |
| Lint / format | ESLint + Prettier |
| Side effects | None. No DOM, no network, no global state |
| Entry point | src/index.js |
npm installRun these after every change, in this order. Do not report a task as done until all pass.
npm run format # normalize style
npm run lint # must be clean
npm run typecheck # must be clean
npm test # must pass
npm run build # must succeed (verifies all 3 bundles)Use npm run test:watch while iterating and npm run lint:fix for autofixable lint issues. For read-only checks (e.g. CI-style), use npm run format:check.
src/
components/ # one file per typography component (the real logic lives here)
components.js # aggregates every component into a single export
config.js # shared configuration values (colors, sizes, etc.)
index.js # package entry point; public API surface
tests/
*.test.js # Vitest unit tests
*.test.ts # typed Vitest examples
dist/ # build output (generated, never edit by hand)
Every component:
- Is a plain function that accepts props and returns an HTML string.
- Is pure and deterministic: same input, same output. No DOM access, no I/O.
- Lives in
src/components/and is re-exported throughsrc/components.js, thensrc/index.js. - Takes shared styling values from
src/config.jsrather than hard-coding them.
Available components:
| Export | Output |
|---|---|
headingComponent |
<h3> section heading |
titleComponent |
<h1> main title |
subtitleComponent |
styled <p> subtitle |
paragraphComponent |
body text wrapper <div> |
strongComponent |
<strong> bold text |
italicComponent |
<i> italic text |
linkComponent |
<a> anchor |
listComponent |
<ul> unordered list |
listItemComponent |
<li> with inner <p> |
imageComponent |
<img> inside a centered <p> |
separatorComponent |
horizontal *** separator |
buttonComponent |
styled <a> button link |
⚠️ Prop names and exact HTML are not documented here on purpose, so they can't drift from the code. Read the component file and its test before using or changing it.
Add a new component
- Create
src/components/<name>Component.jsexporting a pure function that returns a string. - Pull shared styles from
src/config.jsinstead of inlining magic values. - Re-export it from
src/components.js(and confirm it surfaces throughsrc/index.js). - Add
tests/<name>Component.test.jscovering default props, custom props, and empty/edge input. - Add a row to the component table in this README.
- Run the verification loop.
Change an existing component's output
- Read its current test first. Existing tests define the contract.
- Update the implementation, then update tests to match the intended new output.
- Treat output changes as potentially breaking for downstream MTE templates; call this out in your summary.
Fix a bug
- Write a failing test that reproduces it.
- Fix the code.
- Confirm the test passes and the full loop is green.
Do
- Keep functions pure and string-in/string-out.
- Match existing naming (
<thing>Component) and file layout. - Escape or sanitize user-provided strings if you introduce any new interpolation, and note it in tests.
- Keep changes small and focused; one concern per change.
Don't
- Don't add Jest or Babel. The toolchain is Vitest + tsup on purpose.
- Don't edit
dist/. It is generated bynpm run build. - Don't add runtime dependencies without a strong reason; this library is meant to stay tiny.
- Don't touch the DOM or use browser-only APIs; output must work in Node, ESM, CJS, and the IIFE bundle.
- Don't change
separatorComponent's***output casually; it is intentionally not an HTML tag. - Don't skip failing tests or loosen lint rules to get green.
-
format,lint,typecheck,test, andbuildall pass - New or changed behavior has tests
- README component table updated if the public API changed
- No unrelated files modified
- Summary notes any output changes that could affect downstream templates
Prefer reading the nearest existing component and test and mirroring them over inventing a new pattern. If the intended HTML for something is ambiguous, ask rather than guess.
npm install
npm test
npm run buildimport * as components from 'atherdon-newsletter-js-layouts-typography';
// Each component takes props and returns an HTML string.
// See src/components/ for the exact props each one accepts.Browser (IIFE build):
<script src="dist/index.iife.js"></script>
<script>
// Components are available on the global:
console.log(newsletterLayoutsTypographyPlainJS);
</script>| Command | Description |
|---|---|
npm test |
Run tests with Vitest |
npm run test:watch |
Vitest watch mode |
npm run typecheck |
Typecheck with tsc --noEmit |
npm run lint |
Lint src/ with ESLint |
npm run lint:fix |
Auto-fix ESLint issues |
npm run format |
Format source files with Prettier |
npm run format:check |
Check formatting without writing |
npm run build |
Build CJS, ESM, and IIFE bundles with tsup |
npm run dev |
Watch mode build |
Bundled with tsup. npm run build writes:
| File | Format | package.json field |
|---|---|---|
dist/index.cjs.js |
CommonJS | main |
dist/index.es.js |
ESM | module |
dist/index.iife.js |
Browser IIFE | browser (global newsletterLayoutsTypographyPlainJS) |
Tests run with Vitest. There is no Jest or Babel in the toolchain.
- Fork and create a branch.
- Make your change with tests.
- Run
npm run format && npm run lint && npm run typecheck && npm test && npm run build. - Open a pull request describing what changed and why.
See the repository for license information.