Deliver the shared layer of every site as one Astro package with a starter, and check each build inside it
Context and Problem Statement
The anatomy (ADR-0023) says what every site carries, the patterns exist as components (ADR-0024) and the standards have a checker (ADR-0025), but all three live inside this repository: a site can read the rules and copy nothing, which also means it can build nothing. Of the twelve sites, seven are placeholders and five were each built on their own; the most advanced, akribis.info, keeps its own fonts (Inter and Manrope), its own tokens, 13 hand-copied marks, 222 inline styles, no skip link, no security headers, a form that posts to the CRM from the browser with a hard-coded organisation id and a reCAPTCHA script, Spanish slugs under /en/ and /pt/, an SSR adapter for pages that are all static, and an accepted baseline of 232 type errors. The rebuild of eleven sites cannot start from a page of rules; it needs the shared layer as something a site installs. In what form do the patterns, the layout, the Worker and the checker reach a site, how is a site started, and how is the standard enforced where the site is built?
Decision Drivers
Criteria 1 (trust: the same company everywhere), 2 (one family), 4 (accessibility), 7 (three languages) and 9 (generated and tested) of ADR-0009; ADR-0023 (the layers), ADR-0024 (the patterns), ADR-0025 (the standards), ADR-0029 (the package and its version), ADR-0030 (the version is a date). The audit of akribis.info of 6 October 2026.
Considered Options
- One Astro package,
@akribis/site: an integration that configures the site from the brand’s data, the patterns as Astro components, the page layout, the Worker, the checker that runs after every build, and a starter inside the package, with the brand’s data carried in it - Web components or HTML and CSS partials, framework-free, each site wiring them in
- A template repository to fork, with the components copied into every site
Decision Outcome
Proposed: the first option. Every AKRIBIS site is Astro (the five real ones already are), so
the components are Astro components and the integration is the one knob. packages/site/ holds
the package; the brand build writes the data into it and packs it with the tokens package; this
site mounts it, and the starter in packages/starter/ is built on every build as the proof.
- One call.
akribis({ brand, kind, site })in a site’sastro.config.mjssets the three languages under/es/,/en/and/pt/with no redirect to a default, trailing slashes, directory output, the token CSS (base, type, shape and the brand’s file) imported into every page, the shared 404 androbots.txtas injected routes, the sitemap, and the data the components read (virtual:akribis: the register, the manifest, the anatomy, the strings, the font paths).indexable: falsefor a draft;assetsnames Brand’s origin for the marks and the fonts. A wrong brand id or kind fails at configuration time. - The checker runs inside the build. After
astro build, the integration runs the checker of ADR-0025 overdist/with the site’s kind and origin and fails the build on one violation. “Every site runs it before deploying” stops being an instruction: a site that is not deployable does not build. The checker’s code moves into the package (brand/src/check-site.tskeeps the command for this repository) so that every site runs the same rules at the same version. - The layout.
Shell.astrois the page: the head the standards require (language, title, description, canonical, hreflang with x-default, Open Graph with the brand’s profile picture, the icons and theme colour from the published files, the two font preloads), the skip link, on a unit’s site the group bar holding the language switch, the header with the site’s mark from the published file and the site’s navigation in a slot,main, and the footer with the site’s own columns, the group block with the unit directory and the legal line linking privacy and the legal notice. A site styles its own pages with the tokens; the base styles come with the shell. - The data travels in the package.
data/holdsunits.json,manifest.json,anatomy.json,standards.json, the newstrings.json(the texts of the shared layer, frombrand/strings.yaml),fonts.jsonand the token CSS, as they were on the day of the build. The marks and the fonts stay on Brand at their stable addresses (ADR-0028): a site ships none. The manifest now carries every image’s width and height, so a component can size a mark without reading the file. - The Worker.
createWorker()from@akribis/site/workerdoes the three things a site needs beyond static files:/goes to the visitor’s language with Spanish as the fallback (ADR-0025);POST /contactreceives the shared form, drops what filled the honeypot, validates the required fields, relays to the CRM’s Web-to-Lead address held in the Worker’s variables (CONTACT_RELAY,SF_OID,LEAD_SOURCE) with the site’s lead source, and sends the visitor to the thank-you page in the same language; old addresses redirect permanently. The HTML carries no CRM id and loads no third-party script; without a relay configured the form answers 503 rather than losing a message silently. This decides the open point of ADR-0024: one handler, in the package, per site. - The starter.
starter/inside the package is a site as it begins: the config with the three values, the Worker,_headerswith the response-header baseline, the home page with the AKRIBIS One module, and the four shared pages at their slugs in the three languages (contact with the form, thank-you, privacy and the legal notice showing the pending notice until the company’s texts arrive as data). It is also a workspace package here, built on every build and checked as a unit’s site: three languages, five pages, the 404, zero violations, no font or mark of its own. - Standards gained, as data in
standards.yamland rules in the checker:pages(every shared page exists for every locale at its slug),headers(the baseline in_headers:X-Content-Type-Options,Referrer-Policy,Permissions-Policy,X-Frame-Options), and the sitemap rule readssitemap-index.xmlas@astrojs/sitemapwrites it. Brand carries the baseline it asks for and passes its own check; being a draft and not a public property, it is exempt frompagesalone. - Two packages.
@akribis/tokens(ADR-0029) stays for anything that is not an Astro site (Office, print, tools) and gainsstrings.jsonandunits.schema.json, the schema thatunits.jsonalready named and that is now published.@akribis/sitecarries the same data for the sites. Both are packed on every build with the build date as their version; the starter the package ships pins that version./packages/index.jsonlists both. - Proof. The tests unpack both tarballs and check their contents and versions; check that every pattern the anatomy requires has a component in the package; lint the package’s and the starter’s CSS against the shape tokens as they lint this site’s; build the starter and run the checker over it as a unit’s site; and exercise the new rules on a page that breaks them.
Consequences
- Good, because a new unit’s site is three values and its own pages, and it passes the standards from its first build or does not build.
- Good, because the patterns, the texts, the marks and the fonts have one home and one version; the copies in the five real sites end as each is rebuilt on the package.
- Good, because the checker runs where the site is built, with no continuous integration needed for the first level of enforcement.
- The package is Astro-only by design; a non-Astro consumer has the tokens package and the data.
brand.akribis.groupmust resolve before any site is published: the marks and the fonts load from it. Until the custom domain is connected, a site passesassetswith the preview origin.- Open: publishing to npm (the licence and the organisation, ADR-0029); templates for own content (product, service, case, article); consent, built when a script needs it; the company’s legal texts as data; a second header pattern for sites with deep navigation.