Pular para o conteúdo
Navegar na documentação
Nesta página

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.