basic html document structure

the skeleton every page starts with.

beginner html
easy to follow, before styling anything, every page needs this same basic skeleton — here's what each piece actually does.

the full skeleton

every HTML page, no matter how simple or complex, starts from this same template:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>my page</title>
  <meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>

  <!-- everything visible goes here -->

</body>
</html>

breaking it down

  • <!DOCTYPE html> — tells the browser "this is a modern HTML5 document." always the very first line.
  • <html lang="en"> — wraps the whole page. the lang attribute tells browsers and screen readers what language the content is in.
  • <head> — holds information about the page that isn't shown directly on screen: title, character encoding, linked fonts and stylesheets, favicon.
  • <meta charset="UTF-8"> — makes sure special characters (like ♡ or accented letters) render correctly instead of turning into garbled symbols.
  • <title> — the text shown in the browser tab.
  • <meta name="viewport" ...> — tells mobile browsers to render the page at the device's actual width instead of zoomed out. skip this and your page will look tiny on phones.
  • <body> — everything the visitor actually sees: text, images, buttons, all of it.

adding fonts and styles

custom fonts (like the ones used across this site) get linked inside <head>, before your own <style> block:

<head>
  ...
  <link rel="preconnect" href="https://fonts.googleapis.com">
  <link href="https://fonts.googleapis.com/css2?family=Nunito&display=swap" rel="stylesheet">

  <style>
    body { font-family: 'Nunito', sans-serif; }
  </style>
</head>
tip: the preconnect link isn't strictly required, but it lets the browser start connecting to google fonts' servers early, so the font loads a little faster.

a more complete head

the skeleton above covers the bare minimum. in practice, most real pages — including this one — pack a lot more into <head>: seo hints, social media preview tags, favicons, font connections, and scripts. here's what this exact page's <head> looks like in full:

<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">  
<title>html structure ♡ tutorials</title>
<link rel="shortcut icon" href="https://kkul.store/media/graphics/melodys.gif">
<meta name="robots" content="nosnippet">
<meta name="robots" content="max-snippet:0">
<meta name="robots" content="noarchive">
<meta name="robots" content="notranslate">
<meta name="robots" content="nositelinkssearchbox">
<meta name="robots" content="max-video-preview:0">
<meta name="robots" content="max-image-preview:standard">
<meta name="title" content="tutorials ♡">
<meta name="description" content="made with ribbons & honey">
<meta name="url" content="https://kkul.store/">
<meta name="identifier-URL" content="https://kkul.store/">
<meta name="thumbnail" content="https://kkul.store/media/backgrounds/chii-og.jpg">
<meta property="og:url" content="https://kkul.store/">
<meta property="og:title" content="tutorials ♡">
<meta property="og:site_name" content="https://kkul.store/">
<meta property="og:description" content="made with ribbons & honey">
<meta property="og:type" content="website">
<meta property="og:image" content="https://kkul.store/media/backgrounds/chii-og.jpg">
<meta property="og:image:type" content="image/jpeg">
<meta property="og:image:width" content="1200">
<meta property="og:image:height" content="630">
<meta property="og:image:alt" content="illustration by _mika_0_0">
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:url" content="https://kkul.store/">
<meta name="twitter:title" content="tutorials ♡">
<meta name="twitter:description" content="made with ribbons & honey">
<meta name="twitter:image" content="https://kkul.store/media/backgrounds/chii-og.jpg">
<meta name="twitter:image:alt" content="illustration by _mika_0_0">
<meta name="copyright" content="© 2026 kkul.store. all rights reserved.">
<meta name="author" content="mocha">
<link rel="icon" href="/media/graphics/melodys.gif" type="image/gif">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="stylesheet" href="/styles/tutorial-post.css">
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script src="https://static.tumblr.com/lspzyz3/xloqk6cgp/jquery.style-my-tooltips.js"></script>

that's a lot at once, so let's go through it group by group.

meta tags: search engines & robots

these tags don't change how the page looks to a visitor at all — they're instructions aimed at search engine crawlers (google, bing, etc), telling them how to treat and display this page in search results.

  • <meta name="robots" content="nosnippet"> — normally, google shows a short preview of your page's text underneath the title in search results (called a "snippet"). this tag tells google not to generate one at all, so only the title and url show up.
  • content="max-snippet:0" — a more explicit version of the same idea: it caps the snippet length at exactly zero characters, which functions the same as nosnippet but is the newer, more precise way to write it.
  • content="noarchive" — google normally keeps a saved "cached" copy of your page, viewable even if your site goes down temporarily. this tag disables that cached copy from being offered.
  • content="notranslate" — google sometimes shows a "translate this page" prompt in search results if it detects the content is in a language different from the searcher's. this tag turns that offer off.
  • content="nositelinkssearchbox" — for well-known sites, google sometimes adds a mini search box directly under the main search result, letting people search your site from google itself. this opts out of that feature.
  • content="max-video-preview:0" — if your page has embedded video, google may auto-play a short preview of it right in search results. setting this to 0 disallows any video preview from being generated.
  • content="max-image-preview:standard" — controls how large an image thumbnail google is allowed to show for this page in results. standard caps it at a normal size instead of the largest possible ("large") or none at all ("none").
tip: you don't have to write out seven separate <meta name="robots"> tags like this — they can be combined into a single line by comma-separating the values: <meta name="robots" content="nosnippet, noarchive, notranslate">. writing them out individually (like this page does) works exactly the same, it's just more verbose and easier to toggle one on/off at a time.

meta tags: title, description & identity

this group tells search engines (and some browser tools) what the page is fundamentally about.

  • <meta name="title"> — a duplicate of the page's title, stored as metadata rather than in the visible <title> tag. most modern search engines actually ignore this and read the real <title> tag instead, but some older tools and browser extensions still check it, so it's harmless to include as a backup.
  • <meta name="description"> — the short one or two sentence summary that search engines display underneath your title in the results list. this is one of the few meta tags that actually affects what a visitor sees before clicking, so it's worth writing something inviting and accurate.
  • <meta name="url"> and <meta name="identifier-URL"> — both point back to the "real," canonical address of this content. they help tools understand where the authoritative version of the page lives, especially useful if the same content is ever mirrored or accessible from more than one url.

open graph tags (the og: ones)

open graph is a standard originally created by facebook, now used by nearly every platform (discord, tumblr, imessage, whatsapp, slack, and more) to generate the little preview card that appears when someone pastes your link somewhere. without these tags, a shared link just shows a bare, plain-text title and url — no image, no description, nothing eye-catching.

  • og:url — the canonical link this content officially lives at. platforms use this so the preview card links back to the correct address, even if the link was shared from a slightly different url.
  • og:title — the bold, large text shown at the top of the preview card.
  • og:description — the smaller text shown underneath the title in the card.
  • og:site_name — the site's name, usually shown in small text above or below the title, so people recognize which site the link is from at a glance.
  • og:type — categorizes what kind of content this is. common values are website for a general page or article for a blog-style post; some platforms use this to slightly change how the card is laid out.
  • og:image — the thumbnail image shown in the preview card. this is usually the single most important open graph tag, since it's what actually catches someone's eye.
  • og:image:type — the mime type of that image file (e.g. image/jpeg, image/png), so the receiving platform knows how to decode it without having to guess.
  • og:image:width / og:image:height — the exact pixel dimensions of the image. providing these lets the platform reserve the right amount of space for the image immediately, instead of waiting for the image to load first to figure out its size (which would otherwise cause the preview card to visibly "jump" as it loads).
  • og:image:alt — alternative text describing the image, read aloud by screen readers for visitors who are visually impaired, and shown as a fallback if the image fails to load.
tip: the ideal og:image size is 1200×630 pixels — that's roughly a 1.91:1 ratio, which is what most platforms crop preview images to. using a different ratio risks having the important part of your image cropped out awkwardly on some platforms.

twitter card tags

twitter/X reads open graph tags as a fallback, but has its own separate set of tags for finer control over how links look specifically on that platform.

  • twitter:card — sets the overall layout type of the preview. summary_large_image (used here) shows one large image spanning the full width above the title; the alternative, summary, shows a small square thumbnail beside the text instead.
  • twitter:url, twitter:title, twitter:description, twitter:image — these mirror their og: counterparts exactly, letting you set twitter-specific text/images if you ever want them to differ from the open graph versions. here they're set to the same content as the og: tags.
  • twitter:image:alt — alt text specifically for the twitter preview image, same purpose as og:image:alt but read by twitter's own accessibility tools.

credits & housekeeping meta tags

  • <meta name="copyright"> — a plain-text copyright notice stored as metadata. browsers don't display or enforce this in any way — it's purely informational, mostly read by archival tools or humans viewing the page source.
  • <meta name="author"> — states who made the page. like copyright, this isn't shown anywhere visible and isn't enforced — it's just good practice to credit yourself in the source code.

favicon links

the favicon is the small icon shown in the browser tab, in bookmarks, and in browser history next to the page title.

  • <link rel="shortcut icon"> — the older, legacy way of declaring a favicon, still respected by some older browsers.
  • <link rel="icon"> — the modern standard way to declare the same thing, including a type attribute (here, image/gif) so the browser knows the file format upfront without having to inspect it.
tip: including both isn't strictly necessary anymore in most modern browsers, but it's a common safety net to make sure the icon shows up consistently everywhere, including in older browsers that only check for shortcut icon.

connecting to external resources early

loading fonts (or any resource) from another domain normally means the browser has to do several separate steps before it can even start downloading the file: look up the domain's address (DNS lookup), open a secure connection to it (TLS handshake), and then finally request the file. each of these steps takes a small amount of time, and normally they only start once the browser reaches the actual <link> or <script> tag that needs the resource.

  • <link rel="preconnect" href="https://fonts.googleapis.com"> — tells the browser to get a head start on connecting to google fonts' main server, before it's actually needed. this is where the font's css file (which lists what font files to download) is hosted.
  • <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> — the actual font files themselves (the .woff2 files with the letter shapes) are served from this second, different domain, so it needs its own separate preconnect. the crossorigin attribute is required here because fonts are fetched with what's called "anonymous mode" cross-origin requests — without it, the browser would open a second, wasted connection instead of reusing this preconnected one.
  • <link rel="stylesheet" href="/styles/tutorial-post.css"> — this is a normal stylesheet link (no preconnect needed, since it's on the same domain as the page itself), loading the css specific to this tutorial page's layout.
tip: preconnect is most worth using for resources you know for certain will be needed as soon as the page loads (like fonts used in the initial view). overusing it on resources that might not load at all can actually waste the visitor's bandwidth by opening connections that go unused.

loading scripts in the head

by default, when a browser reaches a <script> tag, it stops building the rest of the page, downloads the script fully, runs it top to bottom, and only then continues. placing scripts in <head> means this pause happens before the visitor sees anything at all, since <head> is parsed before <body>.

  • jquery-3.6.0.min.js — loads the jquery library itself, a toolkit that simplifies common javascript tasks like selecting elements and handling events. many older plugins (including the one below) are built expecting jquery to already exist.
  • jquery.style-my-tooltips.js — a plugin that adds styled, customizable tooltips, built directly on top of jquery's features. because it depends on jquery already being loaded and ready, its <script> tag has to come after jquery's tag in the document — script tags run strictly in the order they appear, so reversing these two would cause the tooltip plugin to fail immediately, since it would be looking for jquery before jquery exists yet.
note: loading scripts this way, directly in <head> with no special attributes, is sometimes called "render-blocking" — it can slightly delay the page from appearing, since the browser is busy downloading and running these scripts before it moves on to building the visible page. for scripts that aren't needed immediately, a common alternative is placing them right before the closing </body> tag instead, so the rest of the page loads first. but when a plugin strictly depends on load order (like the tooltip script here depending on jquery), keeping both together in <head>, in the correct order, is the simplest way to guarantee they work.

a note on structure vs. style

HTML's only job is to describe what something is — a heading, a paragraph, a link, an image. it shouldn't be responsible for how it looks. that's what CSS is for. keep them separate and your pages stay easy to maintain.

next step: once you're comfortable with this skeleton, check out the 3-column grid tutorial to start actually laying content out.