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

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;
  • $attributes só 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:

  1. publique ou importe o asset pelo pipeline do projeto;
  2. mantenha os URLs compatíveis com site.basePath;
  3. inicialize o plugin depois de criar o Reveal;
  4. preserve o hash e a navegação por teclado, salvo se houver uma decisão de UX explícita;
  5. 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-motion ao 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:

  1. descobrir o arquivo .mdx e ler o front matter;
  2. validar type: presentation;
  3. exigir a flag global do runtime Reveal.js;
  4. dividir o corpo somente em separadores fora de fenced code blocks;
  5. rejeitar partes vazias;
  6. renderizar o corpo sem separadores como artigo;
  7. renderizar cada parte como HTML de um slide;
  8. gerar a view do artigo;
  9. gerar a view da apresentação com <section id="slide-N">;
  10. gerar a rota derivada /rota/slides para uso interno;
  11. publicar a URL /rota/slides.html;
  12. gerar noindex e canonical para o artigo;
  13. publicar Reveal.js localmente e resolver os nomes pelo manifest;
  14. manter a variante fora do catálogo indexável;
  15. testar servidor dinâmico, build estático, basePath e 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:

  1. adicionar e validar o bloco presentation no metadata;
  2. hidratar os valores com defaults compatíveis com o layout atual;
  3. transportar a configuração até a Route e a view;
  4. serializar os valores sem permitir JavaScript arbitrário;
  5. aplicar precedência entre defaults globais e configuração local;
  6. preservar basePath, hash navigation e acessibilidade;
  7. 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.