How to Build and Publish a One-Page Portfolio in HTML
A portfolio has one job: convince someone to contact you. It does not need a CMS, a framework, a build step or a monthly subscription. One HTML file, written well, outperforms most template sites — it loads instantly, says exactly what you want it to say, and you can change it in thirty seconds. Here's how to structure it and get it online.
Decide what the page is for before you write markup
Nearly every weak portfolio fails here rather than in the CSS. Answer three questions first:
- Who is reading? A hiring manager scanning forty tabs, or a prospective client comparing three freelancers. Both are skimming.
- What should they do? Email you, book a call, download a CV. One primary action, stated more than once.
- What proves you can do the work? Three strong pieces beat nine mixed ones. Cut anything you'd feel the need to caveat.
A useful test: if someone read only your headline and one project, would they know whether to contact you? If not, the headline is too vague — "Product designer" is a category, "I design onboarding flows for fintech apps" is a reason to keep reading.
The structure that works
In order down the page:
- Header — your name, what you do, where you are. One line each.
- Selected work — three to six pieces. For each: what it was, what you did, what happened as a result.
- About — short. Two or three sentences of context, not a biography.
- Contact — an email address you can copy, and links that open in a new tab.
Skip the navigation bar. On a page this short, a nav is a link that scrolls somewhere the reader would have reached in two seconds anyway.
Writing a project entry
The pattern that reads well is context, action, outcome:
<article class="project">
<h3>Redesigning checkout for Northwind</h3>
<p class="meta">Lead designer · 2025 · 6 weeks</p>
<p>
Cart abandonment sat at 74%. I rebuilt the three-step checkout as a
single page with inline validation and ran it past eight users.
Abandonment fell to 51% over the following quarter.
</p>
<p><a href="https://example.com/case-study">Read the case study →</a></p>
</article>
Numbers make the difference. "Improved the checkout" is a claim; "74% to 51%" is evidence. If you don't have numbers, describe the constraint you worked under instead — that's also evidence of judgement.
A complete starting file
Self-contained, responsive, and readable. Change the text and it's yours.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Maria Chen — Product Designer</title>
<meta name="description" content="Product designer working on fintech
onboarding. Selected work and contact details.">
<style>
*, *::before, *::after { box-sizing: border-box; }
:root { --ink: #14161c; --dim: #5b6270; --line: #e6e8ee; --accent: #0b5cff; }
body {
margin: 0; background: #fff; color: var(--ink);
font: 17px/1.65 system-ui, -apple-system, "Segoe UI", sans-serif;
overflow-wrap: break-word;
}
.wrap { max-width: 660px; margin: 0 auto; padding: 5rem 1.25rem; }
h1 { font-size: clamp(2rem, 6vw, 2.8rem); line-height: 1.1; margin: 0 0 .5rem; }
.tagline { font-size: 1.15rem; color: var(--dim); margin: 0 0 2.5rem; }
h2 {
font-size: .8rem; text-transform: uppercase; letter-spacing: .1em;
color: var(--dim); margin: 4rem 0 1.5rem;
}
.project { padding-bottom: 2rem; border-bottom: 1px solid var(--line); margin-bottom: 2rem; }
.project h3 { font-size: 1.2rem; margin: 0 0 .3rem; }
.meta { font-size: .9rem; color: var(--dim); margin: 0 0 .8rem; }
p { max-width: 65ch; }
a { color: var(--accent); }
img { max-width: 100%; height: auto; border-radius: 8px; }
.contact a { display: inline-block; margin-right: 1.5rem; }
@media (prefers-color-scheme: dark) {
:root { --ink: #eef0f5; --dim: #9aa2b2; --line: #262a33; --accent: #7c9bff; }
body { background: #0d0f14; }
}
</style>
</head>
<body>
<main class="wrap">
<h1>Maria Chen</h1>
<p class="tagline">
I design onboarding flows for fintech apps. Based in Lisbon,
available for project work from March.
</p>
<h2>Selected work</h2>
<article class="project">
<h3>Redesigning checkout for Northwind</h3>
<p class="meta">Lead designer · 2025 · 6 weeks</p>
<p>Cart abandonment sat at 74%. I rebuilt the three-step checkout as a
single page with inline validation. Abandonment fell to 51%.</p>
</article>
<article class="project">
<h3>Design system for Kestrel Bank</h3>
<p class="meta">Contract · 2024</p>
<p>Consolidated four product teams onto one component library,
cutting new-screen build time roughly in half.</p>
</article>
<h2>About</h2>
<p>Eight years in product design, the last four in financial services.
I like problems where the interface is the product.</p>
<h2>Contact</h2>
<p class="contact">
<a href="mailto:maria@example.com">maria@example.com</a>
<a href="https://linkedin.com/in/example" target="_blank" rel="noopener">LinkedIn</a>
</p>
</main>
</body>
</html>
That page includes a dark-mode variant, scales from phone to desktop, and loads in well under a second because there is nothing to load — no fonts, no framework, no images until you add them.
Images, when everything lives in one file
Project screenshots are the one thing a single-file portfolio has to think about. Options:
- External URLs — an image host, a CDN, or a GitHub raw link. Simplest, and keeps your HTML small.
- Data URIs — fine for a logo or a small diagram, bad for photographs. Base64 inflates a file by about a third, so a 500 KB screenshot becomes 670 KB of HTML that has to download before anything renders.
- Describe instead of show — underrated. A crisp sentence about the outcome often does more than a screenshot of a UI the reader has no context for.
Whatever you choose, compress first. A hero image that is 3 MB will undo every other performance decision on the page.
Publishing it
Because the whole portfolio is one self-contained file, a one-file host is a direct fit. On hdply: paste the HTML, choose your-name.hdply.com, save. It's live with HTTPS, and later edits are made in the browser editor.
Two things worth doing straight after publishing:
- Turn on search indexing if you want to be findable — sites are published
noindexby default so that drafts don't leak into search results. - Add Open Graph tags, so the link shows a proper card when a recruiter forwards it. See the OG image guide.
If you want yourname.com rather than a subdomain, you'll need a host that supports custom domains — hdply doesn't. The same HTML file moves to GitHub Pages or Cloudflare Pages unchanged; see the hosting comparison.
Before you send the link
- Open it on a phone. Most recruiters will.
- Click every link, including the email one.
- Read it aloud — that's how you catch the sentences that sound like a template.
- Ask someone outside your field what you do, having read only the top of the page.
- Check the
<title>. It's the bookmark name and the search result headline.
Related: Make your page mobile-friendly · Add a contact form