All templates
Contact form with dark mode
The same form, themed with CSS custom properties and a prefers-color-scheme block. No toggle, no localStorage, no flash of the wrong theme on load. The OS already knows what the person wants and the browser applies it before first paint.
HTML · CSS
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Contact us</title>
<style>
:root {
--page: #f4f4f5;
--surface: #ffffff;
--text: #18181b;
--muted: #71717a;
--line: #e4e4e7;
--input-line: #d4d4d8;
--accent: #18181b;
--accent-text: #ffffff;
}
@media (prefers-color-scheme: dark) {
:root {
--page: #09090b;
--surface: #18181b;
--text: #fafafa;
--muted: #a1a1aa;
--line: #27272a;
--input-line: #3f3f46;
--accent: #fafafa;
--accent-text: #18181b;
}
}
*, *::before, *::after { box-sizing: border-box; }
body {
margin: 0;
padding: 32px 16px;
font-family: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
background: var(--page);
color: var(--text);
}
.card {
max-width: 460px;
margin: 0 auto;
padding: 28px;
background: var(--surface);
border: 1px solid var(--line);
border-radius: 12px;
}
h1 { margin: 0 0 6px; font-size: 20px; }
.sub { margin: 0 0 22px; font-size: 14px; color: var(--muted); }
.field { margin-bottom: 16px; }
label { display: block; margin-bottom: 6px; font-size: 13px; font-weight: 500; }
input, textarea {
width: 100%;
padding: 10px 12px;
font: inherit;
font-size: 14px;
color: var(--text);
background: var(--surface);
border: 1px solid var(--input-line);
border-radius: 8px;
}
input:focus, textarea:focus {
outline: 2px solid var(--accent);
outline-offset: 1px;
border-color: transparent;
}
textarea { min-height: 110px; resize: vertical; }
button {
width: 100%;
padding: 11px 16px;
font: inherit;
font-size: 14px;
font-weight: 500;
color: var(--accent-text);
background: var(--accent);
border: 0;
border-radius: 8px;
cursor: pointer;
}
button:active { transform: scale(0.99); }
.hp { display: none; }
</style>
</head>
<body>
<form class="card" action="https://app.portform.co/api/submit" method="POST">
<h1>Get in touch</h1>
<p class="sub">This form follows your system theme.</p>
<input type="hidden" name="access_key" value="YOUR_ACCESS_KEY" />
<input type="checkbox" name="botcheck" class="hp" tabindex="-1" autocomplete="off" />
<div class="field">
<label for="name">Name</label>
<input id="name" type="text" name="name" autocomplete="name" required />
</div>
<div class="field">
<label for="email">Email</label>
<input id="email" type="email" name="email" autocomplete="email" required />
</div>
<div class="field">
<label for="message">Message</label>
<textarea id="message" name="message" required></textarea>
</div>
<button type="submit">Send message</button>
</form>
</body>
</html>Replace YOUR_ACCESS_KEY with the access key from your dashboard. Create one free. The endpoint and the honeypot are already in place.
Useful for
The part behind every one of these is a form backend. This template posts to a PortForm form endpoint, which stores each submission and sends it to your email; the docs cover the fields and responses.
Other templates
- Basic HTML contact formHTML · CSS
- Ajax contact formHTML · CSS · JavaScript
- JavaScript form validationHTML · CSS · JavaScript
- Multi-column contact formHTML · CSS
- Masked phone inputHTML · CSS · JavaScript
- Raw contact form (no CSS)HTML
- Multi-step NPS surveyHTML · CSS · JavaScript
- Multi-step survey formVue · HTML · CSS
- Contact form popupTailwindCSS · Alpine.js · HTML
- Popover feedback widgetTailwindCSS · Alpine.js · HTML
- File upload formHTML · CSS
- JavaScript file upload formHTML · CSS · JavaScript
- React contact formReact · HTML · CSS