@@ -102,35 +102,41 @@ fetches them at **runtime** (ISR ~60s), so changes appear **without a redeploy**
102102CMS is unreachable the site renders unbranded/default-themed rather than crashing — there
103103is no ` press.config ` fallback for identity by design.
104104
105- ** Custom blocks** — the engine ships a ` press.* ` core palette (paragraph, heading, list,
106- quote, image, button, separator, spacer). To add your own, drop a Strapi component under
107- ` packages/cms/src/components/custom/ ` , a matching React component under
105+ ** Custom blocks** — the engine ships a ` preset-* ` Atomic Design palette: atoms
106+ (` preset-atom.paragraph ` , ` heading ` , ` list ` , ` quote ` , ` image ` , ` button ` , ` separator ` ,
107+ ` spacer ` ) and organisms (` preset-organism.hero ` , ` cta ` , ` navbar ` , ` footer ` ). To add your
108+ own, drop a Strapi component under ` packages/cms/src/components/custom-<layer>/ ` (the
109+ folder names the atomic layer, e.g. ` custom-organism/ ` ), a matching React component under
108110` packages/web/blocks/custom/ ` , and wire it in ` packages/web/blocks/custom/index.ts ` :
109111
110112``` ts
111113export const customBlocks: Record <string , ComponentType <any >> = {
112- ' custom.callout' : Callout ,
114+ ' custom-organism .callout' : Callout ,
113115};
114116```
115117
116- Any ` custom.* ` component is auto-admitted into the page's Dynamic Zone, and the
117- materialized host passes this map to its block renderer — so your blocks render
118- server-side alongside the engine's built-in blocks.
118+ Every ` custom-* ` component is auto-admitted into ** all** engine Dynamic Zones — the page
119+ ` body ` and the site-setting ` header ` /` footer ` — so the editor decides placement in the
120+ picker (unlike the engine's own ` preset-* ` blocks, whose placement is curated per
121+ content-type). The materialized host passes this map to its block renderer, so your blocks
122+ render server-side alongside the engine's built-in blocks.
119123
120- ** Overriding a reference block** — the ` press. *` palette ships as defaults, not a
124+ ** Overriding a reference block** — the ` preset- *` palette ships as defaults, not a
121125sealed set. The block renderer merges your map * over* the engine's
122- (` { ...referenceBlocks, ...customBlocks } ` ), so mapping the ** same key** shadows a
123- built-in with your own React component — markup and behavior become yours. This is
124- ** web-only** : the CMS still serves ` press.button ` and the editing experience is
125- unchanged, so existing content keeps rendering.
126+ (` { ...atomBlocks, ...organismBlocks, ...customBlocks } ` ), so mapping the ** same key**
127+ shadows a built-in with your own React component — markup and behavior become yours. Any
128+ ` preset-atom.* ` or ` preset-organism.* ` key is overridable this way (e.g.
129+ ` 'preset-organism.hero': MyHero ` ). This is ** web-only** : the CMS still serves
130+ ` preset-atom.button ` and the editing experience is unchanged, so existing content keeps
131+ rendering.
126132
127133``` tsx
128134// packages/web/blocks/custom/Button.tsx — take the same props the engine block gets.
129- import type { PressButton } from ' @ogs-tech/press-web' ;
135+ import type { PresetAtomButton } from ' @ogs-tech/press-web' ;
130136
131- export function Button({ label , href , variant }: PressButton ) {
137+ export function Button({ label , href , variant }: PresetAtomButton ) {
132138 // Keep `data-block` to inherit the default theme.css styling and only tweak markup;
133- // drop it to fully own the look (the engine's `[data-block="press .button"]` rule
139+ // drop it to fully own the look (the engine's `[data-block="preset-atom .button"]` rule
134140 // then matches nothing and goes inert).
135141 return <a className = " cta" href = { href } data-variant = { variant ?? ' primary' } >{ label } </a >;
136142}
@@ -139,19 +145,19 @@ export function Button({ label, href, variant }: PressButton) {
139145``` ts
140146// packages/web/blocks/custom/index.ts — map the SAME key to shadow the default.
141147export const customBlocks: Record <string , ComponentType <any >> = {
142- ' custom.callout' : Callout ,
143- ' press .button' : Button , // overrides the engine's press .button
148+ ' custom-organism .callout' : Callout ,
149+ ' preset-atom .button' : Button , // overrides the engine's preset-atom .button
144150};
145151```
146152
147153Your override is yours to maintain: ` press upgrade ` keeps updating the engine's
148154* default* (which your map overrides) but never touches your component. If a future
149- engine version changes a block's prop ** shape** , the generated ` Press *` type changes
155+ engine version changes a block's prop ** shape** , the generated ` Preset *` type changes
150156and ` tsc ` flags your override at its call site — so drift fails loud rather than
151157silently rendering wrong.
152158
153159** Reusing a reference block inside a custom block** — the inverse of overriding: instead
154- of * replacing* a ` press. *` block you * embed* one. Each reference block is exported
160+ of * replacing* a ` preset- *` block you * embed* one. Each reference block is exported
155161individually from ` @ogs-tech/press-web ` (` Heading ` , ` Paragraph ` , ` List ` , ` Quote ` ,
156162` Image ` , ` Button ` , ` Separator ` , ` Spacer ` ), so a custom block can reuse the engine's
157163server-rendered, theme-aware markup instead of reimplementing it. You feed each one from
@@ -160,27 +166,27 @@ your **own** schema fields — the presentation is reused, the data stays yours.
160166``` tsx
161167// packages/web/blocks/custom/Callout.tsx
162168import { Heading , Paragraph , Button } from ' @ogs-tech/press-web' ;
163- import type { CustomCallout } from ' <name>-shared/types' ;
169+ import type { CustomOrganismCallout } from ' <name>-shared/types' ;
164170
165- export function Callout({ title , body , ctaLabel , ctaHref }: CustomCallout ) {
171+ export function Callout({ title , body , ctaLabel , ctaHref }: CustomOrganismCallout ) {
166172 return (
167- <aside data-block = " custom.callout" >
168- <Heading text = { title } level = " 3" /> { /* press .heading → { text, level } */ }
169- <Paragraph content = { body } /> { /* press .paragraph → { content } */ }
173+ <aside data-block = " custom-organism .callout" >
174+ <Heading text = { title } level = " 3" /> { /* preset-atom .heading → { text, level } */ }
175+ <Paragraph content = { body } /> { /* preset-atom .paragraph → { content } */ }
170176 <Button label = { ctaLabel } href = { ctaHref } variant = " primary" />
171177 </aside >
172178 );
173179}
174180```
175181
176- The one gotcha is ** prop shape** — the generated ` Press *` types are the source of truth.
182+ The one gotcha is ** prop shape** — the generated ` Preset *` types are the source of truth.
177183` Heading ` wants ` { text, level } ` and ` Button ` wants ` { label, href, variant } ` (plain
178184scalars), but ` Paragraph ` /` List ` /` Quote ` want ` { content } ` — a Strapi ** blocks** value,
179185* not* a string. So to feed a ` Paragraph ` , the custom block's own schema needs a ` blocks `
180186attribute:
181187
182188``` jsonc
183- // packages/cms/src/components/custom/callout.json
189+ // packages/cms/src/components/custom-organism /callout.json
184190" attributes" : {
185191 " title" : { " type" : " string" , " required" : true },
186192 " body" : { " type" : " blocks" }, // ← feeds <Paragraph content={body} />
@@ -189,12 +195,12 @@ attribute:
189195}
190196```
191197
192- The embedded child carries its own ` data-block ` (e.g. ` press .heading` ), so it inherits
193- the default theme.css styling for free; the wrapper's ` data-block="custom.callout" ` is
194- your block's own hook. Reuse is ** web-only and decided by you ** — the editor fills your
195- custom block's flat fields, not a nested press block; you choose the layout. (Letting the
196- * editor* compose ` press. *` blocks inside a custom component is a different, schema-level
197- feature and not currently supported.)
198+ The embedded child carries its own ` data-block ` (e.g. ` preset-atom .heading` ), so it
199+ inherits the default theme.css styling for free; the wrapper's
200+ ` data-block="custom-organism.callout" ` is your block's own hook. Reuse is ** web-only and
201+ decided by you ** — the editor fills your custom block's flat fields, not a nested preset
202+ block; you choose the layout. (Letting the * editor* compose ` preset- *` blocks inside a
203+ custom component is a different, schema-level feature and not currently supported.)
198204
199205## Updating the engine
200206
@@ -221,8 +227,9 @@ code.
221227
222228- ` packages/cli ` — ` @ogs-tech/create-press ` : the run-once scaffolder invoked via ` pnpm create @ogs-tech/press ` ; not added as a project dependency.
223229- ` packages/cms ` — the ** engine** Strapi plugin (` @ogs-tech/press-cms ` ): ships the
224- ` page ` + ` site-setting ` content-types, injects the ` press.* ` reference palette into
225- the page Dynamic Zone, and serves the type-sync contract at ` /api/press/schema ` .
230+ ` page ` + ` site-setting ` content-types, injects the ` preset-* ` Atomic Design palette
231+ (atoms/molecules/organisms/config) into the engine Dynamic Zones, and serves the
232+ type-sync contract at ` /api/press/schema ` .
226233- ` packages/web ` — the ** engine** web layer (` @ogs-tech/press-web ` ): the Next host
227234 template, block renderer, config helpers, and CMS→types sync.
228235- ` packages/shared ` — ` @ogs-tech/press-shared ` : framework-agnostic contract types
0 commit comments