Componente reutilizável para geração de PDF client-side via html2pdf.js.
Inclui: botão flutuante fixo, variante inline, estado de loading animado, marca d'água de rodapé, e CSS de impressão otimizado para o design system do projeto.
Para usar em qualquer página: copie o bloco <!-- BCM PDF BUTTON --> e cole antes do </body> de qualquer página.
O botão flutuante fica fixo no canto inferior direito. Ao clicar, entra em estado de loading com animação de progresso, gera o PDF com html2pdf.js e faz o download automaticamente.
Adicione esta linha no <head> da página, após as fontes Google:
<!-- html2pdf.js — geração de PDF client-side -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/html2pdf.js/0.10.1/html2pdf.bundle.min.js"
integrity="sha512-GsLlZN/3F2ErC5ifS5QtgpiJtWd43JWSuIgh7mbzc1x2gSR/QolHkoxzdjo7mwoLMmJDsXC1UYn3ev/xPBBKA=="
crossorigin="anonymous"></script>
</body>Substitua {{TITULO_DA_PAGINA}} pelo título real de cada página (ex.: "Linha do Tempo", "Ataques ao BCM").
<!-- ════════════════════════════════════════
BCM PDF BUTTON — Componente v1.0
Colar antes de </body> em qualquer página
Substituir {{TITULO_DA_PAGINA}} pelo título real
════════════════════════════════════════ -->
<!-- CSS do botão -->
<style>
.bcm-pdf-btn {
position: fixed;
bottom: 32px;
right: 32px;
z-index: 8000;
display: flex;
align-items: center;
gap: 10px;
background: var(--s900);
border: 1px solid var(--s600);
border-radius: 40px;
padding: 10px 20px 10px 14px;
cursor: pointer;
font-family: var(--font-body);
box-shadow: 0 4px 24px rgba(30,18,10,.28), 0 1px 4px rgba(30,18,10,.18);
transition: all 0.2s cubic-bezier(0.25,0.46,0.45,0.94);
/* Separador decorativo vertical */
outline: none;
text-decoration: none;
-webkit-font-smoothing: antialiased;
}
.bcm-pdf-btn:hover {
background: var(--s800);
border-color: var(--s400);
box-shadow: 0 8px 32px rgba(30,18,10,.36), 0 2px 8px rgba(30,18,10,.22);
transform: translateY(-2px);
}
.bcm-pdf-btn:active {
transform: translateY(0);
box-shadow: 0 2px 8px rgba(30,18,10,.2);
}
.bcm-pdf-btn-icon {
width: 36px;
height: 36px;
background: var(--s600);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
color: var(--s100);
transition: background 0.2s;
}
.bcm-pdf-btn:hover .bcm-pdf-btn-icon {
background: var(--s500);
}
.bcm-pdf-btn-label {
font-size: 13px;
font-weight: 500;
color: var(--white);
letter-spacing: 0.04em;
line-height: 1;
display: block;
}
.bcm-pdf-btn-sub {
font-size: 10px;
color: var(--s400);
letter-spacing: 0.08em;
text-transform: uppercase;
display: block;
margin-top: 2px;
}
/* ── Estado loading ── */
.bcm-pdf-btn.bcm-pdf-loading {
background: var(--s800);
border-color: var(--s700);
cursor: not-allowed;
pointer-events: none;
}
.bcm-pdf-btn.bcm-pdf-loading .bcm-pdf-btn-icon {
background: var(--s700);
}
.bcm-pdf-btn.bcm-pdf-loading .bcm-pdf-btn-label {
color: var(--s300);
}
/* ── Estado sucesso ── */
.bcm-pdf-btn.bcm-pdf-success {
background: #1A3A1A;
border-color: #2A5A2A;
pointer-events: none;
}
.bcm-pdf-btn.bcm-pdf-success .bcm-pdf-btn-icon {
background: #2A5A2A;
color: #A8D8A8;
}
/* ── Spinner ── */
@keyframes bcm-spin {
to { transform: rotate(360deg); }
}
.bcm-spin {
animation: bcm-spin 1s linear infinite;
transform-origin: center;
}
/* ── Tooltip de teclado ── */
.bcm-pdf-btn::after {
content: 'P';
position: absolute;
top: -8px;
right: 12px;
background: var(--s600);
color: var(--s100);
font-size: 9px;
font-weight: 700;
letter-spacing: 0.1em;
padding: 1px 5px;
border-radius: 2px;
opacity: 0;
transition: opacity 0.2s;
pointer-events: none;
}
.bcm-pdf-btn:focus::after,
.bcm-pdf-btn:hover::after { opacity: 1; }
/* ── Responsivo: compacto em mobile ── */
@media (max-width: 600px) {
.bcm-pdf-btn {
bottom: 20px;
right: 16px;
padding: 10px 14px;
border-radius: 50%;
width: 52px;
height: 52px;
justify-content: center;
}
.bcm-pdf-btn-label,
.bcm-pdf-btn-sub { display: none; }
.bcm-pdf-btn-icon { margin: 0; }
}
/* ── CSS de IMPRESSÃO — oculta UI, formata para A4 ── */
@media print {
.bcm-pdf-btn,
.topbar,
.main-nav,
.trilha-bar,
.stats-bar,
.site-footer,
.bcm-search-overlay { display: none !important; }
body { background: #fff !important; color: #000 !important; }
.page-header {
width: 100% !important;
left: 0 !important;
margin-left: 0 !important;
break-after: avoid;
}
.evento-card,
.ataque-body,
.bcm-tese,
.bcm-panel { break-inside: avoid; }
a[href]::after { content: none !important; }
}
</style>
<!-- HTML do botão -->
<button
class="bcm-pdf-btn"
id="bcmPdfBtn"
onclick="bcmPDF.gerar()"
aria-label="Gerar PDF desta página"
title="Gerar PDF (tecla P)"
type="button">
<span class="bcm-pdf-btn-icon" aria-hidden="true">
<svg width="18" height="18" fill="none" stroke="currentColor" stroke-width="1.6" viewBox="0 0 24 24">
<path d="M12 3v13M5 14l7 7 7-7"/>
<rect x="3" y="19" width="18" height="2" rx="1" fill="currentColor" stroke="none"/>
</svg>
</span>
<span class="bcm-pdf-btn-label">Gerar PDF</span>
<span class="bcm-pdf-btn-sub" id="bcmPdfSub">desta página</span>
</button>
<!-- Script de geração -->
<script>
(function(){
'use strict';
// ▶ CONFIGURAÇÃO — alterar por página
const CONFIG = {
titulo: '{{TITULO_DA_PAGINA}}', // ex.: 'Linha do Tempo'
subtitulo: 'verdadeirahistoriadabarra.com.br',
supervisor:'Supervisão: Mattos & Mattos Advogados · OAB/RJ 188.310 e 144.717',
seletor: 'body', // elemento a capturar (pode ser 'main', '.page-wrap', etc.)
arquivo: '{{TITULO_DA_PAGINA}}', // nome do arquivo (sem .pdf)
};
const btn = document.getElementById('bcmPdfBtn');
const subLabel = document.getElementById('bcmPdfSub');
const iconWrap = btn ? btn.querySelector('.bcm-pdf-btn-icon') : null;
const labelEl = btn ? btn.querySelector('.bcm-pdf-btn-label') : null;
// Ícones SVG reutilizáveis
const ICON_DOWNLOAD = ``;
const ICON_SPIN = ``;
const ICON_CHECK = ``;
function setEstado(estado) {
if (!btn) return;
btn.classList.remove('bcm-pdf-loading', 'bcm-pdf-success');
if (estado === 'loading') {
btn.classList.add('bcm-pdf-loading');
btn.disabled = true;
if (iconWrap) iconWrap.innerHTML = ICON_SPIN;
if (labelEl) labelEl.textContent = 'Gerando…';
if (subLabel) subLabel.textContent = 'Preparando';
} else if (estado === 'success') {
btn.classList.add('bcm-pdf-success');
if (iconWrap) iconWrap.innerHTML = ICON_CHECK;
if (labelEl) labelEl.textContent = 'PDF gerado!';
if (subLabel) subLabel.textContent = 'Download iniciado';
} else {
btn.disabled = false;
if (iconWrap) iconWrap.innerHTML = ICON_DOWNLOAD;
if (labelEl) labelEl.textContent = 'Gerar PDF';
if (subLabel) subLabel.textContent = 'desta página';
}
}
function progresso(msg) {
if (subLabel) subLabel.textContent = msg;
}
async function gerar() {
// Verificar se html2pdf está carregado
if (typeof html2pdf === 'undefined') {
alert('A biblioteca html2pdf.js não foi carregada. Verifique a tag
é orgulhosamente mantido com WordPress