A Modern HTML Template (2022)

Copy-paste this minimal, modern template to start a new HTML5 web page with automatic use of system fonts, preference-respecting color themes, and mobile-friendly responsive sizing. Alternatively, just use the CSS for a quick entry into a modern look with dark mode support.

I recommend starting here when you are working on a proof of concept, spinning up a new website, or authoring a one-off web page. You’ll be effortlessly opting into good reading experiences on desktop and mobile, banishing that default Times New Roman look, and saving your eyes with dark mode for those late-night hacking sessions.

The CSS in particular is good to drop into a new CodePen, JSFiddle, JSBin, or other playground. I do it all the time! The links in this paragraph will take you to a template you can easily fork for your next experiment or demo.

HTML

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <title>Untitled</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <!-- <meta name="description" content="TODO" /> -->
    <!-- <link rel="stylesheet" href="TODO" /> -->
  </head>
  <body>
    <h1>Untitled</h1>

    <p>A nearly-blank page beginning with <code>&lt;!doctype html&gt;</code></p>

    <p>
      <a href="https://alanhogan.com/code/modern-html-template"
        >Based on the modern HTML template</a
      >
      by <a href="https://alanhogan.com/">Alan Hogan</a>
    </p>
  </body>
</html>

CSS

/* Automatic dark mode, system fonts, and readable max-width
 * via https://ajh.us/template */

:root {
  --page-bg-color: white;
  --text-color: #222;
  --link-color: #06d;
  --visited-link-color: #91e;
  --code-color: #456658;

  color-scheme: light;
}

@media (prefers-color-scheme: dark) {
  :root {
    --page-bg-color: #141414;
    --text-color: #eee;
    --link-color: #2af;
    --visited-link-color: #c5f;
    --code-color: #c5eddc;

    /* Ask browser to render elements like inputs & scrollbars per dark theme */
    color-scheme: dark;
  }
}

* {
  box-sizing: border-box;
}
html {
  margin: 0;
  padding: 0;
  -webkit-text-size-adjust: 100%;
  -moz-text-size-adjust: 100%;
  text-size-adjust: 100%;
}
html,
body,
input,
button,
select,
textarea {
  font-family: system-ui, sans-serif;
}
body {
  padding: calc(0.5em + 0.5vmin);
  margin: 0 auto;
  max-width: 40em;
  background-color: var(--page-bg-color);
  color: var(--text-color);

  /* Good defaults for handling too-long words; won't help in most tables */
  overflow-wrap: break-word;
}

:link {
  color: var(--link-color);
}
:visited {
  color: var(--visited-link-color);
}

code,
tt,
kbd,
pre {
  font-family: "JetBrains Mono", "IBM Plex Mono", "Source Code Pro",
    "Cascadia Code", ui-monospace, Menlo, "Ubuntu Monospace", "Inconsolata",
    "Fira Code", "Deja Vu Sans Mono", "Bitstream Vera Sans Mono", Monaco,
    "Segoe UI Mono", "Roboto Mono", "Oxygen Mono", Consolas, monospace;
  /* Fight famous 'code too big' problem */
  font-size: 90%;
  color: var(--code-color);
}
pre code,
pre tt,
pre kbd {
  /* We don't want 90% of 90% for <pre><code> */
  font-size: 100%;
}

/* End automatic dark mode, system fonts, and readable max-width */

Preview

This is an iframe showing the above snippets in action.

Notes

  • This template indicates that content will be in English (en). HTML documents without a lang attribute aren’t valid.
  • It declares a UTF-8 charset, which you should probably be using anyway.
  • The body is set to a default maximum width of 40 em, a great default for readable line widths. If this is limiting, move the relevant max-width line so that it applies to some sort of wrapping container instead.
  • A responsive viewport is declared, meaning no side-scrolling should be necessary on mobile. Users can still pinch to zoom if desired. Notably, if you drop in big elements such as a large image, they will still cause side-scrolling without additional code.
  • Using text-size-adjust, we opt out of mobile browsers mucking with font sizes. These algorithms often result in unsightly, inconsistent scaling, and we don’t need them, because we are responsive out of the gate.
  • Dark mode is supported out of the box using CSS variables and the prefers-color-scheme media query. Natively drawn UI such as input elements and scrollbars will render in a dark-mode-friendly manner thanks to the color-scheme property.
  • Minimal base CSS styles are provided, including body background and text colors and unvisited and visited link colors. Most elements will display per browser default styles.
  • All elements are initialized to use border-box sizing.
  • All colors are set using CSS variables, which will fail to apply in Internet Explorer, Edge < 15, Android Browser < 5, and Opera Mini (where browser defaults will apply instead). The vast majority of Internet users are now in browsers that support CSS variables.
  • I provided a commented-out meta description tag in the head. Use it to suggest content for search engine results pages.
  • This HTML (check) and CSS (check) pass validation.
  • In early drafts of this post, I had included a no-js utility class on html and then removed it with JavaScript. However, in keeping with this template’s goals of being modern, minimal, and best-practice oriented, I am no longer including this code. Instead of using a class on HTML in order to show/hide content based on the availability of JavaScript, I would encourage the use of progressive enhancement (adding or changing the UI with JS, when enabled, and gracefully handling the no-JS experience when possible) and noscript tags (a built-in way to provide content for browsers without JS enabled or supported).1

Modern means slim

Here are some things that you might have seen here if I created this template a decade or so earlier:

  • A directive opting into the latest MSIE rendering engine.2 You don’t need this anymore:
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">

  • An XML namespace and/or XHTML doctypes

  • MIME types in <link rel="stylesheet"> and <script> tags

  • An “HTML5 reset” to help old browsers play more nicely with new HTML tags

  • Any sort of polyfill or feature detection

Are you familiar with the HTML5 Boilerplate project? In many senses it is a direct competitor to my much slimmer template, which is like an HTML5 boilerplate without the boilerplate. You might still want to check out that project if you are embarking on a big project, because it comes with a lot more. (But maybe you don't want or need more.) It used to come with jQuery until 2020. It still ships with Modernizr (feature detection), an NPM package.json file, an iconset, and a lot more that you may or may not actually want. My humble offering is a fraction of the size and complexity — more focused on table stakes, not the whole menu.

What you might want to add

  • OpenGraph meta tags for richer link previews in social media: I’m not an expert here, but I think this is a good place to get started.

  • Manual dark/light mode switching: See this codepen for a working example that doesn't require JavaScript, or this one for a JavaScript-based solution that detects the user's system preference and allows persistent overrides with localStorage (I really like this one!).

  • Feature detection-based warnings, errors, or redirections for users of incapable browsers: This is a topic for another day, but I strongly recommend using the technique of 'feature detection' for required web platform features and 'progressive enhancement' and/or 'graceful degradation' for features that 'merely' improve the experience.

  • More typographical love: Web typography is an enormous topic, but if you have any narrow columns in your layout, you may want to consider applying hyphens: auto; (and -webkit-hyphens: auto) to them, to allow long words to break gracefully with hyphenation, newspaper-style.

Final thoughts

Web development has, in many ways, gotten incredibly complex over the last 20 years or so, with long chains of technologies used for development and deployment of web apps. But the simple meat and potatoes of web technologies — “vanilla” HTML, CSS, and JS — these have gotten simpler and easier in the years since the HTML5 project was undertaken (and since Microsoft abandoned Trident). Why? Because HTML5 was a huge success. As I view it, HTML5 was an effort to (1) pave the cowpaths of how people actually write HTML instead of insisting on theory-based adherence to specifications, and (2) standardize all the different browsers in terms of how they handle edge cases, malformed code, and web standards in general. In these aims HTML5 has been hugely effective. It means you really can start making beautiful, performant websites that meet users on their preferred devices (and with their preferred color scheme!) using incredibly few lines of code. For that, I am grateful.

Thanks to Matthew Chavez, Louis Cruz, and Greg Hogan for feedback on a draft of this post.


  1. If you really want the .no-js class, let me know why, would you? You can do it by adding class="no-js" to <html> and adding these lines within the head:

    <script>
      document.documentElement.classList.remove("no-js");
    </script>
    

    ↩︎

  2. Previously on AlanHogan.com ↩︎


December 8th, 2022. (Updated: December 30, 2022 at 11:19am.)
Alan Hogan (@alanhogan_com).  Contact · About