@chrono-os/admin-lp-editor
v0.35.1
Published
Editor visual de Landing Pages (forms por section do @chrono-os/lp-schema, preview vivo em iframe, paletas/cores recentes, sticky CTA, activity feed) com injeção de dependências por context — neutro, consumível por qualquer app admin.
Maintainers
Readme
@chrono-os/admin-lp-editor
Editor visual de Landing Pages — neutro e consumível por qualquer app admin.
Entrega o componente <LPEditor>:
- Forms por section do
@chrono-os/lp-schema(15 tipos: hero, problema, benefícios, depoimentos, FAQ, pricing, form, vídeo, countdown, garantia, footer, programa, instrutores, cta-strip, qualificação interativa). - Preview vivo em iframe (desktop + mobile) por section + sticky CTA, com
postMessagepra refletir edições de cor sem salvar. - Paletas salvas + histórico de cores recentes por kind (
bg/text/hover). - Sticky CTA bar configurável.
- Activity feed (opcional).
O editor não conhece o backend, o roteador, nem o storage do app. Tudo entra por
injeção de dependências via <AdminLpEditorProvider>.
Instalação
yarn add @chrono-os/admin-lp-editor \
@chrono-os/lp-schema @chrono-os/lp-react \
@chrono-os/image-editor-react lucide-reactreact/react-dom >= 18 são peers. @chrono-os/lp-react é peer opcional
(usado pelas rotas de preview do consumer, não pelo editor em si).
Tema — importar o CSS não é mais opcional (0.33.0+)
import '@chrono-os/admin-lp-editor/theme.css'Desde a 0.33.0 as superfícies do editor (fundo, borda, texto, estados) são
CSS vars --alp-*, e não mais variantes dark: hardcoded nos componentes. Sem
esse import tudo cai nos fallbacks inline, que são os valores do tema claro —
o escuro deixa de funcionar. Até 0.32.1 o escuro vinha das classes dark: do
Tailwind do consumer e o pacote não publicava CSS nenhum.
São três temas, no contrato do parque: claro, escuro e black OLED.
| Tema | Marcador no <html> |
|---|---|
| claro | (nenhum) |
| escuro | .dark, [data-mode="dark"] ou [data-theme="dark"] |
| black OLED | o marcador de dark + .oled |
O OLED soma ao escuro: o marcador de dark continua ligado e .oled só
rebaixa fundo, borda e overlay.
Os tokens de superfície, borda e texto forte são var(--neutral-N, <hex do
Tailwind>). Se o app já usa a rampa do @chrono-os/ui-shared, o editor segue a
rampa da marca; se não usa, cai no Tailwind core.
Exceção (0.34.0+): o piso de legibilidade não segue a rampa. Seis tokens —
--alp-text-muted, --alp-text-badge, --alp-text-hint, --alp-text-inert,
--alp-text-faint e --alp-danger — são valores literais do pacote. São os
degraus mais fracos da escada e precisam de AA (≥4,5:1) garantido sobre
--alp-surface, --alp-bg e --alp-bg-strong; a rampa é do consumidor e não dá
essa garantia (medido: neutral-400 sobre neutral-100 dá 2,31:1 no Tailwind
core e 2,23:1 na rampa do Naírio). Se você sobrescrever esses seis, mantenha
AA — há teste no pacote (src/theme.test.ts) que trava o piso em 4,68:1.
Desde a 0.35.0 não sobrou nenhuma classe de cor neutra crua nos componentes: toda superfície, borda, texto e estado passa por token. Ver a seção Tailwind.
Override de token
🔴 Override de token neutro precisa ser por tema. Os blocos do pacote usam
:where(), de especificidade zero, para não atropelar o consumer — então um
:root cru (0,1,0) vence os três e congela o editor nos valores claros
inclusive no escuro e no OLED:
:root:not(.dark) { --alp-border: #C5CBD6; } /* só claro */
:root.dark { --alp-border: #3A4152; } /* só escuro */
:root.oled { --alp-border: #2A2E38; } /* só OLED */--alp-brand-navy é a exceção — cor de marca, igual nos três temas; override
dele em :root é o caminho certo. O accent do editor continua vindo das classes
brand-accent / brand-navy do seu Tailwind, que não variam por tema.
A lista completa dos 38 tokens está no topo do theme.css e no CHANGELOG.md.
Uso
'use client'
import {
LPEditor,
AdminLpEditorProvider,
type AdminLpEditorApi,
type AdminLpEditorNav,
type EditorLp,
} from '@chrono-os/admin-lp-editor'
import { useRouter } from 'next/navigation'
const api: AdminLpEditorApi = {
createLP: (payload) => myApi.createLP(payload),
updateLP: (id, payload) => myApi.updateLP(id, payload),
loadPalettes: () => myApi.loadPalettes(),
savePalette: (name, theme) => myApi.savePalette(name, theme),
deletePalette: (id) => myApi.deletePalette(id),
loadRecentColors: () => myApi.loadRecentColors(),
pushThemeColors: (theme) => myApi.pushThemeColors(theme),
uploadAdapter: myUploadAdapter, // do @chrono-os/image-editor-react
loadActivity: (lpId) => myApi.loadActivity(lpId), // opcional
}
export function EditPage({ lp }: { lp?: EditorLp }) {
const router = useRouter()
const nav: AdminLpEditorNav = {
onCreated: (id) => router.replace(`/admin/dashboard/lps/${id}`),
onCancel: () => router.back(),
statsHref: (id) => `/admin/dashboard/lps/${id}/stats`,
publicHref: (slug) => `/lp/${slug}`,
previewBasePath: '/admin-preview', // default
}
return (
<AdminLpEditorProvider api={api} nav={nav}>
<LPEditor lp={lp} />
</AdminLpEditorProvider>
)
}Rotas de preview
O editor renderiza iframes apontando para:
<previewBasePath>/lp/:id/section/:index?v=...&viewport=desktop|mobile<previewBasePath>/lp/:id/sticky-cta?v=...
O consumer é responsável por servir essas rotas (renderizando a section/sticky
isolada via @chrono-os/lp-react). previewBasePath default é /admin-preview.
Tailwind
Os componentes usam classes utilitárias + tokens do design system Chrono/Naírio
(brand-blue, brand-navy). Inclua o dist deste pacote no content do seu
tailwind.config pra não purgar as classes:
content: [
// ...
'./node_modules/@chrono-os/admin-lp-editor/dist/**/*.{js,cjs}',
]As classes de superfície são arbitrary values (bg-[color:var(--alp-surface,#ffffff)]),
geradas normalmente pelo scanner — mas continuam dependendo do dist estar no
content.
🔴 Nunca use modificador de opacidade sobre a rampa neutra (bg-neutral-50/50,
dark:bg-neutral-800/40…). Com o preset @chrono-os/ui-shared, neutral-N é uma
cor var(), e o Tailwind 3 descarta a utilitária em silêncio — o elemento
fica sem fundo, sem erro nenhum. Foi o defeito consertado em 0.34.0. Translucidez
sobre a rampa vem de token (--alp-bg-veil, --alp-bg-panel…), que declara o
rgba() literal e por cima o color-mix(). Há teste travando isso.
🔴 Classe de cor neutra crua também é proibida (0.35.0+) — text-neutral-400,
bg-neutral-200, hover:text-neutral-600… Sem irmão dark: ela vale o mesmo
valor nos três temas: no claro dá 2,54:1 com a rampa do Naírio, no escuro vira
um bloco claro atravessando o card. E dark: também está proibido no pacote, o
que deixa o token como única saída:
- <span className="text-neutral-400">Tudo salvo</span>
+ <span className="text-[color:var(--alp-text-faint,#6c6c6c)]">Tudo salvo</span>Escolha o token pelo papel que a classe cumpre, não pela proximidade numérica.
Vale o mesmo para red-* cru, que tem --alp-danger. Cor de marca
(brand-accent, brand-navy, blue-*), text-white sobre preenchimento sólido,
o scrim bg-black/70 e fundo inline não-temático (o canvas da LP em preview)
continuam liberados. Dois testes travam isso em src/theme.test.ts.
API pública
| Export | Tipo |
|---|---|
| LPEditor | ({ lp?: EditorLp }) => JSX.Element |
| AdminLpEditorProvider | ({ api, nav, children }) => JSX.Element |
| useAdminLpEditor | () => { api, nav } |
| SECTION_LABELS | Record<LPSectionType, string> |
| defaultSection | (type: LPSectionType) => LPSection |
Tipos: AdminLpEditorApi, AdminLpEditorNav, EditorLp, EditorStickyCta,
SavedPalette, RecentColorsByKind, RecentColorKind, LpThemeState,
ThemeResolvedColors, LpActivity.
