HTML template
A plain HTML starter that uses Systhema's tokens, utility classes and vanilla JavaScript bundle.
On this page
The HTML template is for static landing pages and small marketing sites that don't need React or any framework. Use it for design tokens and CSS utility classes through Tailwind CSS v4. Its vanilla JavaScript listeners have a known browser load failure.
PrerequisitesLink to this section
- Node.js 20.9 or newer and pnpm 10.
- A GitHub personal access token (classic) with the
read:packagesscope. See Registry access.
Quick startLink to this section
1. Create the projectLink to this section
Create a folder with this package.json, plus the index.html, systhema.config.js and src/style.css files described under Project structure and How it works:
{
"name": "my-static-site",
"version": "1.0.0",
"private": true,
"scripts": {
"sync": "systhema-core sync",
"build": "tailwindcss -i ./src/style.css -o ./dist/style.css --optimize",
"dev:css": "tailwindcss -i ./src/style.css -o ./dist/style.css --watch --optimize",
"dev:serve": "serve -l 3000 .",
"dev": "concurrently \"pnpm run dev:css\" \"pnpm run dev:serve\""
},
"dependencies": {
"@systhemaui/core": "^1.7.5",
"@tailwindcss/cli": "^4.3.3",
"tailwindcss": "^4.3.3"
},
"devDependencies": {
"concurrently": "^10.0.5",
"serve": "^14.2.4"
}
}2. Configure registry accessLink to this section
Add the .npmrc and GITHUB_TOKEN from Registry access, then export the token.
3. Install dependenciesLink to this section
pnpm installThe template's package.json pulls in:
@systhemaui/core, design tokens, the Tailwind plugin, and the bundled JS.@tailwindcss/cliandtailwindcss, Tailwind v4 CLI.concurrentlyandserve, for the dev workflow.
4. Add design tokensLink to this section
Place exported token JSON files in src/tokens/. They come out of the Figma plugin.
5. Sync tokens and typesLink to this section
pnpm sync # = systhema-core syncRe-run this whenever tokens or systhema.config.js change.
6. Start the dev serverLink to this section
pnpm devThis runs two processes in parallel via concurrently:
pnpm dev:css, Tailwind in watch mode (@tailwindcss/clirebuildsdist/style.csson change).pnpm dev:serve,serveon port 3000 to serve the static files.
Open http://localhost:3000 to view the page.
For one-off builds:
pnpm build # = tailwindcss -i ./src/style.css -o ./dist/style.css --optimizeProject structureLink to this section
my-static-site/
├── index.html # entry HTML; loads dist/style.css and the Systhema JS bundle
├── package.json
├── systhema.config.js # Systhema configuration (CommonJS)
└── src/
├── style.css # Tailwind entry; @import '@systhemaui/core/tailwind'
└── tokens/ # design-token JSON exported from Figma
└── manifest.json # token indexHow it worksLink to this section
Tailwind entryLink to this section
Use paths relative to your stylesheet; the bundled template's monorepo source path does not exist in a consumer project:
@import '@systhemaui/core/tailwind';
@source '../index.html';
@source '../**/*.html';
@source './tokens/';
/* Custom CSS for this project goes in the `app` layer, */
/* which follows the generated component and utility layers. */
@layer app {
@media (min-width: theme('screens.md')) {
.accordion-title {
@apply flex-row-reverse justify-end;
}
}
}The single @import '@systhemaui/core/tailwind' brings in Tailwind v4 plus all of Systhema's utilities, components, and design-token-derived classes. The @source directives scan your HTML and token files for class names. Core supplies its component safelist when packages.react is enabled. Project-specific overrides belong in @layer app { … }. See Cascade layers for the full layer order.
systhema.config.jsLink to this section
Use CommonJS and enable the component safelist with packages.react: true. This does not install React:
const manifest = require('./src/tokens/manifest.json')
/** @type {import('@systhemaui/core').SysthemaConfig} */
module.exports = {
manifest,
spacing: [
{
function: '%value% / %screen% * 100vw',
include: ['grid', 'gap', 'container', 'section', 'article', 'feature'],
exclude: ['feature.mediaMinHeight'],
},
{
function: '--spacing(%value% / 4)',
include: false,
exclude: '*borderWidth',
swap: true,
},
],
// Mandatory companion to the fluid rule above: it expresses every layout token
// as a ratio of its breakpoint's design width, and core ships no
// responsiveSizing values for xl/2xl/3xl — so without these the `lg` ratios
// would scale unbounded past 1192px. See core.md → spacing.
customTokens: {
responsiveSizing: {
xl: {
grid: { default: { count: '24', gap: '20px', margin: '142px' } },
container: { width: '1156px', margin: '{grid.default.margin}' },
section: { paddingY: '96px' },
article: { paddingX: '147px' },
},
'2xl': {
grid: { default: { count: '24', gap: '24px', margin: '300px' } },
container: { width: '1320px', margin: '{grid.default.margin}' },
section: { paddingY: '120px' },
article: { paddingX: '168px' },
},
'3xl': {
grid: { default: { count: '24', gap: '24px', margin: '620px' } },
container: { width: '1320px', margin: '{grid.default.margin}' },
section: { paddingY: '120px' },
article: { paddingX: '168px' },
},
},
},
packages: {
react: true,
},
}The packages.react: true flag is what enables the safelist for component classes (button-primary, card, accordion, etc.), even though there's no React in the project, the same class names work on plain HTML elements.
Using Systhema utility classesLink to this section
Apply Systhema's classes directly on HTML elements. Use the class structure documented for each component:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Company site</title>
<link rel="stylesheet" href="dist/style.css" />
</head>
<body class="bg-layout-main" data-theme="default">
<a class="skip-link" href="#main-content">Skip to content</a>
<header class="header header-sticky">
<div class="header-container container">
<a href="/" class="header-logo">
<span class="text-xl font-bold">Systhema</span>
</a>
<nav class="header-navigation">
<a href="index.html" class="header-navigation-item">Home</a>
<a href="about.html" class="header-navigation-item">About</a>
</nav>
</div>
</header>
<main id="main-content">
<article class="article" data-theme="default">
<section class="hero-simple" data-theme="default">
<div class="hero-simple-container container">
<h1 class="text-h1 color-heading">Hello world!</h1>
</div>
</section>
</article>
</main>
</body>
</html>data-theme="default" is how you switch color modes on a region; the same attribute drives the theme prop in React.
The bundled JavaScriptLink to this section
The shipped listener entry imports every listener. Each module initializes itself on import or on DOMContentLoaded. The ESM bundle at @systhemaui/core/js has tree-shaking disabled; importing one named export does not opt out of the other listeners.
The IIFE output is dist/js/bundle.global.js, with the global name SysthemaJS. The dist/js/bundle.js file is ESM and is not a classic-script entry. After the browser load bug is fixed, copy the IIFE into a public asset directory rather than serving all of node_modules in production:
mkdir -p public
cp node_modules/@systhemaui/core/dist/js/bundle.global.js public/systhema.js<script src="public/systhema.js" defer></script>These paths describe the shipped outputs, not a workaround for the load failure. Use Vanilla JS bundle for listener APIs, and verify them against the release you deploy.
Adding more pagesLink to this section
Create additional HTML files at the project root (or in subfolders) and link to them. Tailwind's content scanner picks up classes from anywhere in the project (controlled by the @source directives in src/style.css).
# Add another page.
cp index.html about.html
# Edit about.html, then:
pnpm devDifferences from React/Next templatesLink to this section
- No bundler. Tailwind compiles CSS; everything else is plain HTML and the Systhema JS bundle.
- No TypeScript configuration is required. Keep the pnpm lockfile for repeatable installs.
packages.react: trueinsysthema.config.js, explicitly enables the React-component-class safelist so the same class names work on raw HTML.- The HTML and Payload starters use a fluid
%value% / %screen% * 100vwlayout rule with wide-screen overrides. The Next.js starter uses its configured defaults. See Responsive sizing. - Component instances are markup, not React components, read
@systhemaui/reactfor the canonical class structure of each component (the same class names apply on plain HTML).
Ongoing maintenanceLink to this section
- Re-run
pnpm syncafter any design-token update. - Keep your
GITHUB_TOKENactive. - After updating
@systhemaui/core, re-runpnpm syncand redeploy.