SEO & accessibility: OG tags, robots.txt, sitemap, OG image, aria labels, focus trap, keyboard nav
Some checks failed
Deploy to Production / Deploy to Server (push) Has been cancelled
Some checks failed
Deploy to Production / Deploy to Server (push) Has been cancelled
This commit is contained in:
parent
ed273430c7
commit
4833edf44c
6 changed files with 88 additions and 16 deletions
|
|
@ -477,3 +477,39 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||
el.addEventListener('click', function(e) { e.preventDefault(); openEmailChange(); });
|
||||
});
|
||||
});
|
||||
|
||||
// === Accessibility: Escape key closes modals, focus trapping ===
|
||||
(function() {
|
||||
function getActiveModal() {
|
||||
var modals = ['signupModal', 'recoverModal', 'emailChangeModal'];
|
||||
for (var i = 0; i < modals.length; i++) {
|
||||
var m = document.getElementById(modals[i]);
|
||||
if (m && m.classList.contains('active')) return m;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function closeActiveModal() {
|
||||
var m = getActiveModal();
|
||||
if (!m) return;
|
||||
m.classList.remove('active');
|
||||
}
|
||||
|
||||
document.addEventListener('keydown', function(e) {
|
||||
if (e.key === 'Escape') closeActiveModal();
|
||||
|
||||
// Focus trap inside active modal
|
||||
if (e.key === 'Tab') {
|
||||
var modal = getActiveModal();
|
||||
if (!modal) return;
|
||||
var focusable = modal.querySelectorAll('button:not([disabled]), input:not([disabled]), a[href], [tabindex]:not([tabindex="-1"])');
|
||||
if (focusable.length === 0) return;
|
||||
var first = focusable[0], last = focusable[focusable.length - 1];
|
||||
if (e.shiftKey) {
|
||||
if (document.activeElement === first) { e.preventDefault(); last.focus(); }
|
||||
} else {
|
||||
if (document.activeElement === last) { e.preventDefault(); first.focus(); }
|
||||
}
|
||||
}
|
||||
});
|
||||
})();
|
||||
|
|
|
|||
|
|
@ -5,6 +5,15 @@
|
|||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>DocFast — HTML & Markdown to PDF API</title>
|
||||
<meta name="description" content="Convert HTML and Markdown to beautiful PDFs with a simple API call. Built-in invoice templates. Fast, reliable, developer-friendly.">
|
||||
<meta property="og:title" content="DocFast — HTML & Markdown to PDF API">
|
||||
<meta property="og:description" content="Convert HTML and Markdown to beautiful PDFs with a simple API call. Fast, reliable, developer-friendly.">
|
||||
<meta property="og:type" content="website">
|
||||
<meta property="og:url" content="https://docfast.dev">
|
||||
<meta property="og:image" content="https://docfast.dev/og-image.png">
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
<meta name="twitter:title" content="DocFast — HTML & Markdown to PDF API">
|
||||
<meta name="twitter:description" content="Convert HTML and Markdown to beautiful PDFs with a simple API call.">
|
||||
<link rel="canonical" href="https://docfast.dev">
|
||||
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>⚡</text></svg>">
|
||||
<style>
|
||||
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
||||
|
|
@ -231,6 +240,9 @@ html, body {
|
|||
#emailChangeLoading.active { display: flex; flex-direction: column; align-items: center; padding: 40px 0; text-align: center; }
|
||||
#emailChangeResult.active { display: block; }
|
||||
#emailChangeVerify.active { display: block; }
|
||||
|
||||
/* Focus-visible for accessibility */
|
||||
.btn:focus-visible, a:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
|
||||
</style>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
|
|
@ -238,7 +250,7 @@ html, body {
|
|||
</head>
|
||||
<body>
|
||||
|
||||
<nav>
|
||||
<nav aria-label="Main navigation">
|
||||
<div class="container">
|
||||
<div class="logo">⚡ Doc<span>Fast</span></div>
|
||||
<div class="nav-links">
|
||||
|
|
@ -249,7 +261,7 @@ html, body {
|
|||
</div>
|
||||
</nav>
|
||||
|
||||
<section class="hero">
|
||||
<main class="hero" role="main">
|
||||
<div class="container">
|
||||
<div class="badge">🚀 Simple PDF API for Developers</div>
|
||||
<h1>HTML to <span class="gradient">PDF</span><br>in one API call</h1>
|
||||
|
|
@ -262,7 +274,7 @@ html, body {
|
|||
|
||||
<div class="code-section">
|
||||
<div class="code-header">
|
||||
<div class="code-dots"><span></span><span></span><span></span></div>
|
||||
<div class="code-dots" aria-hidden="true"><span></span><span></span><span></span></div>
|
||||
<span class="code-label">terminal</span>
|
||||
</div>
|
||||
<div class="code-block">
|
||||
|
|
@ -275,7 +287,7 @@ html, body {
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<section class="trust">
|
||||
<div class="container">
|
||||
|
|
@ -306,32 +318,32 @@ html, body {
|
|||
<p class="section-sub">A complete PDF generation API. No SDKs, no dependencies, no setup.</p>
|
||||
<div class="features-grid">
|
||||
<div class="feature-card">
|
||||
<div class="feature-icon">⚡</div>
|
||||
<div class="feature-icon" aria-hidden="true">⚡</div>
|
||||
<h3>Sub-second Speed</h3>
|
||||
<p>Persistent browser pool — no cold starts. Your PDFs are ready before your spinner shows.</p>
|
||||
</div>
|
||||
<div class="feature-card">
|
||||
<div class="feature-icon">🎨</div>
|
||||
<div class="feature-icon" aria-hidden="true">🎨</div>
|
||||
<h3>Pixel-perfect Output</h3>
|
||||
<p>Full CSS support including flexbox, grid, and custom fonts. Your brand, your PDFs.</p>
|
||||
</div>
|
||||
<div class="feature-card">
|
||||
<div class="feature-icon">📄</div>
|
||||
<div class="feature-icon" aria-hidden="true">📄</div>
|
||||
<h3>Built-in Templates</h3>
|
||||
<p>Invoice and receipt templates out of the box. Pass JSON data, get beautiful PDFs.</p>
|
||||
</div>
|
||||
<div class="feature-card">
|
||||
<div class="feature-icon">🔧</div>
|
||||
<div class="feature-icon" aria-hidden="true">🔧</div>
|
||||
<h3>Dead-simple API</h3>
|
||||
<p>REST API. JSON in, PDF out. Works with curl, Python, Node, Go — anything with HTTP.</p>
|
||||
</div>
|
||||
<div class="feature-card">
|
||||
<div class="feature-icon">📐</div>
|
||||
<div class="feature-icon" aria-hidden="true">📐</div>
|
||||
<h3>Fully Configurable</h3>
|
||||
<p>A4, Letter, custom sizes. Portrait or landscape. Headers, footers, and margins.</p>
|
||||
</div>
|
||||
<div class="feature-card">
|
||||
<div class="feature-icon">🔒</div>
|
||||
<div class="feature-icon" aria-hidden="true">🔒</div>
|
||||
<h3>Secure by Default</h3>
|
||||
<p>HTTPS only. Rate limiting. No data stored. PDFs stream directly — nothing touches disk.</p>
|
||||
</div>
|
||||
|
|
@ -372,7 +384,7 @@ html, body {
|
|||
</div>
|
||||
</section>
|
||||
|
||||
<footer>
|
||||
<footer aria-label="Footer">
|
||||
<div class="container">
|
||||
<div class="footer-left">© 2026 DocFast. Fast PDF generation for developers.</div>
|
||||
<div class="footer-links">
|
||||
|
|
@ -384,7 +396,7 @@ html, body {
|
|||
</footer>
|
||||
|
||||
<!-- Signup Modal -->
|
||||
<div class="modal-overlay" id="signupModal">
|
||||
<div class="modal-overlay" id="signupModal" role="dialog" aria-label="Sign up for API key">
|
||||
<div class="modal">
|
||||
<button class="close" id="btn-close-signup">×</button>
|
||||
|
||||
|
|
@ -411,7 +423,7 @@ html, body {
|
|||
<p style="margin-top:16px;color:var(--muted);font-size:0.8rem;text-align:center;">Code expires in 15 minutes</p>
|
||||
</div>
|
||||
|
||||
<div id="signupResult">
|
||||
<div id="signupResult" aria-live="polite">
|
||||
<h2>🚀 Your API key is ready!</h2>
|
||||
<div class="warning-box">
|
||||
<span class="icon">⚠️</span>
|
||||
|
|
@ -428,7 +440,7 @@ html, body {
|
|||
|
||||
|
||||
<!-- Recovery Modal -->
|
||||
<div class="modal-overlay" id="recoverModal">
|
||||
<div class="modal-overlay" id="recoverModal" role="dialog" aria-label="Recover API key">
|
||||
<div class="modal">
|
||||
<button class="close" id="btn-close-recover">×</button>
|
||||
|
||||
|
|
@ -455,7 +467,7 @@ html, body {
|
|||
<p style="margin-top:16px;color:var(--muted);font-size:0.8rem;text-align:center;">Code expires in 15 minutes</p>
|
||||
</div>
|
||||
|
||||
<div id="recoverResult">
|
||||
<div id="recoverResult" aria-live="polite">
|
||||
<h2>🔑 Your API key</h2>
|
||||
<div class="warning-box">
|
||||
<span class="icon">⚠️</span>
|
||||
|
|
@ -472,7 +484,7 @@ html, body {
|
|||
|
||||
|
||||
<!-- Email Change Modal -->
|
||||
<div class="modal-overlay" id="emailChangeModal">
|
||||
<div class="modal-overlay" id="emailChangeModal" role="dialog" aria-label="Change email">
|
||||
<div class="modal">
|
||||
<button class="close" id="btn-close-email-change">×</button>
|
||||
|
||||
|
|
|
|||
BIN
public/og-image.png
Normal file
BIN
public/og-image.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 39 KiB |
13
public/og-image.svg
Normal file
13
public/og-image.svg
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="630" viewBox="0 0 1200 630">
|
||||
<rect width="1200" height="630" fill="#0b0d11"/>
|
||||
<rect x="0" y="0" width="1200" height="630" fill="url(#glow)" opacity="0.3"/>
|
||||
<defs>
|
||||
<radialGradient id="glow" cx="50%" cy="50%" r="50%">
|
||||
<stop offset="0%" stop-color="#34d399" stop-opacity="0.2"/>
|
||||
<stop offset="100%" stop-color="#0b0d11" stop-opacity="0"/>
|
||||
</radialGradient>
|
||||
</defs>
|
||||
<text x="600" y="270" text-anchor="middle" font-family="Inter, -apple-system, sans-serif" font-size="80" font-weight="800" fill="#e4e7ed">⚡ Doc<tspan fill="#34d399">Fast</tspan></text>
|
||||
<text x="600" y="370" text-anchor="middle" font-family="Inter, -apple-system, sans-serif" font-size="36" font-weight="400" fill="#7a8194">HTML & Markdown to PDF API</text>
|
||||
<rect x="400" y="420" width="400" height="4" rx="2" fill="#34d399" opacity="0.5"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 910 B |
6
public/robots.txt
Normal file
6
public/robots.txt
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
User-agent: *
|
||||
Allow: /
|
||||
Disallow: /v1/
|
||||
Disallow: /api
|
||||
Disallow: /health
|
||||
Sitemap: https://docfast.dev/sitemap.xml
|
||||
5
public/sitemap.xml
Normal file
5
public/sitemap.xml
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<urlset xmlns="http://www.sitemapns.org/schemas/sitemap/0.9">
|
||||
<url><loc>https://docfast.dev/</loc><changefreq>weekly</changefreq><priority>1.0</priority></url>
|
||||
<url><loc>https://docfast.dev/docs</loc><changefreq>weekly</changefreq><priority>0.8</priority></url>
|
||||
</urlset>
|
||||
Loading…
Add table
Add a link
Reference in a new issue