Ir para o conteúdo
Bondry

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.

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"]
}
  • presets alimentam 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_classes entram na biblioteca de classes globais como .bdg-<name>, e o administrador pode editá-las depois.
  • surfaces, site_parts e templates sã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:

Código
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: hover vira :hover, focus vira :focus-visible, active vira :active;
  • dark mode: o estado dark vira .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 descritor designer. É isso que o elemento HTML do core faz.

Checklist antes de publicar#

  1. Todo label é um callable que retorna __('...') a partir dos arquivos de idioma do seu módulo. Nenhuma string fixa no PHP, Blade ou JS.
  2. Nenhum acesso direto às tabelas de outro módulo. Guards de class_exists e Route::has em toda integração entre módulos.
  3. Tabelas do módulo com prefixo mod_<slug>_ e removidas em onUninstall.
  4. Toda query de collection aplica a visibilidade do módulo dono.
  5. Toda view de widget escapa tudo, emite um alt obrigatório nas imagens (ou as marca como decorativas) e define width e height quando conhecidos.
  6. Um widget retorna uma string vazia quando não tem nada a mostrar, em vez de uma caixa vazia.
  7. Nenhum script inline: o CSP do produto bloqueia. JS vai no seu bundle.
  8. Com o Designer desinstalado, seu módulo funciona identicamente.
  9. Com o Designer instalado e nenhum layout publicado, a tela mostra exatamente o Blade que mostrava antes.
  10. Zero violações do axe em toda tela nova, nos dois papéis.