Personalizar templates, componentes e covers
Layouts, componentes e templates de cover pertencem ao projeto em Content/Views/; nenhuma alteração em Core/ é necessária. O fluxo é o mesmo para um site novo e para o Content/ de referência.
Qual sintaxe usar em cada lugar
| Contexto | O que funciona | Como é escolhido |
|---|---|---|
Página .mdx |
Markdown GFM, HTML permitido e tags <x-...> de componentes Blade |
extra.layout no front matter, ou layouts.app |
Página .blade.php |
Blade completo, herança, seções, stacks e componentes | @extends('layouts.nome') |
| Layout | Blade completo, componentes e @yield/@section |
Referenciado pela página |
| Cover | Blade completo e componentes; HTML/CSS próprios para a imagem | cover no metadata, ou covers.cover |
Em MDX, somente a tag do componente é executada como Blade. Echos ({{ ... }}), blocos {!! ... !!} e diretivas (@if, @foreach etc.) saem como texto literal. O conteúdo do slot também não volta ao parser Markdown: **negrito** dentro de um componente continua sendo texto, não <strong>.
1. Criar e usar um componente
Crie Content/Views/Components/ui/notice.blade.php:
@props(['tone' => 'info'])
<aside {{ $attributes->merge(['class' => 'border p-4']) }} data-tone="{{ $tone }}">
{{ $slot }}
</aside>
Use-o em uma página Blade:
<x-ui.notice tone="warning" class="mt-6">
Revise a configuração antes do build.
</x-ui.notice>
O mesmo componente pode ser usado em MDX. A tag deve ficar no conteúdo do documento, e seus atributos podem ser literais:
## Aviso
<x-ui.notice tone="warning" class="mt-6">
Revise a configuração antes do build.
</x-ui.notice>
Componentes podem ser inline ou em bloco e podem ser aninhados. Use @props, $attributes, $slot e slots nomeados no componente; quando o conteúdo precisar de Markdown processado, coloque esse Markdown fora do componente ou crie uma composição Blade.
2. Criar e selecionar um layout
Adicione um arquivo em Content/Views/layouts/ e use seu nome por pontos. Um layout mínimo precisa emitir o head, carregar o CSS nativo e oferecer a seção content. Se o projeto tiver app.css, carregue-o explicitamente (os layouts oficiais fazem isso automaticamente):
<!doctype html>
<html lang="pt-BR">
<head>
@foreach ($head->render() as $tag)
{!! $tag !!}
@endforeach
<link rel="stylesheet" href="{{ $siteUrl->asset('/'.$cssAsset) }}">
@if ($assets->has('css/app.css'))
<link rel="stylesheet" href="{{ $siteUrl->asset($assets->path('css/app.css')) }}">
@endif
</head>
<body>
<x-layout.header />
<main>@yield('content')</main>
<x-layout.footer />
</body>
</html>
Uma página Blade declara o layout com @extends:
@extends('layouts.site')
@section('content')
<x-blocks.prose><h1>{{ $meta->title }}</h1></x-blocks.prose>
@endsection
Um documento MDX seleciona o layout por metadata:
extra:
layout: layouts.site
Layouts podem usar componentes normalmente. O renderer também expõe $meta, $extra, $currentPath e as chaves de extra diretamente; Collections adicionam seus resumos em $items.
3. Criar uma cover
O build renderiza uma cover social independente para cada rota habilitada e grava PNG de 1200×630. O template padrão é Content/Views/covers/cover.blade.php. Para criar uma variante, adicione, por exemplo, Content/Views/covers/manual.blade.php e selecione-a com o nome da view:
<!doctype html>
<html lang="pt-BR">
<head>
<meta charset="utf-8">
<style>
html, body { width: 1200px; height: 630px; }
body { margin: 0; padding: 72px; background: #4C1D95; color: white; }
</style>
</head>
<body>
<x-ui.badge>Manual</x-ui.badge>
<h1>{{ $head->rawTitle() }}</h1>
@if ($head->rawDescription())
<p>{{ $head->rawDescription() }}</p>
@endif
<small>{{ $domain ?? '' }}</small>
</body>
</html>
O HTML da cover é renderizado fora da página normal e convertido pelo Chromium Headless. Por isso, prefira documento completo, dimensões fixas e CSS embutido; não dependa de navegação, JavaScript de aplicação ou de um layouts.app que carregue recursos relativos da página. Durante o build, o BlogBlade sobe um servidor HTTP local temporário sobre dist/, então assets publicados podem ser carregados sem depender do domínio configurado. Como o documento temporário usa file://, esse servidor libera CORS para CSS, fontes e demais assets publicados.
No metadata da rota:
cover: covers.manual
O template recebe $head, $meta, $extra, $currentPath, os dados da rota, $assets, $domain, $fontStylesheet e $fontPreloads. Também recebe $cover, um contexto explícito com path, width() (1200), height() (630), url() e asset('/caminho'). Use $cover->asset('/'.$fontStylesheet) para carregar o stylesheet local de fontes quando ele existir e $cover->asset($fontPreload) em links de preload; $fontStylesheet é null e $fontPreloads é uma lista vazia quando não há fontes configuradas. Para outros assets publicados, use $cover->asset($assets->path('css/remixicon.css')). asset() retorna uma URL absoluta adequada para o renderizador da imagem; quando site.basePath existe, ele é preservado no caminho. A cover é publicada como /cover.png na home ou /<rota>/cover.png nas demais rotas.
Exemplo mínimo: crie Content/Views/covers/remix.blade.php, habilite o Remix
Icon no config.yaml e selecione essa view no metadata da página:
feature:
remixicon: true
cover: covers.remix
<!doctype html>
<html lang="pt-BR">
<head>
<meta charset="utf-8">
@if ($assets->has('css/remixicon.css'))
<link rel="stylesheet" href="{{ $cover->asset($assets->path('css/remixicon.css')) }}">
@endif
<style>
html, body { width: 1200px; height: 630px; }
.icon { color: #FFC700; font-size: 48px; }
</style>
</head>
<body>
<i class="ri-rocket-line icon" aria-hidden="true"></i>
</body>
</html>
O <link> usa o manifesto lógico css/remixicon.css. Não copie o nome hashado do CSS. $cover->asset() mantém o site.basePath e a resolução
das fontes relativas durante a rasterização. Depois, use qualquer classe
ri-* disponível no pacote, como ri-home-line, ri-database-2-line ou
ri-github-fill.
Para uma página MDX, o campo fica no front matter:
---
title: Página especial
cover: covers.manual
---
Para uma página Blade, use o sidecar correspondente (meta.yaml para index.blade.php ou <nome>.meta.yaml para <nome>.blade.php). cover: false desativa o PNG dessa rota. Sem social.image, os cards Open Graph e Twitter apontam para o PNG gerado; com cover: false, eles são omitidos, salvo quando social.image fornece uma imagem explícita.
Covers herdadas
Em MDX, _defaults.yaml pode definir cover para uma árvore e o front matter do documento pode substituí-lo. Em uma Collection, defaults.cover vale para os itens e o front matter do item tem precedência. A página-mãe configura sua própria cover: ela não herda automaticamente a cover dos itens, e os itens não herdam metadata da página-mãe.
Consulte Escrever conteúdo MDX para a sintaxe Markdown, Blade no Pocket Banana para as variáveis disponíveis, Metadata para o schema e Componentes para a superfície fornecida pelo Content atual.