@clauzier/astro-kit (0.10.0)
Installation
@clauzier:registry=npm install @clauzier/astro-kit@0.10.0"@clauzier/astro-kit": "0.10.0"About this package
@clauzier/astro-kit
Couche front partagée de la plateforme multi-clients : client typé vers cms-core, couche contenu, réglages et libellés, gabarit de site, composants Astro, styles socle et modules verticaux opt-in (esport, twitch).
Modules
| Entrypoint | Contenu |
|---|---|
client |
createCmsClient : client typé minimal vers l'API cms-core (dont getSiteSettings) |
entities |
createContentSource, createSiteSettings, normalisation, libellés de schémas |
labels |
createLabels, interpolate : textes d'interface surchargeables depuis les réglages |
layout |
buildSiteHead, pageTitle, footerLine : tête de page depuis les réglages |
pages |
cmsPagePaths, cmsPageSeo, renderCmsPage, notFoundSeo : route fourre-tout et 404 |
blocks |
Registre de renderers + rendu HTML sûr (escapeHtml, sanitizeHtml, safeUrl) |
seo |
Balises meta/OG, URLs absolues, robotsTxt |
esport |
Opt-in. withEsport, createEsportBlockRegistry, helpers de fiche équipe |
twitch |
Opt-in. renderWebTv, createWebTvRenderer, statut Twitch ; twitch/island (îlot) |
components/* |
Composants Astro : SiteLayout, Header, Footer, NotFound, TeamHero, … |
styles/* |
base.css (socle), esport.css, twitch.css : tout en tokens surchargeables |
Le barrel racine et ./entities/./blocks ne contiennent rien d'esport ni de
twitch : un site qui n'active pas ces presets n'en embarque rien.
Couche contenu
Une coquille cliente ne fournit que sa configuration et ses fixtures ; lecture API, normalisation snake_case → camelCase, tris et garde anti-repli viennent du paquet.
import { createContentSource, createSiteSettings } from '@clauzier/astro-kit/entities';
import { withEsport } from '@clauzier/astro-kit/esport';
const source = withEsport(
createContentSource({
baseUrl: import.meta.env.CMS_BASE_URL,
fixtures: { site: createSiteSettings({ name: 'Mon club' }), pages, staff, socials, menus },
avatarColors: ['#1226aa', '#e63946'],
}),
{ teams, players, matches, achievements },
);
export const { getSiteSettings, getLabels, listPageEntries, listTeams, listPalmares } = source;
Sans baseUrl, les fixtures sont servies (CI, previews) et fixtures.site est
obligatoire. Avec une API configurée, toute erreur de lecture fait échouer le
build — y compris une fiche Site absente (« créez et publiez la fiche Site en
back-office ») : un repli silencieux publierait un site plausible mais faux.
Les libellés de schéma (rôles, plateformes…) viennent de /api/content-schemas
via schemaLabels() — ils ne sont jamais recopiés côté front.
Réglages et libellés
GET /api/site sert la fiche singleton site : identité, contact, SEO et
surcharges de textes. getSiteSettings() la normalise (SiteSettings, champs
absents à null, champs hors contrat dans extra, ex. twitch_channel) et la
lit une fois par build.
Chaque texte humain a une clé de libellé, avec un défaut dans le module qui le rend et une surcharge possible depuis les réglages :
const labels = await getLabels(); // surcharges API > défauts TS > clé
labels.t('footer.copyright', { year: 2030, name: 'Mon club' }); // « © 2030 Mon club »
labels.t('header.menu'); // « Menu »
t() rend du texte brut, toujours échappé à l'affichage : une surcharge
<b>…</b> s'affiche telle quelle. Les défauts génériques vivent dans
labels/defaults.ts, ceux des modules dans esport/labels.ts et
twitch/labels.ts ; withEsport étend getLabels() avec les siens, et chaque
renderer garantit les siens quoi qu'on lui passe. La liste des clés est figée
par tests/labelsCatalog.test.ts, miroir du LabelCatalog de cms-core :
ajouter une clé se fait des deux côtés, jamais dans une coquille.
Les libellés s'injectent par closure : createCoreBlockRegistry(source, { labels }),
createEsportBlockRegistry(source, { labels }), createWebTvRenderer({ labels }), createAwesomeFormsRenderer({ labels }),
prop labels des composants, PartContext.labels des renderers de fragments.
Registres de briques : défauts + core + modules
Une coquille compose son registre par familles : les briques par défaut
(heading, paragraph, hero, section, image, form), celles du preset
core (staff_grid, socials_grid, depuis listStaff/listClubSocials),
puis les modules qu'elle active.
Aucun chemin du site n'est supposé par le socle : la coquille fournit sa base d'URL d'équipe et ses liens de repli, et sans lien il n'y a pas de bouton.
import { createCoreBlockRegistry, createDefaultBlockRegistry } from '@clauzier/astro-kit/blocks';
import { createEsportBlockRegistry } from '@clauzier/astro-kit/esport';
import { createWebTvRenderer } from '@clauzier/astro-kit/twitch';
const registry = createDefaultBlockRegistry({ layout: 'section', form });
createCoreBlockRegistry(source, { registry, labels });
createEsportBlockRegistry(source, {
registry,
labels,
clubName: settings.name,
teamBase: '/sections/',
links: { calendar: '/calendrier/', tv: '/direct/' },
});
registry.register(
createWebTvRenderer({
apiBaseUrl: source.apiBaseUrl,
channel: String(settings.extra.twitch_channel ?? ''),
labels,
}),
);
Briques esport : teams_grid, matches (mode: results), palmares
(mode: tournaments, group_by_year, filter_by_game — filtre par jeu sans
JavaScript, scopé à l'instance), next_match.
Fiche équipe : splitRoster(roster, staffRoles) et teamNote(team, staff, labels).
Bloc webtv : coquille statique (jamais de faux LIVE), hydratée côté client
par import '@clauzier/astro-kit/twitch/island' dans le gabarit — l'îlot lit
ses textes en data-label-* et hydrate aussi les comptes à rebours. Sans canal
ni origine d'API, seule la coquille est rendue. La pastille flottante est
twitch/components/LivePill.astro. Les feuilles esport.css et twitch.css
sont importées explicitement par la coquille ; twitch.css attend le token
--live.
Gabarit de site et pages CMS
SiteLayout.astro rend la tête SEO (titre suffixé, favicon, OG de repli),
l'en-tête (logo et CTA des réglages), le lien d'évitement, <main id="contenu">
et le pied de page (« © année nom · mention légale », ou footer_text). Il
n'importe aucune feuille de style : la coquille charge sa palette puis
base.css dans son propre gabarit.
---
import '@/styles/theme.css';
import '@clauzier/astro-kit/styles/base.css';
import SiteLayout from '@clauzier/astro-kit/components/SiteLayout.astro';
import { getLabels, getSiteSettings, listClubSocials, listMenu } from '@/lib/content.js';
const [settings, labels, header, footer, socials] = await Promise.all([
getSiteSettings(),
getLabels(),
listMenu('header'),
listMenu('footer'),
listClubSocials(),
]);
---
<SiteLayout
{settings}
{labels}
{socials}
menus={{ header, footer }}
brandSplit={3}
{...Astro.props}
>
<slot />
</SiteLayout>
Route fourre-tout des pages composées en back-office, en une quinzaine de lignes :
---
import { cmsPagePaths, cmsPageSeo, renderCmsPage } from '@clauzier/astro-kit/pages';
export async function getStaticPaths() {
return cmsPagePaths({ source, homeSlug: 'accueil', reserved: ['404', 'sections'] });
}
const { entry } = Astro.props;
const seo = cmsPageSeo(entry);
const html = await renderCmsPage(entry, { registry, path: Astro.url.pathname });
---
<Base title={seo.title} description={seo.description} ogImage={seo.ogImage}>
<Fragment set:html={html} />
</Base>
reserved accepte des préfixes ou un prédicat asynchrone ; un slug réservé ou
un accueil absent (avec API) cassent le build avec un message explicite.
NotFound.astro (settings, labels?, links?) et notFoundSeo() couvrent
la 404 ; robotsTxt({ siteUrl }) sert robots.txt.
Composants et styles
Le Header ne charge aucun script : le burger mobile et les sous-menus sont
des <details>/<summary>, dont l'état d'ouverture est exposé nativement aux
technologies d'assistance. Au-dessus de 900 px, [open] est neutralisé par la
feuille socle et les sous-menus reviennent au survol. L'entrée parente d'un
sous-menu n'est plus un lien — ajouter un « Tout voir » en tête de ses enfants
si elle porte une page propre.
base.css porte le reset global du document et n'écrit aucune couleur en dur :
tout passe par des tokens (--bg, --panel, --accent, --muted,
--font-display…) que la coquille définit dans sa propre feuille de palette
(tokens.css en donne le contrat et un point de départ neutre).
Développement
bun install
bun run lint && bun run format:check && bun run typecheck && bun run test
Dependencies
Dependencies
| ID | Version |
|---|---|
| sanitize-html | ^2.17.7 |
Development dependencies
| ID | Version |
|---|---|
| @eslint/js | ^10.0.0 |
| @types/node | ^26.0.0 |
| @types/sanitize-html | ^2.16.1 |
| astro | ^7.2.4 |
| eslint | ^10.0.0 |
| eslint-plugin-astro | ^1.3.0 |
| prettier | ^3.9.0 |
| prettier-plugin-astro | ^0.14.0 |
| typescript | ~5.9.0 |
| typescript-eslint | ^8.40.0 |
| vitest | ^4.0.0 |
Peer dependencies
| ID | Version |
|---|---|
| astro | ^7.0.0 |