---
title: "v1.8.0"
description: "Payload 3.93 security fixes, a five-minute login lock, authors limited to their own posts, components without stock Tailwind classes, motion and stacking tokens, and Systhema Docs with an MCP server for agents."
url: https://docs.systhema.app/changelog/v1.8.0
version: changelog
docs_index: https://docs.systhema.app/llms.txt
---

## ✨ Highlights

1.8.0 starts with security. It requires Payload 3.93.0, Payload's November 2026 security release, and fixes two weaknesses in Systhema itself: a locked account unlocked after 0.3 seconds instead of five minutes, and the Posts Author role could edit every post. **Every Payload project should upgrade.**

The rest of the release makes Systhema a cleaner base under Tailwind. Components no longer ship stock Tailwind classes, so a class you pass in `className` always wins. Every component takes `classNames` for its inner parts. Typography colours get `text-heading`, `text-copy` and `text-meta`. Motion and stacking come from design tokens. None of this changes how a page looks: each change was compared element by element against 1.7.6 on real consumer sites, at desktop and mobile widths. The documentation moves to [Systhema Docs](https://docs.systhema.app/changelog.md), versioned per release, and coding agents can read it through an MCP server.

### Security: Payload 3.93.0

Payload marks 3.93.0 as "vital security updates":

- Read access on child blocks is now enforced in queries.
- A block field accepts only its configured block types.
- `pasteURL` restrictions apply to remote URL uploads.
- The XML response policy covers XSL.
- The Open Graph endpoint clamps its description.
- The Lexical HTML converters escape stored values.

These fixes have no GHSA ids yet. Every `@payloadcms/*` peer of `@systhemaui/payload` is now `^3.93.0`. Both bundled Payload patches are regenerated as `@payloadcms__ui@3.93.0.patch` and `@payloadcms__richtext-lexical@3.93.0.patch`, with every hunk kept. `systhema upgrade` and `systhema doctor` install them.

Payload 3.92 raised its Node.js floor to `^22.23.4 || >=24.22.1`, so Payload projects need one of those. The Payload template's `engines.node` follows. Projects without Payload stay on Node 20.9 or later, and the CLI on Node 22 (#290).

New projects also get new and raised `pnpm.overrides` floors:

| Override                                                                                                         | Fixes                                                                    |
| ---------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------ |
| `dompurify@>=3.0.0 <3.4.17` → `^3.4.17`                                                                          | 19 advisories on the copy `monaco-editor` pins, through `@payloadcms/ui` |
| `@babel/core@>=7.0.0 <7.29.6` → `^7.29.6`                                                                        | GHSA-4x5r-pxfx-6jf8, through `next > styled-jsx`                         |
| `fast-uri@>=3.0.0 <3.1.9` → `^3.1.9`                                                                             | four advisories                                                          |
| `nodemailer@>=10.0.0 <10.1.4` → `^10.1.4`                                                                        | SMTP and SendGrid projects                                               |
| `undici@>=6.0.0 <6.29.1` → `^6.29.1`, `undici@>=7.0.0 <7.30.1` → `^7.30.1`, `undici@>=8.0.0 <8.12.2` → `^8.12.2` | the October and November undici CVEs                                     |

### Locked accounts stay locked for five minutes

After five failed logins, an account now stays locked for five minutes. Before, it stayed locked for 0.3 seconds, so a password-guessing script was barely slowed down.

The cause was a unit mismatch. The `users.auth.lockTime` option is documented in seconds (default `300`), but it reached Payload unchanged, and Payload reads `lockTime` in milliseconds. Systhema now converts it, and a booted-Payload test pins the lock (#284).

### Authors edit only their own posts

The Posts module's built-in Author role now edits only posts where it is the author. Before, an author could edit any post, including other authors' drafts and the SEO fields of published posts. An author also always becomes the author of the posts they create and save, so they cannot publish under someone else's name or hand a post over. Authors still read every post. Publishers, admins and custom roles with `posts.update` are unchanged.

The role trades `posts.update` for a new `posts.updateOwn` capability. The new exported helper `requireUpdateOwnOrScoped(collectionSlug, ownerField = 'author')` gives your own collections the same rule (#285).

### A Tailwind class in `className` always wins

Systhema components no longer put stock Tailwind classes in their markup. Before, `<Stack direction="col">` rendered `class="stack flex flex-col"`. A `md:flex-row` you passed sat in the same layer, so Tailwind's internal sort order decided which one won. Now every prop maps to a Systhema class that sits below Tailwind's utilities:

| Where                                               | Before                                                     | Now                                                                                       |
| --------------------------------------------------- | ---------------------------------------------------------- | ----------------------------------------------------------------------------------------- |
| `<Stack>`                                           | `flex`, `flex-col`, `justify-between`, `max-md:flex-row` … | `.stack` is a flex box itself; `stack-col`, `stack-justify-between`, `max-md:stack-row` … |
| Stack divider                                       | `self-stretch` / `w-full` plus an inline style             | `stack-divider-vertical` / `stack-divider-horizontal`, restyled with `classNames.divider` |
| Payload Stack block items                           | `flex-1`, `grow`, `self-*`                                 | `stack-item-flex-1`, `stack-item-grow`, `stack-item-self-*`                               |
| `<Image>` / `<Video>` `objectFit`, `objectPosition` | `object-cover`, `object-left-top`                          | `media-fit-cover`, `media-position-top-left`                                              |
| Media blocks' `align` field                         | `mr-auto`, `mx-auto`, `ml-auto`                            | `figure-align-left`, `figure-align-center`, `figure-align-right`                          |
| PostCard, HighlightCard, PostMeta, ShareButtons     | `flex flex-col gap-4`, `absolute inset-0 … p-7` …          | the `.post-*` rules in core's posts CSS                                                   |

Two stock utilities stay on purpose: `aspect-*`, now without brackets (`aspect-4/3`), and `sr-only!`. The layer order is documented in [Cascade layers](../styling/cascade-layers.md) (#286, #287).

### `classNames` for a component's inner parts

`className` styles a component's root. The new `classNames` prop reaches the inner elements the component renders itself. Each value is added after that part's default classes:

```tsx
<Section classNames={{ container: 'max-w-none', content: 'gap-xl' }}>…</Section>
<Carousel classNames={{ thumbs: 'gap-sm', nextButton: 'rounded-full' }}>…</Carousel>
```

It covers Section, the three heroes, Carousel, Gallery, SearchField, Stack, AccordionContent, the Header and Footer wrappers and icons, and the Posts components, in react and in the Next twins. Each component page has a "Styling the parts" table (#286).

### `text-heading`, `text-copy` and `text-meta`

Typography colours get `text-*` classes that pair with type styles the way Tailwind's `text-red-500 text-2xl` do: `text-heading text-h1`, `text-copy text-body`. The colour roles behind them are renamed to match: `typography.body` is now `typography.copy`, and `typography.label` is now `typography.meta`.

Everything old keeps working in 1.x: `color-heading`, `color-body`, `color-label`, `--color-typography-body`, `{typography.body}` references, and `customTokens` overrides under either name. `systhema upgrade` renames the roles in your token files.

`cn()` also stopped dropping Systhema classes. It treated every `text-<word>` as a colour, so `cn('text-h1 text-heading')` returned `text-heading` alone, and a PostCard title lost `text-h4` when you passed `text-white` (#286, #292).

### Motion and stacking from tokens

Component durations, easing curves and page-level `z-index` values now come from tokens. Every value falls back to what it was before:

- **`transition` collection.** It drives the header menu and its overlay, sub-menus, chevrons and toggle, the checkbox and radio marks, the video play overlay and its hover zoom, the share pills and the cookie banner. For example, `header.menu.transitionDuration` becomes `--transition-header-menu-transition-duration`.
- **`zIndex` collection (new).** It sets `--z-index-header` (40), `headerMenu` (50), `headerMenuOverlay` (45), `postShare` (40), `cookieConsent` (100) and `skipLink` (9999).
- **Durations.** `easings` gains `fast` (150ms), `normal` (300ms) and `slow` (500ms).

Core ships all of these at the values components already used, and the upgrade adds them to your project, so you can tune motion in Figma. With reduced motion on, the mobile menu, its sub-menus, chevrons and toggle, and the media hover zoom now snap to their end state instead of animating (#289, #292). See [Motion tokens](../design/motion-tokens.md) and [Token collections](../design/token-collections.md).

### The cookie banner without `!important`

The banner now loads vanilla-cookieconsent's stylesheet inside `@layer components`. Unlayered, it beat every layer, so Systhema's banner theme needed `!important` on almost every declaration. 136 of the theme's 143 `!important` declarations are gone, and your own CSS in `@layer app` now overrides the banner without `!important`.

The banner's category switches also read their own `cookieConsent.toggle.*` tokens. By default they alias the form switch tokens, so the banner looks the same. Detach one in Figma to style the banner's switch on its own (#286, #292).

### Systhema Docs and an MCP server for agents

The documentation now lives at [docs.systhema.app](https://docs.systhema.app/changelog.md), with a page for every component, utility family and Payload block, and live demos. The site keeps a copy of the docs for each minor version. The Editor guide for site owners, the home page and the release notes are public. The developer docs need the GitHub token you already use to install Systhema, or an issued access key.

Coding agents get the docs for the project's installed version through a `systhema-docs` MCP server:

```bash
systhema mcp add docs --agent claude,codex,cursor --scope project
systhema mcp list
```

`systhema mcp` writes the entry for Claude Code, Codex, Cursor, VS Code, Gemini CLI and OpenCode. The entry runs `systhema mcp serve docs`, which sends the key the CLI already uses for the registry, so no key ends up in an MCP config. A new `docs-access` doctor check warns when that key is missing or no longer opens the docs.

The consumer skills shrink from about 95,000 to 18,000 words. They keep the field-tested guardrails and look everything else up in the docs. Runtime messages that cite a page now link `https://docs.systhema.app/v/<installed version>/…` instead of a repository path (#264). See [MCP server](../ai-agents/mcp-server.md) and [`systhema mcp`](../cli/mcp.md).

### Smaller stylesheet, missing post CSS restored

Projects now scan a class list that each package build writes (`dist/systhema-classes.txt`) instead of Systhema's compiled JavaScript:

- Doc comments and JavaScript negations no longer generate CSS. A consumer-style compile drops from 543.8 KB to 401.8 KB raw (25.8 KB brotli).
- Post pages get the CSS for `post-byline-meta`, `post-meta-author-role` and `post-related-title` back. Payload's frontend templates were never scanned.
- The header menu toggle's hover icon colour now applies. A selector typo had stopped it from matching.

(#286, #287)

---

## ⬆️ Upgrade

```bash
pnpm add -g @systhemaui/cli@latest
cd <your-project>
systhema upgrade --dry-run
systhema upgrade --yes
```

The upgrade moves Payload projects to Payload 3.93.0 and installs both bundled patches. It runs no codemods and no database hooks. Its token step offers one batch that changes nothing on the page:

- It renames `typography.body` to `copy` and `typography.label` to `meta` in `colorSystem`. The `body` type size in `responsiveSizing` is left alone. Core's three `{typography.body}` references are rewritten only when they still hold the stock value; other references resolve through the alias.
- It adds `fast`, `normal` and `slow` to `easings`.
- It writes the `transition` and `zIndex` collections and their manifest entries.
- It points the cookie banner's `cookieConsent.toggle.*` tokens at the form switch tokens. The banner always rendered the form switch, so nothing visible changes.

`footer.social.iconSize` is not added to existing projects. On a 1.7 project the footer's social icons size from their own glyph aspect, and the token would square them at 24px. That visible change waits for 2.0. New projects get the token from core.

**Check Node.js on Payload projects.** You need `^22.23.4` or `>=24.22.1`. Update `.nvmrc`, `.node-version`, Docker base images and your host's Node setting.

**Check `users.auth.lockTime`.** If you set it in milliseconds to work around the bug (for example `300000`), set it in seconds now (`300`). Otherwise the lock lasts 1,000 times longer.

**Give authors `posts.update` if they must edit each other's posts**, through a custom role, `overrideRoles` or per-user capabilities. Posts without an author can no longer be edited by authors; a publisher can assign one.

**Copy the new override floors into existing projects.** `systhema upgrade` does not write `pnpm.overrides`. Add the floors from the table above to your `pnpm.overrides`, then run `pnpm install`.

**Connect your agents to the docs** with `systhema mcp add docs --agent <agents>`.

---

## ⚠️ Breaking & Behavioral Changes

### 1. Payload projects need Payload 3.93 and Node.js 22.23.4 or 24.22.1

The `payload` and `@payloadcms/*` peers of `@systhemaui/payload` are `^3.93.0`. Payload 3.92 raised Payload's own Node floor to `^22.23.4 || >=24.22.1`. The vendored AI SDK moves to version 7. Its deprecated names (`system`, `totalUsage`, `toTextStreamResponse`) are replaced inside the package, and the AI module's behaviour and stored usage are unchanged (#290).

### 2. `users.auth.lockTime` reaches Payload in milliseconds

The option stays in seconds and is now multiplied by 1,000. A project that already passed milliseconds gets a lock 1,000 times longer (#284).

### 3. The Author role edits only its own posts

Authors lose `posts.update` and get `posts.updateOwn`. Their `author` field is always set to themselves on create and save (#285).

### 4. Components render Systhema classes instead of stock Tailwind classes

Computed styles are unchanged. Markup changes in three ways:

- CSS, tests or scripts that select Systhema components by `.flex`, `.flex-col`, `.object-cover` or `.mx-auto` no longer match. Select by the Systhema class instead.
- Default aspect classes lose their brackets: `aspect-[4/3]` is now `aspect-4/3`.
- Thirteen rules that only comments or plain code in Systhema's packages ever triggered are no longer generated from them: `.grid`, `.contents`, `.object-cover`, `.fixed`, `.border`, `.transform`, `.resize`, `.ordinal`, `.visible`, `.grow`, `.shrink`, `.line-clamp-5` and `.line-clamp-none`. A project that uses them in its own markup still gets them from its own scan.

(#287)

### 5. `GalleryWrapper`'s `className` goes to its root

It used to go to every slide. Use `classNames.slide` for the slides (#286).

### 6. Deprecated: `color-*` classes and the `body` and `label` colour roles

`color-heading`, `color-body`, `color-label`, `typography.body`, `typography.label` and `--color-typography-body`/`--color-typography-label` keep working in 1.x and will be removed in 2.0. Use `text-heading`, `text-copy`, `text-meta`, `typography.copy`, `typography.meta` and `--color-typography-copy`/`--color-typography-meta`.

A project colour role whose `text-` name clashes with one of its own text styles keeps only its `color-<role>` class, with a build warning (#286, #292).

### 7. Reduced motion stops more parts from moving

With `prefers-reduced-motion: reduce`, the mobile menu panel, its overlay, sub-menus, chevrons and toggle lines, and the media overlay image zoom now get `transition-duration: 0s`. Colour fades keep their timing (#289).

### 8. The cookie banner's library CSS sits in `@layer components`

Your own overrides with `!important` still win. Unlayered project CSS now beats the library's rules too. The library moves to vanilla-cookieconsent 3.3.0, which appends the modal to `<body>` and reads `navigator.languages` (#286, #290).

### 9. Runtime messages link docs.systhema.app

Messages that printed a repository path or a GitHub URL now link the public docs page for the installed version. Developer pages need the registry token or an access key (#264).

---

## 🐛 Fixes & Internal Improvements

### Bug Fixes

- **`spacing-reset` works on Tailwind 4.3.4 and later.** A root `theme('spacing')` started returning the whole spacing scale, so the class emitted a broken rule instead of `--spacing: 4px` (#290)
- **A repeated swapped spacing variable is wrapped once.** `var(--x) var(--x)` became a double-wrapped `--spacing()` in the header menu toggle's `mask-size` (#286)
- **The Next and HTML starters ship the cookie consent tokens.** 48 `--cookie-consent-*` variables were unresolved in new projects (#286)
- **The columns spacer drops its nine `!important`s**, so a project can size or show it (#286)
- **The Figma plugin reads Figma's current composed colours.** Since November 2026, Figma hands an alias with an opacity over as `{ color, opacity }`. The plugin exported five foundation colours as raw objects that core could not read. It now exports and re-imports them correctly. The plugin is renamed Systhema Design Tokens Manager and shows its version in the window (#291)
- **The doctor's `missing-tokens` check** no longer reports an addition into a collection that a migration introduces; the missing collection is the one finding (#292)

### Internal / Monorepo

- **Node 24.22.3** for the monorepo, CI and the demo and design images, after the October and November Node security releases (#279, #288)
- **Every other dependency bump that passes**: ESLint 10, Next 16.4.6, Tailwind 4.3.5, Vite 8.3.8 and Vitest 5.1 among them. The undici CVEs are patched across the workspace (#280, #290)
- **The demo installs its security overrides** and keeps them when its Docker build pins the Payload family (#281, #282, #283)
- **A token `rename` takes an optional `target` collection.** `typography.body` is also a type size in `responsiveSizing`, so the role rename would otherwise have renamed both (#292)
- **`docs/` is restructured into the Systhema Docs tree**, with `pnpm docs:generate`, `docs:check`, `docs:snapshot` and `docs:keys`, regenerable screenshots and Editor guide translations (#264)

---

## List of all changes

### 🚀 Features

#### apps/docs

- feat(apps/docs): add Systhema Docs, versioned docs with an MCP server for agents (#264) (b43089b7)

#### core

- feat(core): read component motion and stacking from tokens (#289) (20d8cdd1)
- feat(core): follow the refreshed Figma starter tokens (#292) (6b45bf54)

#### figma

- feat(figma): rename the plugin and read figma's new composed colours (#291) (008f9d71)

### 🐛 Bug fixes

#### apps/demo

- fix(apps/demo): update published systhema pins to 1.7.6 (#281) (07c8caa7)
- fix(apps/demo): apply the template security overrides to the demo install (#282) (edbb9cb0)
- fix(apps/demo): keep the security overrides when pinning the payload family (#283) (0821f308)

#### core, react, next

- fix(core,react,next): restore missing css, order the cascade and expose component parts (#286) (99ef88b0)

#### core, react, next, payload

- fix(core,react,next,payload): ship systhema classes only and scan a class manifest (#287) (621d7ab0)

#### deps

- fix(deps): patch undici CVEs (#280) (149b035f)

#### payload

- fix(payload): lock an account for five minutes, not 0.3 seconds, after failed logins (#284) (8b80ca19)
- fix(payload): limit the author role to editing their own posts (#285) (5b24fdfa)

#### general

- fix: pin Node 22.23.4 (October 2026 security release) (db3d5268)
- fix: pin Node 24.22.3 (November 2026 security release) (#288) (1762dfda)

### 🧹 Chores

#### apps/demo, apps/design

- chore(apps/demo,apps/design): use node 24 for apps and ci (#279) (be7311cc)

#### deps

- chore(deps): refresh dependencies for 1.8.0 (#290) (e487324e)
