Designer para autores
Os contratos que um módulo ou um tema usa para estender o page builder, com a referência de controles do schema.
Este é o guia para estender o Designer, não para implementá-lo. A regra que
atravessa a página inteira: todo registro feito aqui fica inerte sem o
Designer. Você registra no seu próprio boot(), e se o módulo não
estiver instalado nada acontece. Um módulo nunca depende do Designer para
funcionar.
Onde os contratos ficam#
Eles estão no core, ao lado dos outros registries. O módulo Designer os consome.
| Contrato | Para |
|---|---|
App\Support\Widgets |
Elementos arrastáveis no editor |
App\Support\Designer\Surfaces |
Telas cujo miolo o Designer pode substituir |
App\Support\Designer\Collections |
Fontes de dados para o elemento de loop |
App\Support\Designer\DynamicTags |
Valores dinâmicos nos controles |
App\Support\Designer\Conditions |
Regras de exibição por nó e por documento |
App\Support\Designer\FormActions |
Ações depois que um formulário é enviado |
App\Support\Designer\Tokens |
Leitura dos tokens do tema ativo |
App\Support\Breadcrumbs |
A trilha que o elemento de breadcrumb lê |
Tudo é estático e registrado em boot(). Os labels são sempre callables:
no momento do boot seu arquivo de idioma pode ainda não estar carregado.
Os registros do lado do módulo estão em Widgets e surfaces. O que segue é o lado do tema e o material de referência.
Tokens do tema#
O Designer não cria tokens: ele lê os que o tema ativo declara. Todo
controle de customizer.json com um var vira um token oferecido nos
pickers de cor, fonte, raio e espaçamento.
A cadeia de pai para filho é resolvida junto com isso, então um child theme
que sobrescreve --color-primary muda todo layout que aponta para esse
token, sem o layout ser tocado.
Quando o tema não cobre algo, o administrador cria uma variável do Designer
(--bd-*), guardada pelo módulo na própria stylesheet. Mudar uma não
republica nada.
themes/<slug>/designer.json#
Opcional, ao lado de customizer.json.
{
"presets": {
"spacing": ["0", "0.25rem", "0.5rem", "1rem", "1.5rem", "2rem", "3rem"],
"shadows": { "soft": "0 1px 2px rgba(15, 23, 42, 0.06)" },
"type_scale": { "sm": "0.875rem", "base": "1rem", "lg": "1.125rem" }
},
"global_classes": [
{ "name": "card", "styles": { "desktop": { "normal": {
"background-color": { "token": "--color-background" },
"border-radius": { "token": "--radius" },
"padding": "1.5rem"
}}}}
],
"surfaces": { "blog.index": "designer/blog-index.json" },
"site_parts": { "header": "designer/header.json" },
"templates": ["designer/templates/hero-1.json"]
}
presetsalimentam os sliders e pickers do inspetor. Um tema com sua própria escala tipográfica faz o editor oferecer essa escala em vez de valores brutos.global_classesentram na biblioteca de classes globais como.bdg-<name>, e o administrador pode editá-las depois.surfaces,site_partsetemplatessão layouts que seu tema sugere. Ativar o tema oferece importá-los; isso nunca sobrescreve o que o usuário já publicou sem confirmação, e os layouts vivem no banco de dados, então trocar de tema preserva o trabalho.
Um valor de estilo pode ser bruto ("1.5rem") ou apontar para um token
({ "token": "--radius" }). Prefira o token: é o que faz um layout
acompanhar uma mudança de tema.
A cascata#
Do mais fraco para o mais forte:
CSS do tema -> CSS do módulo -> variáveis do Designer -> stylesheet do documento -> CSS customizado do tema
Regras da stylesheet compilada do documento:
- sem
!important; - um seletor por nó (
.bd-<id>) e um por classe global (.bdg-<name>), ambos com a mesma especificidade, então a ordem decide, e o estilo local de um nó sempre vem depois da classe global; - desktop first, media queries da mais larga para a mais estreita, o que é o que faz a herança de desktop para tablet e para mobile funcionar sem truques de especificidade;
- estados:
hovervira:hover,focusvira:focus-visible,activevira:active; - dark mode: o estado
darkvira.dark .bd-<id>. Um tema que declara suporte a dark mode não precisa fazer mais nada.
Referência de controles do schema#
Estes são os valores de type aceitos em designer.schema num widget, em
filters numa collection e em schema numa form action. label é sempre
uma chave de idioma.
| Tipo | Valor armazenado | Opções do descritor |
|---|---|---|
text |
string | maxlength, placeholder |
textarea |
string | rows |
code |
string | monospace, nunca executado |
richtext |
HTML sanitizado | o editor do core, sob demanda |
number |
int ou float | min, max, step |
slider |
string com uma unidade ("24px") |
min, max, unit, presets, token_group |
select |
string | options |
select2 |
lista de strings | options, seleção múltipla |
choose |
string | options, radios desenhados como botões |
switcher |
bool | |
color |
string ou {token} |
a paleta do tema no picker |
media |
{path,width,height} |
accept, meta |
icon |
nome de ícone ou um SVG enviado | |
url |
string | apenas esquemas seguros |
gallery |
lista de {image,alt,caption} |
|
repeater |
lista de mapas | fields, add |
dimensions |
{top,right,bottom,left} |
quatro lados com um toggle de vínculo |
font |
string | as famílias do tema |
popup |
id de popup | picker de popups publicados |
heading |
nada | um label de seção |
divider |
nada | uma linha divisória |
Controles que guardam um único valor (text, textarea, url, media,
number, slider, color) recebem o botão de dynamic tag
automaticamente.
Permissões#
O Designer não inventa um sistema de permissões por módulo. Ele usa a matriz de grupo:
- uma surface é restringida por
designer.surfaces.<module>, construída automaticamente a partir das surfaces que você registrou: nada a fazer no seu módulo; - um elemento individual é restringido pelo administrador, em Designer > Element Manager;
- para exigir uma permissão antes que um nó do seu elemento possa ser
salvo, declare
'requires' => 'unfiltered_html'no descritordesigner. É isso que o elemento HTML do core faz.
Checklist antes de publicar#
- Todo label é um callable que retorna
__('...')a partir dos arquivos de idioma do seu módulo. Nenhuma string fixa no PHP, Blade ou JS. - Nenhum acesso direto às tabelas de outro módulo. Guards de
class_existseRoute::hasem toda integração entre módulos. - Tabelas do módulo com prefixo
mod_<slug>_e removidas emonUninstall. - Toda query de collection aplica a visibilidade do módulo dono.
- Toda view de widget escapa tudo, emite um
altobrigatório nas imagens (ou as marca como decorativas) e definewidtheheightquando conhecidos. - Um widget retorna uma string vazia quando não tem nada a mostrar, em vez de uma caixa vazia.
- Nenhum script inline: o CSP do produto bloqueia. JS vai no seu bundle.
- Com o Designer desinstalado, seu módulo funciona identicamente.
- Com o Designer instalado e nenhum layout publicado, a tela mostra exatamente o Blade que mostrava antes.
- Zero violações do axe em toda tela nova, nos dois papéis.