---
title: "HTML template"
description: "A plain HTML starter that uses Systhema's tokens, utility classes and vanilla JavaScript bundle."
requested_language: fr
language: en
translation_notice: "This page isn't translated yet"
url: https://docs.systhema.app/fr/next/getting-started/templates/html
version: unreleased (main)
docs_index: https://docs.systhema.app/fr/next/llms.txt
---
> This page isn't translated yet. Showing English.


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.

> [!NOTE]
> `systhema create` does not offer the HTML template. Set it up by hand with the files this page describes.

> [!WARNING]
> The shipped vanilla JavaScript bundle currently throws on load in browsers. Verify a fixed release before relying on its accordion, menu, scroll, parallax or cookie-consent listeners. CSS generation is a separate path.

## Prerequisites

- Node.js 20.9 or newer and pnpm 10.
- A GitHub personal access token (classic) with the `read:packages` scope. See [Registry access](https://docs.systhema.app/fr/next/getting-started/registry-access.md).

## Quick start

### 1. Create the project

Create a folder with this `package.json`, plus the `index.html`, `systhema.config.js` and `src/style.css` files described under [Project structure](#project-structure) and [How it works](#how-it-works):

```json title="package.json"
{
  "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 access

Add the `.npmrc` and `GITHUB_TOKEN` from [Registry access](https://docs.systhema.app/fr/next/getting-started/registry-access.md), then export the token.

### 3. Install dependencies

```bash
pnpm install
```

The template's `package.json` pulls in:

- `@systhemaui/core`, design tokens, the Tailwind plugin, and the bundled JS.
- `@tailwindcss/cli` and `tailwindcss`, Tailwind v4 CLI.
- `concurrently` and `serve`, for the dev workflow.

### 4. Add design tokens

Place exported token JSON files in `src/tokens/`. They come out of the [Figma plugin](https://docs.systhema.app/fr/next/design/figma.md).

### 5. Sync tokens and types

```bash
pnpm sync   # = systhema-core sync
```

Re-run this whenever tokens or `systhema.config.js` change.

### 6. Start the dev server

```bash
pnpm dev
```

This runs two processes in parallel via `concurrently`:

- `pnpm dev:css`, Tailwind in watch mode (`@tailwindcss/cli` rebuilds `dist/style.css` on change).
- `pnpm dev:serve`, `serve` on port 3000 to serve the static files.

Open `http://localhost:3000` to view the page.

For one-off builds:

```bash
pnpm build   # = tailwindcss -i ./src/style.css -o ./dist/style.css --optimize
```

## Project structure

```text
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 index
```

## How it works

### Tailwind entry

Use paths relative to your stylesheet; the bundled template's monorepo source path does not exist in a consumer project:

```css title="src/style.css"
@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](https://docs.systhema.app/fr/next/styling/cascade-layers.md) for the full layer order.

### `systhema.config.js`

Use CommonJS and enable the component safelist with `packages.react: true`. This does not install React:

```js title="systhema.config.js"
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,
  },
}
```

> [!IMPORTANT]
> **Keep the `customTokens.responsiveSizing` block if you keep the `%screen%` spacing rule.** Together they are one decision: the rule makes layout tokens fluid, and the block re-bases them at each breakpoint wider than `lg`. Dropping the block leaves the `lg` ratios applying to every display above 1192px, a container that reaches 2225px on a 2560px screen. Full explanation and the numbers: [Responsive sizing](https://docs.systhema.app/fr/next/concepts/responsive-sizing.md#spacing-rules).

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 classes

Apply Systhema's classes directly on HTML elements. Use the class structure documented for each [component](https://docs.systhema.app/fr/next/components.md):

```html title="index.html"
<!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 JavaScript

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:

```bash
mkdir -p public
cp node_modules/@systhemaui/core/dist/js/bundle.global.js public/systhema.js
```

```html
<script src="public/systhema.js" defer></script>
```

These paths describe the shipped outputs, not a workaround for the load failure. Use [Vanilla JS bundle](https://docs.systhema.app/fr/next/styling/vanilla-js.md) for listener APIs, and verify them against the release you deploy.

## Adding more pages

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`).

```bash
# Add another page.
cp index.html about.html
# Edit about.html, then:
pnpm dev
```

## Differences from React/Next templates

- **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: true` in `systhema.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% * 100vw` layout rule with wide-screen overrides. The Next.js starter uses its configured defaults. See [Responsive sizing](https://docs.systhema.app/fr/next/concepts/responsive-sizing.md#spacing-rules).
- **Component instances are markup**, not React components, read [`@systhemaui/react`](https://docs.systhema.app/fr/next/components.md) for the canonical class structure of each component (the same class names apply on plain HTML).

## Ongoing maintenance

- Re-run `pnpm sync` after any design-token update.
- Keep your `GITHUB_TOKEN` active.
- After updating `@systhemaui/core`, re-run `pnpm sync` and redeploy.
