Theme SDK
Rendez les pages publiques avec des templates TSX ou des documents de blocs.
Sur cette page
Le runtime de distribution
Un thème empaqueté exporte une factory recevant ThemeRuntime : createElement, Fragment, defineTheme, Blocks, Menu, SiteLayout et ContentFields. Les dépendances JavaScript statiques sont assemblées au packaging ; la factory conserve le contexte React fourni par le CMS.
import type { ThemeRuntime } from "@bracten/sdk/theme";
export default function theme(runtime: ThemeRuntime) {
const { SiteLayout, Blocks } = runtime;
return runtime.defineTheme({
id: "agency.example", name: "Example", version: "0.1.0",
parent: "official.starter",
templates: {
page: (context) => (
<SiteLayout context={context}>
<h1>{context.content?.title}</h1>
{context.content && (
<Blocks document={context.content.document} context={context} />
)}
</SiteLayout>
),
},
});
}theme:create génère le projet TypeScript et la configuration JSX. themes/minimal-child fournit aussi une référence complète avec manifest, feuille CSS et packaging ; ce fragment seul n’est pas un package installable.
TemplateContext
| Valeur | Rôle |
|---|---|
| site, menu | Réglages et menu principal, conservé pour compatibilité. |
| menus, menuLabels | Menus nommés et libellés accessibles, facultatifs dans le type. |
| messages, interfaceLocale | Catalogue et langue des libellés publics. |
| content, contentType | Contenu courant ou null et son schéma. |
| collection, search, path, archiveTitle | Résultats paginés et contexte de la route publique. |
| parts, syncedPatterns, themeSettings | Parties, compositions synchronisées et réglages résolus. |
| terms, fieldTerms, references, parent | Termes et références autorisées, y compris chemins de champs imbriqués. |
| media | Dictionnaire des médias des champs, avec titre indépendant, texte alternatif, légende, MIME, dimensions et variantes. |
ContentFields rend les valeurs structurées, médias, PDF, termes et relations résolues ; userRelation reste omis du rendu public. Une cible privée ou inaccessible ne doit pas être lue directement à partir de son identifiant. Passez context aux blocs pour préserver compositions et données dynamiques.
MediaEntry.title peut être vide. Un thème peut utiliser explicitement context.media[id].title, avec l’échappement habituel de React ; les composants natifs n’en déduisent ni un texte alternatif, ni une légende, ni un attribut HTML title. Le nom du fichier et ses URL restent indépendants.
Résolution et héritage
Les noms reconnus sont index, home, page, post, archive, taxonomy, search, 404, type_<identifiant>, taxonomy_<identifiant> et les noms de terme produits par termTemplateName. Un template propre au terme précède celui de sa taxonomie, puis taxonomy, archive et index. post peut revenir à page puis index. Les personnalisations enregistrées peuvent remplacer les documents distribués.
Un enfant peut remplacer un template TSX par un document ou inversement. La chaîne est limitée à huit niveaux et exige un index effectif. Parents absents, incompatibles ou circulaires sont refusés. Les CSS parents précèdent celui de l’enfant.
Blocs versionnés des thèmes enfants
ThemeDefinition peut déclarer contributions: { apiVersion: 1, blocks, hooks }, détectable par runtime.contributionsVersion === 1. Un ancien thème sans contributions conserve son comportement. Une version de contrat inconnue ou une déclaration invalide bloque aperçu, activation ou mise à jour. Cette API ne fournit aucun accès aux comptes, à la base ou aux services du Plugin SDK.
Chaque bloc déclare name, label, version, fields et render(values, context). Les parents puis l’enfant alimentent Blocs complémentaires ; GET /api/v1/extensions distingue themeId de pluginId. Le document conserve un bloc extension avec exactement une de ces identités, puis name, version et values dans props, et une copie native dans children. Un nom identique chez un parent, un enfant ou un plugin désigne des contributions distinctes. Chaque thème accepte au plus trente blocs, sans paramètre richText ni champ extension.
Le renderer reçoit les valeurs validées et { themeId, activeThemeId, themeSettings } sous forme de copies profondément gelées. Il doit retourner synchroniquement un document natif de contenu valide et non vide, sans bloc extension ni composition synchronisée. Une Promise, une exception ou un résultat invalide arrête l’opération. Les limites de documents et l’assainissement du HTML restent actifs ; aucun JavaScript de thème n’est injecté dans l’administration.
POST /api/v1/theme-blocks/:theme/blocks/:name/render reçoit { version, values } et retourne { values, document }. La session, le CSRF et l’une des permissions fraîches content.create, content.update ou appearance.edit sont exigés. Seuls les blocs de la chaîne active sont exécutables. L’aperçu privé et l’enregistrement recalculent la copie ; une visite publique utilise la copie enregistrée. Changer de thème ou retirer une contribution conserve celle-ci et ses paramètres. Un bloc indisponible sans copie valide ne peut pas être publié.
Le registre durable conserve le contrat machine des champs pour chaque identité (themeId, name, version), même après retrait de la déclaration ou désinstallation du thème. Une modification des types, obligations, valeurs possibles, cibles ou limites exige une version supérieure. Les libellés et le seul ordre de présentation ne changent pas ce contrat. Activation, mise à jour et retour de version le contrôlent dans leur transaction ; un échec conserve packages, contrats et contenus. Une installation reste sans exécution : un paquet réinstallé peut être enregistré mais refusé à l’activation.
La migration additive theme_block_contracts:1 crée le registre et réserve aussi les identités présentes dans les contenus, champs, révisions, brouillons et personnalisations, sans réécrire leurs données. Un schéma inconnu reste NULL. Seul le paquet de référence installé lors de la migration, identifié par son empreinte, peut compléter sa déclaration encore disponible. Une ancienne identité sans schéma récupérable refuse toute nouvelle déclaration de même version : utilisez une version supérieure. Un paquet de référence cassé doit être réparé depuis sa copie d’origine avant son retrait ; aucun ancien contrat n’est inventé.
Les anciennes instances gardent version, valeurs et copie, sans migration implicite. La mise à jour du parent contrôle les descendants sans modifier le paquet de l’enfant ; le retour de version conserve les personnalisations compatibles. docs/theme-block-contract-history.md décrit la reprise, ses limites et les tests locaux de retrait, réinstallation, migration, droits et concurrence. Le scan historique dépend du volume des sources persistantes ; mesurez-le sur une copie avant déploiement. Les preuves navigateur de docs/qualification-child-theme-contributions.md restent distinctes du correctif de persistance, qui n’annonce aucun nouveau parcours navigateur.
Contributions avant et après le rendu
contributions.hooks accepte render.before et render.after pour émettre des blocs avant ou après la vue, sur les pages publiques et les aperçus privés, même sans SiteLayout. Ces contributions visuelles sont distinctes des notifications homonymes du Plugin SDK. Elles ne modifient ni métadonnées, ni permissions, ni contenu stocké.
Un hook possède un nom unique dans son emplacement et son thème, une priorité entière de -1000 à 1000, dix par défaut, et render(context). Le TemplateContext est copié et gelé par callback, avec themeId, activeThemeId, themeSettings et template. Les priorités croissantes précèdent l’ordre parent-enfant, puis l’ordre de déclaration. Les hooks de l’enfant s’ajoutent à ceux du parent, y compris si leurs noms sont identiques.
Un emplacement accepte vingt hooks par thème et 10 000 blocs rendus au total. Le résultat synchrone peut être vide mais reste un document natif soumis aux mêmes restrictions que les blocs. Une erreur refuse le rendu de contrôle ou produit une erreur publique générique sans HTML partiel. Ces callbacks restent du code serveur de confiance : aucun bac à sable ni délai maximal n’est fourni, et une transaction ne peut pas rappeler un effet externe. Contrôlez les cas dépendant de vos données sur un staging isolé.
Documents, édition et mises à jour
ThemeDefinition accepte documents, parts, patterns, tokens et settings. L’éditeur de site gère aperçu, copies privées, récupération, révisions et retour à la source. Les compositions synchronisées conservent une copie de secours.
Le bloc navigation utilise props.menu, primary par défaut, et un props.label accessible facultatif. Les liens vers des contenus ou termes suivent leur adresse et sont filtrés selon leur visibilité. Un menu absent rend une navigation vide ; il ne se replie pas sur un autre menu.
defaultMessages associe explicitement des blocs de documents distribués à des clés du catalogue de langue. Starter utilise ce mécanisme pour ses textes par défaut. Une surcharge personnalisée reste un contenu de l’utilisateur, même si son texte ressemble au texte initial ; elle n’est pas traduite implicitement. Le rendu public suit la langue du site ou du contenu, pas la préférence du compte connecté.
Les réglages et surcharges appartiennent au thème actif ; les schémas de réglages des parents ne sont pas fusionnés automatiquement. La mise à jour locale prépare une sauvegarde, contrôle l’héritage et conserve les surcharges. Un rollback rétablit un package antérieur compatible, sans annuler toutes les données de l’instance. Consultez docs/themes.md pour ces garanties.