Référence du manifest thème

Chaque thème exporte un unique depuis son . C'est le contrat entre le thème et l'admin. Faites ça bien et tout le reste suit.

Chaque thème exporte un ThemeManifest<TConfig> unique depuis son manifest.ts. C'est le contrat entre le thème et l'admin. Faites ça bien et tout le reste suit.

Shape complète

TS
export interface ThemeManifest<TConfig = unknown> {
  id: string;
  name: string;
  version: string;
  description?: string;
  imageFormats?: ImageFormatConfig;
  scssEntry?: string;
  cssText: string;
  compileCss?: (config: TConfig) => string;
  templates: {
    base: ComponentType<BaseLayoutProps>;
    home: ComponentType<HomeTemplateProps>;
    single: ComponentType<SingleTemplateProps>;
    category: ComponentType<CategoryTemplateProps>;
    author: ComponentType<AuthorTemplateProps>;
    notFound: ComponentType<NotFoundTemplateProps>;
  };
  blocks?: BlockManifest[];
  settings?: {
    navLabelKey: string;
    defaultConfig: TConfig;
    component: ComponentType<ThemeSettingsPageProps<TConfig>>;
  };
  i18n?: Record<string, Record<string, unknown>>;
  jsText?: string;                 // optionnel — code du menu-loader / posts-loader inline
}

Champs

id — obligatoire

Kebab-case, ASCII lower. Immuable. Utilisé comme :

  • Clé de stockage : settings.activeThemeId, settings.themeConfigs[id]
  • Path d'asset : theme-assets/<id>.css sur Flexweg
  • Namespace de bloc : tous les blocs <id>/<name>
  • Namespace i18n : theme-<id>
  • Path d'install (thèmes externes) : /admin/themes/<id>/

name — obligatoire

Affichage UI dans la liste des thèmes.

version — obligatoire

Semver. Affichée à la réinstallation pour les thèmes externes (v1.0.0 → v1.1.0).

description — optionnel

Phrase courte affichée sur la carte du thème.

imageFormats — optionnel

Déclare les variantes d'image que le thème consomme. Voir Variantes d'image.

TS
imageFormats: {
  inputFormats: [".jpg", ".jpeg", ".png", ".webp", ".gif"],
  outputFormat: "webp",
  quality: 80,
  variants: [
    { name: "thumbnail", maxWidth: 400, maxHeight: 400, fit: "cover" },
    { name: "medium", maxWidth: 800, maxHeight: 800, fit: "contain" },
    { name: "large", maxWidth: 1600, maxHeight: 1600, fit: "contain" },
  ],
}

Si omis, le pipeline standard s'applique (les trois variantes admin-* + un fallback default).

scssEntry — optionnel

Indique le fichier SCSS principal (à des fins de documentation). N'a pas d'impact runtime — c'est cssText qui compte.

cssText — obligatoire

La CSS finale que l'admin upload sur Flexweg. Typiquement importée via ?inline ou ?raw :

TS
// Pipeline SCSS
import cssText from "./theme.scss?inline";

// Pipeline Tailwind (theme.compiled.css est produit par le prebuild)
import cssText from "./theme.compiled.css?inline";

// Pure CSS / hand-written
import cssText from "./theme.css?raw";

L'admin upload cssText (ou compileCss(config) si défini) à theme-assets/<id>.css à l'activation du thème + à chaque save de la page de réglages.

compileCss — optionnel

Hook qui transforme cssText (baseline) en CSS live (avec overrides utilisateur). Appelé chaque fois que l'admin upload theme-assets/<id>.css.

TS
compileCss: (config: MyThemeConfig) => {
  let css = cssText;
  // Append :root with user overrides
  const overrides = Object.entries(config.style.vars)
    .map(([k, v]) => `  ${k}: ${v};`)
    .join("\n");
  css += `\n\n:root {\n${overrides}\n}\n`;
  // Swap font @imports
  css = css.replace(
    /@import url\("https:\/\/fonts\.googleapis\.com[^"]+"\);/,
    `@import url("https://fonts.googleapis.com/css2?family=${config.style.fontHeadline}&display=swap");`
  );
  return css;
};

Sans compileCss, chaque sync écrase les overrides utilisateur — donc obligatoire dès que la page de réglages expose des overrides de style.

templates — obligatoire

Les six composants React. Tous obligatoires. Voir Templates et props.

blocks — optionnel

Tableau de BlockManifest. Auto-enregistrés à l'activation du thème. Voir Blocs de thème.

settings — optionnel

Déclare une page de réglages thème (/theme-settings). Voir Page de réglages thème.

i18n — optionnel

Bundles de traduction par locale. Chargés au boot dans le namespace theme-<id>. Mêmes conventions que pour les plugins.

jsText — optionnel

Code JavaScript runtime inline. Typique : un menu-loader.js qui remplit les [data-cms-menu] containers depuis /menu.json au chargement de la page.

TS
import jsText from "./menu-loader.js?raw";

const manifest: ThemeManifest = {
  // ...
  jsText,
};

L'admin upload jsText à theme-assets/<id>-menu.js à l'activation du thème. Le BaseLayout référence ce fichier via <script defer src="/theme-assets/<id>-menu.js"></script>.

Pour les thèmes avec plusieurs loaders, utilisez des conventions de noms (<id>-menu.js, <id>-posts.js, <id>-search.js, etc.) et uploadez chacun séparément dans la cible de régénération du thème.

Exemple minimal

TS
import cssText from "./theme.scss?inline";
import { BaseLayout } from "./templates/BaseLayout";
import { HomeTemplate } from "./templates/HomeTemplate";
import { SingleTemplate } from "./templates/SingleTemplate";
import { CategoryTemplate } from "./templates/CategoryTemplate";
import { AuthorTemplate } from "./templates/AuthorTemplate";
import { NotFoundTemplate } from "./templates/NotFoundTemplate";
import type { ThemeManifest } from "@flexweg/cms-runtime";

const manifest: ThemeManifest = {
  id: "my-theme",
  name: "My Theme",
  version: "1.0.0",
  description: "A minimal example theme",
  cssText,
  templates: {
    base: BaseLayout,
    home: HomeTemplate,
    single: SingleTemplate,
    category: CategoryTemplate,
    author: AuthorTemplate,
    notFound: NotFoundTemplate,
  },
};

export default manifest;

C'est tout pour un thème fonctionnel. Ajoutez blocks, settings, i18n, compileCss, jsText au besoin.