From Design I/O & QA
Exports HTML to vector PDF via Playwright Chromium for slides and landing pages. Maintains selectable text, reasonable file size, and correct slide breaks.
How this skill is triggered — by the user, by Claude, or both
Slash command
/design-io:export-pdfWhen to use
Юзер просит «сделай PDF», «отправлю в Telegram», «один файл для презентации». После slides или standalone-html.
The summary Claude sees in its skill listing — used to decide when to auto-load this skill
Playwright headless Chromium → векторный PDF. Текст копируется, размер ~200KB-2MB.
Playwright headless Chromium → векторный PDF. Текст копируется, размер ~200KB-2MB.
scripts/export-pdf.js:
const { chromium } = require('playwright');
const path = require('path');
async function exportPdf(htmlPath, opts = {}) {
const browser = await chromium.launch();
const page = await browser.newPage();
await page.goto(`file://${path.resolve(htmlPath)}`, { waitUntil: 'networkidle' });
await page.waitForTimeout(opts.wait || 1000);
const out = opts.out || htmlPath.replace(/\.html$/, '.pdf');
await page.pdf({
path: out,
format: opts.format || 'A4',
landscape: opts.landscape ?? false,
printBackground: true,
margin: opts.margin || { top: '0', right: '0', bottom: '0', left: '0' },
preferCSSPageSize: true,
scale: opts.scale || 1,
});
await browser.close();
return out;
}
if (require.main === module) {
const args = process.argv.slice(2);
const html = args[0];
const isSlides = args.includes('--slides');
exportPdf(html, isSlides ? { format: { width: '1920px', height: '1080px' }, landscape: true } : {})
.then(out => console.log(`✓ ${out}`));
}
module.exports = { exportPdf };
node scripts/export-pdf.js artifact.html
node scripts/export-pdf.js deck.html --slides
Если артефакт — slides:
page-break-after: always в CSS.slide {
width: 1920px;
height: 1080px;
page-break-after: always;
break-after: always; /* modern */
break-inside: avoid;
}
@media print {
body { background: #fff; }
.deck { display: block !important; }
.slide {
position: static;
opacity: 1 !important;
transform: none !important;
pointer-events: auto;
}
}
В JS (slides каркас) для PDF-режима:
// Активировать все слайды для PDF
if (new URL(location).searchParams.get('print') === '1') {
document.querySelectorAll('.slide').forEach(s => s.classList.add('active'));
}
И передавать ?print=1 в URL при экспорте.
Континуальный документ — без page-break. Просто весь HTML на A4 portrait.
await page.pdf({
path: 'landing.pdf',
format: 'A4',
printBackground: true,
margin: { top: '20mm', bottom: '20mm', left: '15mm', right: '15mm' },
});
В HTML:
@page {
size: 1920px 1080px;
margin: 0;
}
@page :first {
margin-top: 0;
}
И preferCSSPageSize: true в Playwright options.
Web fonts через CDN иногда не embed'ятся. Решение:
await page.evaluateHandle('document.fonts.ready'); // ждать загрузки
await page.waitForTimeout(500); // запас
Или self-host шрифты в fonts/ — гарантированный embed.
Что увеличивает:
Что уменьшает:
printBackground: false (но теряешь градиенты)scale: 0.8 (но теряешь чёткость)Если артефакт = N экранов, и хочешь разные ориентации:
const { PDFDocument } = require('pdf-lib');
const slides = await exportPdf('slides.html', { landscape: true });
const docs = await exportPdf('docs.html');
const merged = await PDFDocument.create();
for (const file of [slides, docs]) {
const src = await PDFDocument.load(fs.readFileSync(file));
const pages = await merged.copyPages(src, src.getPageIndices());
pages.forEach(p => merged.addPage(p));
}
fs.writeFileSync('combined.pdf', await merged.save());
print-styles — добавляет @media print стили для красивой печатиpptx-editable-extractor — альтернатива если нужен PPTX, не PDFverifier — проверяй артефакт перед exportslides / standalone-html — обычно exportим этиdocument.fonts.ready → fallback-шрифт в PDFprintBackground: false на artistic дизайне → потеря всех градиентов и фоновpage.pdf() без waitUntil: 'networkidle' → asset'ы не догруженыnpx claudepluginhub jhamidun/claude-code-config-pack --plugin design-ioGuides completion of development work by verifying tests, detecting environment, and presenting structured options for merge, PR, or cleanup.
Enforces test-driven development: write failing test first, then minimal code to pass. Use when implementing features or bugfixes.
Guides creation and editing of skills using test-driven development with pressure scenarios and subagents to verify agent compliance.