Criar e personalizar apresentações
Uma página MDX com type: presentation tem duas visualizações da mesma fonte:
Content/Pages/palestra.mdx
│
├── /palestra/
│ artigo vertical normal
│
└── /palestra/slides.html
apresentação Reveal.js
O artigo é a leitura principal: ele pode ser lido de cima para baixo, indexado
e compartilhado como uma página comum. A apresentação é uma segunda saída,
opt-in, feita para palco, reunião ou aula. Reveal.js não transforma o artigo em
um deck; ele só é carregado na variante slides.html.
Começar pelo exemplo mínimo
Crie um arquivo como Content/Pages/palestra.mdx:
---
title: Como publicar melhor
description: Uma introdução prática ao fluxo de publicação.
type: presentation
sitemap:
priority: 0.6
changefreq: monthly
showInNav: false
---
# Como publicar melhor
Uma ideia principal para abrir a conversa.
<!-- slide -->
## O problema
Explique o contexto em poucas linhas.
<x-ui.alert type="info" title="Ideia-chave">
O artigo e a apresentação usam a mesma fonte MDX.
</x-ui.alert>
<!-- slide -->
## A solução
```php
echo 'Markdown, código e componentes continuam disponíveis';
```
<!-- slide -->
## Encerramento
Use teclado, controles ou touch para navegar pelos slides.
Depois de renderizar, as URLs serão:
/palestra/ artigo vertical
/palestra/slides.html apresentação
/palestra/slides.html#/slide-2 segundo slide
O fragmento #/slide-2 é interpretado pelo navegador. O servidor recebe
somente /palestra/slides.html; depois que o Reveal.js inicializa, ele encontra
o elemento com id="slide-2".
Front matter e opt-in
O mínimo necessário é:
---
title: Minha apresentação
type: presentation
---
Na prática, uma página indexável também precisa declarar sitemap.priority e
sitemap.changefreq, como qualquer outra página indexável do projeto. O tipo
presentation não muda as regras de metadata do artigo.
Campos relevantes:
| Campo | Obrigatório | Efeito |
|---|---|---|
title |
sim | título do artigo e da apresentação |
description |
não | descrição do artigo e metadata social |
type: presentation |
sim | habilita a segunda renderização |
sitemap.priority |
para rota indexável | prioridade do artigo no sitemap |
sitemap.changefreq |
para rota indexável | frequência do artigo no sitemap |
showInNav |
não | controla a navegação do artigo |
extra.layout |
não | altera o layout do artigo, não o layout do deck |
O recurso global precisa estar ligado em config.yaml:
feature:
revealjs: true
O padrão é false. Se existir uma página com type: presentation enquanto o
recurso estiver desabilitado, o build falha de propósito e informa para habilitar
Reveal.js. Isso evita publicar um artigo com um link para uma apresentação sem
runtime ou sem CSS.
Consulte Configuração para a chave completa.
Dividir o conteúdo em slides
O separador oficial é uma linha contendo somente:
<!-- slide -->
Ele aceita espaços ao redor e variações de maiúsculas/minúsculas, mas deve continuar isolado em sua própria linha. Tudo antes do primeiro separador é o primeiro slide; tudo entre separadores vira um slide; tudo depois do último separador vira o último slide.
Este conteúdo gera três slides:
# Slide 1
<!-- slide -->
# Slide 2
<!-- slide -->
# Slide 3
Sem separadores, o corpo inteiro vira um único slide. Um separador não cria um slide vazio válido. Portanto, isto falha:
# Primeiro
<!-- slide -->
<!-- slide -->
# Terceiro
O parser também não divide um separador que esteja dentro de um fenced code block. Para ensinar a sintaxe do separador sem executá-la, use um fence externo maior:
```md
<!-- slide -->
```
Fences com crases e com til são reconhecidos. O fence precisa estar corretamente fechado; caso contrário, o marcador continua sendo tratado como código.
O que pode existir dentro de um slide
Cada slide continua sendo Markdown MDX normal. Você pode usar:
- headings, parágrafos, listas e tabelas;
- links e âncoras;
- task lists e tachado;
- fenced code blocks;
- HTML permitido pelo parser;
- componentes Blade anônimos
<x-...>; - componentes aninhados;
- classes CSS do projeto.
O engine renderiza cada parte para HTML e então gera uma seção Blade equivalente a esta estrutura:
<section id="slide-1">
<x-blocks.prose>
<!-- HTML do slide -->
</x-blocks.prose>
</section>
O wrapper x-blocks.prose é adicionado pelo engine. Normalmente não é preciso
adicioná-lo manualmente no MDX.
Componentes Blade em apresentações
Componentes são a forma recomendada de reutilizar blocos visuais:
## A ideia principal
<x-ui.alert type="success" title="Resultado">
A mensagem que o público precisa lembrar.
</x-ui.alert>
<x-ui.card title="Exemplo">
<x-ui.badge featured="true">Importante</x-ui.badge>
Um bloco reutilizável dentro do slide.
</x-ui.card>
Em MDX:
- atributos literais são encaminhados ao componente;
$sloté o slot default disponível;$attributessó funciona se o componente os imprimir;- componentes podem ser aninhados;
- Markdown dentro do slot não passa por uma segunda renderização Markdown;
{{ ... }},{!! ... !!}e diretivas Blade permanecem texto literal.
Por isso, prefira:
<x-ui.alert type="info">
<strong>Texto HTML</strong> dentro do componente.
</x-ui.alert>
em vez de presumir que isto será convertido novamente:
<x-ui.alert type="info">
**Isto pode permanecer com os asteriscos.**
</x-ui.alert>
Para criar um bloco próprio, adicione uma view em
Content/Views/Components/ e use o nome correspondente <x-...> no MDX.
Componentes de apresentação continuam sendo componentes Blade comuns; o engine
não cria uma API de componente exclusiva para slides.
Personalizar o visual
A personalização atual acontece em três camadas:
| Camada | Local | Responsabilidade |
|---|---|---|
| Conteúdo | arquivo .mdx |
texto, estrutura, componentes e separação |
| Componentes | Content/Views/Components/ |
blocos reutilizáveis |
| Aparência global | Content/assets/css/app.css |
cores, tipografia, dimensões e espaçamento |
| Shell da apresentação | Content/Views/layouts/presentation.blade.php |
HTML, assets e inicialização do Reveal.js |
O layout atual usa estes seletores como pontos de extensão:
.presentation-page /* body e fundo geral */
.presentation-main /* área principal */
.presentation-page .reveal /* deck */
.reveal .slides section /* alinhamento do slide */
.reveal .slides section .prose /* largura do conteúdo */
.reveal .controls /* controles */
.reveal .progress /* progresso */
Exemplo de tema visual local:
.presentation-page.theme-ocean {
background: #071827;
color: #e0f2fe;
}
.presentation-page.theme-ocean .reveal .slides section .prose {
max-width: 68rem;
}
.presentation-page.theme-ocean .reveal .controls,
.presentation-page.theme-ocean .reveal .progress {
color: #38bdf8;
}
Para aplicar uma classe como theme-ocean ao <body>, altere o layout
layouts.presentation. Atualmente o front matter não injeta classes ou temas
por página automaticamente.
O layout do artigo pode ser escolhido por extra.layout, mas o layout da
apresentação é gerado pelo engine com layouts.presentation. Para trocar o
shell global, edite ou substitua essa view no projeto consumidor. Depois de
alterar o layout, mantenha o contrato dos nomes lógicos dos assets:
{{ $assets->path('js/reveal.js') }}
{{ $assets->path('css/reveal.css') }}
Nunca fixe no layout nomes como reveal.4bbf460b02.mjs: o hash muda com a
versão do pacote.
Personalizar o runtime
O layout atual inicializa o deck com estas opções globais:
{
embedded: false,
width: '100%',
height: '100%',
controls: true,
progress: true,
slideNumber: 'c/t',
hash: true,
keyboard: true,
touch: true,
center: true,
transition: 'slide',
}
Isso fornece controles, teclado, touch, progresso, numeração e links por hash.
Para alterar transições, plugins ou configurações avançadas, modifique o
script inline de layouts.presentation. Essa configuração é global: hoje não
há uma chave de front matter para mudar transition, controls, theme ou
slideNumber em uma página individual.
Ao adicionar um plugin ou comportamento JavaScript:
- publique ou importe o asset pelo pipeline do projeto;
- mantenha os URLs compatíveis com
site.basePath; - inicialize o plugin depois de criar o
Reveal; - preserve o hash e a navegação por teclado, salvo se houver uma decisão de UX explícita;
- crie um teste browser que verifique o comportamento real.
Artigo, apresentação e acessibilidade
O artigo não usa Reveal.js e deve continuar legível sem JavaScript. Use-o para explicações longas, contexto, referências e leitura assíncrona. Use os slides para frases curtas, exemplos e momentos de apresentação.
Na apresentação:
- mantenha um heading principal por slide quando fizer sentido;
- use contraste suficiente entre texto e fundo;
- não dependa somente de cor para transmitir estado;
- mantenha links e botões com texto visível;
- use
aria-hidden="true"em ícones que são apenas decoração; - teste teclado e touch;
- respeite
prefers-reduced-motionao adicionar transições próprias; - não esconda informação essencial somente nos controles do Reveal.js.
Os IDs atuais são posicionais: slide-1, slide-2, slide-3. Reordenar ou
inserir um slide pode mudar o destino de um link #/slide-N. Use os hashes como
atalhos temporários de navegação, não como identificadores permanentes de
conteúdo.
Assets, desenvolvimento e publicação
Habilite o recurso em config.yaml e deixe o engine publicar os assets locais.
No desenvolvimento:
public/assets/reveal/reveal.mjs
public/assets/reveal/reveal.css
No build:
dist/assets/reveal/reveal.<hash>.mjs
dist/assets/reveal/reveal.<hash>.css
Use os nomes lógicos do manifest nos layouts. site.basePath é aplicado por
$siteUrl->asset(...), portanto um projeto publicado em /BlogBlade deve
continuar gerando URLs como /BlogBlade/assets/reveal/....
Fluxos suportados:
just dev servidor dinâmico e assets estáveis
just build-fast build estático sem covers sociais
just build build estático completo
just serve serve o dist já gerado
No build estático, a apresentação é gravada como
dist/<rota>/slides.html. No servidor dinâmico, RouteCatalog resolve a
variante sem adicioná-la às rotas indexáveis.
SEO e saída gerada
A URL do artigo é a canônica. A URL da apresentação recebe:
<meta name="robots" content="noindex, follow">
<link rel="canonical" href="/palestra/">
Isso evita duplicar o conteúdo no índice de busca sem impedir que links sejam
seguidos. A variante de slides não aparece na navegação principal, sitemap,
feeds ou llms.txt como uma rota adicional.
Consulte Saída gerada para a estrutura
completa de dist/ e Assets para o manifest.
Troubleshooting
| Sintoma | Causa provável | Ação |
|---|---|---|
/palestra/ funciona, mas slides.html retorna 404 |
servidor ou build antigo | reinicie just dev ou execute just build-fast |
| tela sem estilo ou sem controles | Reveal.js não publicado | confirme feature.revealjs: true |
| build acusa slide vazio | separadores consecutivos ou conteúdo só com espaços | remova o separador extra ou adicione conteúdo |
| marcador dentro de código divide o slide | fenced block não foi fechado | feche o fence com a mesma sintaxe |
| classe CSS não aparece | Tailwind não descobriu a classe no MDX | adicione @source na entrada CSS |
| asset aponta para a raiz errada | site.basePath foi ignorado |
use $siteUrl e $assets |
| Markdown dentro de componente aparece literal | slots não sofrem segunda passagem Markdown | use HTML ou um componente dedicado |
| hash abre o slide errado | slides foram reordenados | atualize o link #/slide-N |
| sitemap acusa metadata ausente | artigo indexável sem bloco sitemap |
defina priority e changefreq |
Contrato técnico para implementar em outro projeto
Uma implementação compatível pode seguir este pipeline:
- descobrir o arquivo
.mdxe ler o front matter; - validar
type: presentation; - exigir a flag global do runtime Reveal.js;
- dividir o corpo somente em separadores fora de fenced code blocks;
- rejeitar partes vazias;
- renderizar o corpo sem separadores como artigo;
- renderizar cada parte como HTML de um slide;
- gerar a view do artigo;
- gerar a view da apresentação com
<section id="slide-N">; - gerar a rota derivada
/rota/slidespara uso interno; - publicar a URL
/rota/slides.html; - gerar
noindexe canonical para o artigo; - publicar Reveal.js localmente e resolver os nomes pelo manifest;
- manter a variante fora do catálogo indexável;
- testar servidor dinâmico, build estático,
basePathe hash navigation.
Os contratos essenciais são:
entrada: type: presentation + <!-- slide -->
artigo: /rota/
apresentação: /rota/slides.html
rota interna: /rota/slides
IDs: slide-1, slide-2, ...
assets lógicos: js/reveal.js, css/reveal.css
SEO: noindex,follow + canonical do artigo
Não misture a rota derivada com as rotas indexáveis. O build pode gravar o
arquivo slides.html, enquanto o servidor dinâmico pode resolver a mesma
variante a partir do artigo original.
Proposta futura: configuração por apresentação
Esta seção é uma proposta, não uma API disponível. A implementação atual não aceita o bloco abaixo no front matter:
presentation:
theme: default
transition: slide
controls: true
progress: true
slideNumber: c/t
hash: true
keyboard: true
touch: true
center: true
Para implementar essa evolução com segurança, o engine precisaria:
- adicionar e validar o bloco
presentationno metadata; - hidratar os valores com defaults compatíveis com o layout atual;
- transportar a configuração até a
Routee a view; - serializar os valores sem permitir JavaScript arbitrário;
- aplicar precedência entre defaults globais e configuração local;
- preservar
basePath, hash navigation e acessibilidade; - cobrir cada opção com teste de build e browser.
Até essa API existir, personalize o comportamento global editando
layouts.presentation e personalize páginas individuais com componentes e CSS.