Skip to content
Docs · 11 pages

Docs / Start here

The integration

What `moonarc()` does to your project, and how to turn each part off.

2 min readThis page as MarkdownSource on GitHub

astro.config.mjs
import { defineConfig } from 'astro/config';
import moonarc from 'moonarc';

export default defineConfig({
  integrations: [
    moonarc({
      injectStyles: true, // base tokens on every page
      jsGate: true, // html[data-ma-js] before first paint
      noExternal: false, // vite.ssr.noExternal escape hatch
    }),
  ],
});

injectStyles

Adds import '@moonarc/core/styles/base.css' to every page’s frontmatter through injectScript('page-ssr'), ahead of the page’s own imports, so Vite bundles it first with the rest of your CSS. The file declares the cascade-layer order @layer theme, base, components, utilities (Tailwind’s own order, harmless without Tailwind), puts the tokens in theme, and wraps them in :where(). Override any token with a plain :root { --ma-edge: … } in your own stylesheet; no !important needed. The colour tokens follow the three-state theme rule (light base, system dark unless the page chose light, explicit dark via [data-theme='dark'] or .dark), and --ma-dark is the same rule as a number (0 on a light ground, 1 on a dark one), so a component can lean lighter or darker inside calc() without repeating it.

Turn it off if you want to control stylesheet order yourself, and import the file where you want it. The layer order then has to come before every component’s CSS some other way: a component whose CSS loads first ranks below every reset in base. The manual install puts the statement at the top of <head>.

jsGate

Injects one inline script into <head>:

<script>(f=>addEventListener('astro:after-swap',f,f()))(()=>document.documentElement.dataset.maJs=1)</script>

Entrance effects (Reveal, SplitText) hide content only behind html[data-ma-js]. The router replaces <html>’s attributes on every navigation and never re-runs an inline script it has already run, so the script sets the attribute again on astro:after-swap. The runtime also carries it across a swap, for layouts that inline an older one-line gate. Turn this off if you manage head scripts, and inline gateScript, exported by @moonarc/core, yourself.

noExternal

Vite externalises node_modules for SSR and Node cannot parse .astro. That would surface as ERR_UNKNOWN_FILE_EXTENSION, except that Astro handles .astro files from packages itself. We verify on every release that the package builds without this on Astro 5, 6 and 7, in static and server output. The option exists for setups that override Vite’s SSR externalisation; if you hit the error, set it to true and open an issue.

What it does not do

It adds no components to your pages, no global scripts beyond the 92-byte gate, and nothing at runtime. Every effect is opt-in by import.

motionAttribute

Off by default. When on, every @media (prefers-reduced-motion: reduce) block in the compiled CSS (components, Tailwind’s motion-reduce: utilities, your own styles) is mirrored under html[data-ma-motion='reduce'], and the runtime’s prefersReducedMotion() reads that attribute too. Stamp it from a toggle and the page previews reduced motion as the media query would; see Accessibility. A (prefers-reduced-motion: no-preference) block, which Tailwind’s motion-safe: writes, is not mirrored: motion that only such a block turns on keeps running under the attribute.