# The integration

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

```js title="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](/docs/installation/#manual-install) puts the statement at
the top of `<head>`.

## `jsGate`

Injects one inline script into `<head>`:

```html
<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](/docs/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.