Blocos de página: 15 seções acessíveis (hero, preços, FAQ com JSON-LD…), editor no admin e registro extensível
Instalar
genpm add @core/blocksO que você recebe
- Código em src/lib/blocks/, 25 arquivos. (52,7 kB)
- Regras de IA em src/lib/blocks/AGENTS.md, mais arquivos de regras da IDE.
- Resolve @core/admin, @core/contracts, @core/db, @core/media, @core/rich-text, @core/ui para você.
README
Este pacote não tem README.
Isto é exatamente o que sua IA lê quando trabalha em src/lib/blocks. Nada mais é adicionado ao contexto dela.
@core/blocks — rules for AI agents
Purpose
Page sections that editors combine from the admin: hero, features, benefits, testimonials, logos, pricing, FAQ (with FAQPage JSON-LD), call to action, gallery, video, stats, comparison, guarantee, rich text and form. Each block is a zod schema + editor fields + a React component. Other packages and kits add their own blocks (product grid, latest posts…) to the same registry. Blocks are stored as JSON inside @core/content entries; no tables of its own.
Map
index.ts—defineBlock,createBlockRegistry,coreBlocks(and each block),RenderBlocks,collectMediaIds.core/*.ts— one file per block.parts.ts—Section,Heading,Img,CtaLink,JsonLdfor new blocks.media.ts—loadBlockMedia(blocks, registry)(server: one query to @core/media).editor.ts—'use client':blocksRenderer(blockMeta(registry))forAdminApp.blocks.css— styles on @core/ui tokens.
Integration
- Create
src/genpm/blocks.ts:export const blocks = createBlockRegistry([...coreBlocks /*, kit blocks */]); - Add a field to a content collection: schema
blocks: blocks.schema, admin fieldblocks.field('blocks', 'Sections'). - Admin: compute
blockMeta(blocks)on the server and pass it to the client component that renders<AdminApp renderers={{ blocks: blocksRenderer(meta), /* richText, media… */ }} />(never import the registry in client code). - Page (server component):
const media = await loadBlockMedia(page.data.blocks, blocks); return <RenderBlocks blocks={page.data.blocks} registry={blocks} media={media} renderForm={(key) => <Form formKey={key} />} embeds={hasConsent('marketing') ? 'iframe' : 'link'} />; - Styles: import
src/lib/ui/ui.cssthensrc/lib/blocks/blocks.cssin the root layout. - Verify: a page with every block passes axe and has one
h1; a block with an unknown type renders nothing and warns in dev.
Conventions
- A new block = new file with
defineBlock+ entry in the registry. Never addif (type === …)toRenderBlocks. - Use
SectionandHeadingwithctx.level(items one level below); only blocks withpageTitlemay become theh1. - Images are media ids resolved with
loadBlockMedia; links go through thehrefschema (nojavascript:). - Text in blocks is plain text; use the
rich-textblock for formatting.
Don't
- Don't render user HTML (
dangerouslySetInnerHTML) except viaJsonLd, which escapes<,>and&. - Don't load video iframes without marketing consent (
embeds: 'link'is the default). - Don't invent testimonials, ratings or statistics; the editor help texts remind users these must be real and verifiable.
# @core/blocks — rules for AI agents
## Purpose
Page sections that editors combine from the admin: hero, features, benefits, testimonials, logos, pricing, FAQ (with
FAQPage JSON-LD), call to action, gallery, video, stats, comparison, guarantee, rich text and form. Each block is a
zod schema + editor fields + a React component. Other packages and kits add their own blocks (product grid, latest
posts…) to the same registry. Blocks are stored as JSON inside @core/content entries; no tables of its own.
## Map
- `index.ts` — `defineBlock`, `createBlockRegistry`, `coreBlocks` (and each block), `RenderBlocks`, `collectMediaIds`.
- `core/*.ts` — one file per block. `parts.ts` — `Section`, `Heading`, `Img`, `CtaLink`, `JsonLd` for new blocks.
- `media.ts` — `loadBlockMedia(blocks, registry)` (server: one query to @core/media).
- `editor.ts` — `'use client'`: `blocksRenderer(blockMeta(registry))` for `AdminApp`. `blocks.css` — styles on @core/ui tokens.
## Integration
1. Create `src/genpm/blocks.ts`: `export const blocks = createBlockRegistry([...coreBlocks /*, kit blocks */]);`
2. Add a field to a content collection: schema `blocks: blocks.schema`, admin field `blocks.field('blocks', 'Sections')`.
3. Admin: compute `blockMeta(blocks)` on the server and pass it to the client component that renders
`<AdminApp renderers={{ blocks: blocksRenderer(meta), /* richText, media… */ }} />` (never import the registry in client code).
4. Page (server component):
```tsx
const media = await loadBlockMedia(page.data.blocks, blocks);
return <RenderBlocks blocks={page.data.blocks} registry={blocks} media={media}
renderForm={(key) => <Form formKey={key} />} embeds={hasConsent('marketing') ? 'iframe' : 'link'} />;
```
5. Styles: import `src/lib/ui/ui.css` then `src/lib/blocks/blocks.css` in the root layout.
6. Verify: a page with every block passes axe and has one `h1`; a block with an unknown type renders nothing and warns in dev.
## Conventions
- A new block = new file with `defineBlock` + entry in the registry. Never add `if (type === …)` to `RenderBlocks`.
- Use `Section` and `Heading` with `ctx.level` (items one level below); only blocks with `pageTitle` may become the `h1`.
- Images are media ids resolved with `loadBlockMedia`; links go through the `href` schema (no `javascript:`).
- Text in blocks is plain text; use the `rich-text` block for formatting.
## Don't
- Don't render user HTML (`dangerouslySetInnerHTML`) except via `JsonLd`, which escapes `<`, `>` and `&`.
- Don't load video iframes without marketing consent (`embeds: 'link'` is the default).
- Don't invent testimonials, ratings or statistics; the editor help texts remind users these must be real and verifiable.
A árvore exata que será injetada, após o .genpmignore. Fixada em
/* Estilos de los bloques sobre los tokens de @core/ui. Cópialos o impórtalos después de ui.css. */
.blk { padding-block: clamp(2rem, 6vw, 5rem); padding-inline: 1rem; max-width: 72rem; margin-inline: auto; }
.blk__title { margin: 0 0 0.75rem; font-size: clamp(1.5rem, 3vw, 2.25rem); line-height: 1.2; }
.blk__intro { color: var(--ui-muted); max-width: 48rem; margin: 0 0 2rem; }
.blk__note { color: var(--ui-muted); font-size: 0.875rem; }
.blk__actions { display: flex; flex-wrap: wrap; gap: 0.75rem; margin-block-start: 1.5rem; }
.blk-hero { display: grid; gap: 2rem; align-items: center; grid-template-columns: repeat(auto-fit, minmax(18rem, 1fr)); }
.blk-hero--center { text-align: center; grid-template-columns: 1fr; justify-items: center; }
.blk-hero__eyebrow { color: var(--ui-primary); font-weight: 600; margin: 0 0 0.5rem; }
.blk-hero__title { font-size: clamp(2rem, 5vw, 3.5rem); line-height: 1.1; margin: 0; }
.blk-hero__subtitle { font-size: 1.125rem; color: var(--ui-muted); max-width: 40rem; }
.blk-hero__image, .blk img { max-width: 100%; height: auto; border-radius: var(--ui-radius); }
.blk-grid { display: grid; gap: 1.5rem; list-style: none; padding: 0; margin: 0; grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr)); }
@media (min-width: 64rem) { .blk-grid--2 { grid-template-columns: repeat(2, 1fr); } .blk-grid--3 { grid-template-columns: repeat(3, 1fr); } .blk-grid--4 { grid-template-columns: repeat(4, 1fr); } }
.blk-card { border: 1px solid var(--ui-border); border-radius: var(--ui-radius); background: var(--ui-surface); padding: 1.25rem; }
.blk-card__icon { font-size: 1.5rem; }
.blk-split { display: grid; gap: 2rem; grid-template-columns: repeat(auto-fit, minmax(18rem, 1fr)); align-items: center; }
.blk-checklist { list-style: none; padding: 0; display: grid; gap: 0.5rem; }
.blk-checklist li::before { content: "✓ "; color: var(--ui-success); font-weight: 700; }
.blk-rating { color: var(--ui-warning); margin: 0; }
.blk-card blockquote { margin: 0.5rem 0; }
.blk-avatar { width: 2.5rem; height: 2.5rem; border-radius: 50%; object-fit: cover; vertical-align: middle; margin-inline-end: 0.5rem; }
.blk-logos { display: flex; flex-wrap: wrap; gap: 2rem; align-items: center; justify-content: center; list-style: none; padding: 0; }
.blk-logos img { max-height: 3rem; width: auto; filter: grayscale(1); }
.blk-plan--highlighted { border-color: var(--ui-primary); border-width: 2px; }
.blk-plan__price strong { font-size: 2rem; }
.blk-faq__item { border-block-end: 1px solid var(--ui-border); padding-block: 0.75rem; }
.blk-faq__item summary { cursor: pointer; font-weight: 600; }
.blk-cta { text-align: center; }
.blk-cta--primary { background: var(--ui-surface); border-radius: var(--ui-radius); }
.blk-gallery { display: grid; gap: 1rem; list-style: none; padding: 0; grid-template-columns: repeat(auto-fill, minmax(14rem, 1fr)); }
.blk-gallery figure { margin: 0; }
.blk-gallery img { aspect-ratio: 4 / 3; object-fit: cover; width: 100%; }
.blk-video { position: relative; aspect-ratio: 16 / 9; }
.blk-video iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; border-radius: var(--ui-radius); }
.blk-stats { display: grid; gap: 1.5rem; grid-template-columns: repeat(auto-fit, minmax(10rem, 1fr)); margin: 0; }
.blk-stats div { display: flex; flex-direction: column-reverse; }
.blk-stats dd { margin: 0; font-size: 2.5rem; font-weight: 700; }
.blk-stats dt { color: var(--ui-muted); }
.blk-guarantee { text-align: center; max-width: 48rem; }
.blk-guarantee__badge { display: inline-block; border: 2px solid var(--ui-success); color: var(--ui-success); border-radius: 999px; padding: 0.25rem 1rem; font-weight: 700; }
.blk-rich-text--narrow { max-width: 44rem; }
.blk-editor { list-style: none; padding: 0; margin: 0; }
Este pacote não declara servidores MCP.
| Versão | Commit | Publicado | Análise |
|---|---|---|---|
| 1.0.0 | a255d6d | há 5 horas | análise aprovada |
- genpm
- @core/admin ^1.0.0@core/contracts ^1.0.0@core/db ^1.0.0@core/media ^1.0.0@core/rich-text ^1.0.0@core/ui ^1.0.0
- proposto
- O GenPM propõe o comando npm e só o executa se você disser sim.
- Usado por (1)
- @core/kit-cms ^1.0.0
- análise
- análise aprovada · 0 achados
- commit
- v1.0.0 → a255d6d08201d6342d07ad41d96123ea1407a5f1 · verificado após o download
- scripts
- Nenhum. O GenPM nunca executa código de pacotes.
- licença
- MIT
- Qualidade
- 100/100
- Licença reconhecidacumprido
- AGENTS.md explica o propósitocumprido
- AGENTS.md tem passos de integraçãocumprido
- AGENTS.md lista convenções ou proibiçõescumprido
- Inclui testescumprido
- Escaneamento de segurança aprovadocumprido
- Publicado nos últimos 6 mesescumprido
- Publicador verificadocumprido
- Resumo e palavras-chavecumprido
- denúncia
- Viu algo errado?