<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: htmlput</title>
    <description>The latest articles on DEV Community by htmlput (@htmlput).</description>
    <link>https://dev.to/htmlput</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4172919%2F32da8018-f77e-41a6-93d0-571ceb08e4cd.png</url>
      <title>DEV Community: htmlput</title>
      <link>https://dev.to/htmlput</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/htmlput"/>
    <language>en</language>
    <item>
      <title>5 ways to put a single HTML file online, actually tested</title>
      <dc:creator>htmlput</dc:creator>
      <pubDate>Thu, 22 Oct 2026 05:08:23 +0000</pubDate>
      <link>https://dev.to/htmlput/5-ways-to-put-a-single-html-file-online-actually-tested-4156</link>
      <guid>https://dev.to/htmlput/5-ways-to-put-a-single-html-file-online-actually-tested-4156</guid>
      <description>&lt;p&gt;Last week I published a Pomodoro timer as one self-contained HTML file — no build step, no dependencies, about 1.6 KB. The obvious next question: how do you actually get a file like that onto the internet?&lt;/p&gt;

&lt;p&gt;I took the exact same file and ran it through five popular options in October 2026, timing each and writing down what each one really asks of you. No marketing copy — just the flows as they are.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. GitHub Pages
&lt;/h2&gt;

&lt;p&gt;The classic. Push your file to a repo, turn on Pages, get a &lt;code&gt;username.github.io/repo&lt;/code&gt; link.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What actually happened:&lt;/strong&gt; I wasn't signed into GitHub in this session, so the flow stops at the account wall: create a repo, add the file as &lt;code&gt;index.html&lt;/code&gt;, go to Settings → Pages, deploy from a branch, wait about a minute for the first build.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Time to link:&lt;/strong&gt; not timed hands-on (account required); about a minute after the first push per the documented flow.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Cloudflare Pages
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What actually happened:&lt;/strong&gt; same story — this one needs a Cloudflare account before anything else. After that it's "Upload assets" (drag a folder straight into the dashboard, no git needed) or connect a git repo, then deploy.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Time to link:&lt;/strong&gt; not timed (account required).&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Netlify Drop
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What actually happened:&lt;/strong&gt; the only one of the five with no account gate on the front door. Open app.netlify.com/drop and you get a giant "Drag &amp;amp; drop to publish instantly" zone — drop a file, a folder, or a zip (their FAQ explicitly blesses dropping a single &lt;code&gt;index.html&lt;/code&gt;), or use the file/folder picker buttons. No login to start dropping.&lt;/p&gt;

&lt;p&gt;I couldn't complete the drop in this session — I had the file as pasted text, not as a file on disk — but the UI is exactly what it claims to be. Their FAQ: drops stay on the free plan "for as long as you like," and you can rename the project or attach a custom domain from the dashboard (which is where the account comes in).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Time to link:&lt;/strong&gt; seconds after the drop, by design; not timed end-to-end here.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. htmlput — paste the code
&lt;/h2&gt;

&lt;p&gt;(Disclosure: I built this one.)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What actually happened:&lt;/strong&gt; opened htmlput.com, pasted the full file into the editor — it counted "1.5 KB of 1 MB used" — ticked the Cloudflare "Verify you are human" checkbox, clicked "Host now"… and landed on the sign-in page: "Please sign in to host a page." Their FAQ confirms it: "Yes. Create a free account (or sign in), then paste HTML or upload a ZIP to get a share link in seconds."&lt;/p&gt;

&lt;p&gt;So the paste flow is real and quick, but anonymous hosting is gone — you need the free account first. The free tier is 30 MB of storage, and per their FAQ, "hosted pages stay available and share links keep working unless you delete the page."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Time to link:&lt;/strong&gt; blocked at sign-in; the paste-and-verify part took under a minute.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. tiiny.host — Paste HTML
&lt;/h2&gt;

&lt;p&gt;(Side note: itty.bitty, the link-in-a-URL trick, no longer resolves — DNS failure, tried twice — so tiiny.host stood in for this slot.)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What actually happened:&lt;/strong&gt; opened tiiny.host, switched to the "Paste HTML" tab, pasted the code (their editor reformatted it), clicked Continue — the file staged as &lt;code&gt;code.html (index)&lt;/code&gt;, 1.67 KB — and then: "Please verify your email. We need to verify your email to reduce spam." Google, GitHub, Microsoft, or Yahoo SSO, or an email field. I stopped there rather than create an account.&lt;/p&gt;

&lt;p&gt;Their FAQ: links stay online indefinitely on the free plan as long as you log into your account once every 3 months.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Time to link:&lt;/strong&gt; blocked at email verification.&lt;/p&gt;

&lt;h2&gt;
  
  
  The comparison
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;Steps&lt;/th&gt;
&lt;th&gt;Time to link&lt;/th&gt;
&lt;th&gt;Custom domain on free&lt;/th&gt;
&lt;th&gt;Expiry&lt;/th&gt;
&lt;th&gt;Best for&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;GitHub Pages&lt;/td&gt;
&lt;td&gt;new repo → add &lt;code&gt;index.html&lt;/code&gt; → Settings → Pages → deploy from branch&lt;/td&gt;
&lt;td&gt;~1 min after push (documented; account needed, not timed)&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;td&gt;none&lt;/td&gt;
&lt;td&gt;maintained projects&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cloudflare Pages&lt;/td&gt;
&lt;td&gt;account → Pages → Upload assets or connect git → deploy&lt;/td&gt;
&lt;td&gt;not timed (account needed)&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;td&gt;none&lt;/td&gt;
&lt;td&gt;projects that will grow&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Netlify Drop&lt;/td&gt;
&lt;td&gt;open app.netlify.com/drop → drop file, folder, or zip&lt;/td&gt;
&lt;td&gt;seconds after drop (not timed end-to-end; no local file in session)&lt;/td&gt;
&lt;td&gt;via dashboard (account)&lt;/td&gt;
&lt;td&gt;none — "as long as you like"&lt;/td&gt;
&lt;td&gt;fastest share of a file on disk&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;htmlput&lt;/td&gt;
&lt;td&gt;paste code → verify you're human → Host now → sign in&lt;/td&gt;
&lt;td&gt;blocked at sign-in (free account required)&lt;/td&gt;
&lt;td&gt;not stated&lt;/td&gt;
&lt;td&gt;none, until you delete the page&lt;/td&gt;
&lt;td&gt;paste-and-share once you have an account&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;tiiny.host&lt;/td&gt;
&lt;td&gt;Paste HTML tab → paste → Continue → verify email&lt;/td&gt;
&lt;td&gt;blocked at email verification&lt;/td&gt;
&lt;td&gt;feature listed; free-tier terms not verified&lt;/td&gt;
&lt;td&gt;indefinite if you log in every 3 months&lt;/td&gt;
&lt;td&gt;quick share once you have an account&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  How to choose (no winner declared)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Maintained project → Pages.&lt;/strong&gt; Git history, real custom domains, nothing expires. The minute of setup pays off forever.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;One file in 30 seconds → a paste host.&lt;/strong&gt; Just know that in 2026 both paste options I tried want an account first — "instant" starts after signup.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A file sitting on your disk and zero patience for signup forms → Netlify Drop.&lt;/strong&gt; It's the only flow here that goes from file to link with no account in between.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The honest surprise of this test: the "no signup needed" era of paste-and-host is mostly over. The real cost was never money — it's the account.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>html</category>
      <category>beginners</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>A Pomodoro timer in 20 lines of vanilla JS</title>
      <dc:creator>htmlput</dc:creator>
      <pubDate>Thu, 22 Oct 2026 05:01:59 +0000</pubDate>
      <link>https://dev.to/htmlput/a-pomodoro-timer-in-20-lines-of-vanilla-js-4h4l</link>
      <guid>https://dev.to/htmlput/a-pomodoro-timer-in-20-lines-of-vanilla-js-4h4l</guid>
      <description>&lt;p&gt;Most first JavaScript projects die in setup. You pick a framework, install a toolchain, and by the time anything appears on screen you've forgotten what you wanted to learn.&lt;/p&gt;

&lt;p&gt;A Pomodoro timer is the opposite. It needs exactly three things: a heartbeat, a piece of state, and a screen. That's it. No libraries, no build step, no backend. One HTML file, a handful of lines of JavaScript, and you end up understanding the core loop behind nearly every interactive UI ever built.&lt;/p&gt;

&lt;h2&gt;
  
  
  The three moving parts
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. &lt;code&gt;setInterval&lt;/code&gt; is the heartbeat.&lt;/strong&gt; The entire timer is this line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;timer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;setInterval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tick&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every second, the browser calls &lt;code&gt;tick()&lt;/code&gt;. Everything else is decoration around that one repeating pulse.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. One variable is the entire state.&lt;/strong&gt; &lt;code&gt;let left = FOCUS&lt;/code&gt; — seconds remaining. 25 x 60 = 1500. There is no state object, no store, no reducer. The timer's whole memory is a single number counting down. When you're learning, that's the point: state doesn't have to be architecture. It can be a number.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. &lt;code&gt;textContent&lt;/code&gt; is the render.&lt;/strong&gt; A &lt;code&gt;fmt&lt;/code&gt; helper turns seconds into &lt;code&gt;MM:SS&lt;/code&gt; with &lt;code&gt;padStart&lt;/code&gt;, and &lt;code&gt;tick&lt;/code&gt; writes it to the page:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;left&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No virtual DOM, no diffing — you compute the string, you set the string. Every framework's render cycle is a fancier version of this exact idea.&lt;/p&gt;

&lt;h2&gt;
  
  
  The pause/resume trick
&lt;/h2&gt;

&lt;p&gt;Here's my favorite part. Look at the start button handler:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;btn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onclick&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;timer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nf"&gt;clearInterval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;timer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="nx"&gt;timer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;btn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Resume&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;left&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;left&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;FOCUS&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;timer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;setInterval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tick&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="nx"&gt;btn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Pause&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pausing isn't a mode. It's the &lt;em&gt;absence&lt;/em&gt; of the interval. &lt;code&gt;timer&lt;/code&gt; is either an interval ID (running) or &lt;code&gt;null&lt;/code&gt; (paused), so the variable doubles as the paused flag. Beginners often reach for an &lt;code&gt;isPaused&lt;/code&gt; boolean and then spend an hour keeping it in sync with reality. The trick: make the state &lt;em&gt;be&lt;/em&gt; the reality. If there is no interval, you're paused. Nothing to sync, nothing to drift.&lt;/p&gt;

&lt;p&gt;Reset is the same idea in reverse: clear the interval, put 1500 back into &lt;code&gt;left&lt;/code&gt;, redraw.&lt;/p&gt;

&lt;h2&gt;
  
  
  The full file
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"en"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;charset=&lt;/span&gt;&lt;span class="s"&gt;"UTF-8"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"viewport"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"width=device-width, initial-scale=1.0"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;Pomodoro — 25 min focus&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;style&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;body&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;font-family&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;system-ui&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;grid&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="py"&gt;place-items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nb"&gt;center&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="nl"&gt;min-height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="m"&gt;100vh&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="nl"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="m"&gt;#0f172a&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="m"&gt;#f1f5f9&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nf"&gt;#time&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="m"&gt;5rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="nl"&gt;font-variant-numeric&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;tabular-nums&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="nl"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;1rem&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="m"&gt;1rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="m"&gt;.6rem&lt;/span&gt; &lt;span class="m"&gt;1.4rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="nl"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="m"&gt;.3rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="nl"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="m"&gt;999px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="nl"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nb"&gt;pointer&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nf"&gt;#start&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="m"&gt;#22c55e&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="m"&gt;#052e16&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="nf"&gt;#reset&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="m"&gt;#334155&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="m"&gt;#e2e8f0&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/style&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"text-align:center"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"time"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;25:00&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"start"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Start&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"reset"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Reset&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;FOCUS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;25&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;left&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;FOCUS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;timer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;el&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;time&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;btn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;start&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fmt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;floor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;padStart&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;0&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;padStart&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;0&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tick&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;left&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="nx"&gt;left&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nf"&gt;clearInterval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;timer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="nx"&gt;timer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;btn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Start&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nf"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Time's up — take a break!&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="nx"&gt;btn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onclick&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;timer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nf"&gt;clearInterval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;timer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="nx"&gt;timer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;btn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Resume&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;left&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;left&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;FOCUS&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;timer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;setInterval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tick&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="nx"&gt;btn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Pause&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;reset&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;onclick&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nf"&gt;clearInterval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;timer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="nx"&gt;timer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;left&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;FOCUS&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;left&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="nx"&gt;btn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Start&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Copy it into a file called &lt;code&gt;pomodoro.html&lt;/code&gt;, open that file in your browser, and you have a working focus timer. No server, no install, nothing to configure.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fork it in 30 seconds:&lt;/strong&gt; change one constant — &lt;code&gt;const FOCUS = 5 * 60;&lt;/code&gt; — and you get a 5-minute test run instead of waiting 25 minutes to see the "Time's up" alert. Edit, refresh, observe: the fastest feedback loop in programming.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to build next
&lt;/h2&gt;

&lt;p&gt;This timer is a seed. Each natural upgrade teaches one new concept:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Break mode&lt;/strong&gt; — alternate 25-minute focus blocks with 5-minute breaks. Teaches you state machines (a &lt;code&gt;mode&lt;/code&gt; variable: &lt;code&gt;'focus'&lt;/code&gt; or &lt;code&gt;'break'&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A sound when time's up&lt;/strong&gt; — swap the &lt;code&gt;alert&lt;/code&gt; for the Web Audio API. Teaches you browser APIs beyond the DOM.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A streak counter in &lt;code&gt;localStorage&lt;/code&gt;&lt;/strong&gt; — persist completed sessions across reloads. Teaches you the only browser database most small projects ever need.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Pick one, and the 20-line timer becomes a real app — still in a single file.&lt;/p&gt;

</description>
      <category>html</category>
      <category>javascript</category>
      <category>beginners</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Single-file HTML is underrated: lessons from shipping 50+ one-page sites</title>
      <dc:creator>htmlput</dc:creator>
      <pubDate>Sat, 10 Oct 2026 04:05:22 +0000</pubDate>
      <link>https://dev.to/htmlput/single-file-html-is-underrated-lessons-from-shipping-50-one-page-sites-3gj9</link>
      <guid>https://dev.to/htmlput/single-file-html-is-underrated-lessons-from-shipping-50-one-page-sites-3gj9</guid>
      <description>&lt;p&gt;Everyone I know reaches for a framework before they've finished the sentence describing what they're building. Next.js for a landing page. A full CMS for a project retrospective. I've done it too.&lt;/p&gt;

&lt;p&gt;But over the last year I've shipped 50+ single-file HTML pages — reports, explainers, dashboards, interactive demos — and I've become convinced the humble &lt;code&gt;.html&lt;/code&gt; file is the most underrated medium on the web. Here's what I learned.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. One file forces one idea
&lt;/h3&gt;

&lt;p&gt;When your entire page lives in a single file, scope creep becomes physically painful. There's no &lt;code&gt;components/&lt;/code&gt; folder to hide half-finished ideas in. Every section has to justify its existence, because you can feel the file getting heavy. This constraint produces tighter writing and clearer structure than any linter ever did for me.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. A URL beats a repo
&lt;/h3&gt;

&lt;p&gt;The moment someone can open your work in a browser with zero setup, your distribution changes completely. "Clone this, npm install, ..." is a funnel with 90% drop-off at each step. A link is a funnel with one step. For anything meant to be &lt;em&gt;read&lt;/em&gt; rather than &lt;em&gt;run&lt;/em&gt; — reports, proposals, visual explainers — a single file shared via URL will reach 10x the people.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Own your bytes
&lt;/h3&gt;

&lt;p&gt;A single HTML file is the most portable artifact in computing. It works offline. It works in 20 years. No build step to rot, no dependency to deprecate, no platform to shut down your account. I've re-opened pages I made a decade ago and they render fine. Try that with a framework project.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Know when NOT to do it
&lt;/h3&gt;

&lt;p&gt;Single-file breaks down when: you need a database, you have more than ~3 distinct page types, or multiple people edit concurrently. It's a medium for documents and demos, not applications. Respecting that boundary is the whole trick.&lt;/p&gt;

&lt;h3&gt;
  
  
  Practical tips that actually matter
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Inline everything, but watch the weight.&lt;/strong&gt; CSS and JS inline is the point — but base64 images balloon fast. Keep total under ~500KB or split images out.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Design for the phone first.&lt;/strong&gt; Over 60% of my page views are mobile. If your layout needs horizontal scrolling on a 390px viewport, it failed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Print styles are free goodwill.&lt;/strong&gt; Five lines of &lt;code&gt;@media print&lt;/code&gt; CSS makes your page genuinely useful as a document. Nobody does this; everyone appreciates it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Semantic HTML is your SEO.&lt;/strong&gt; No framework, no meta framework — just real headings, real paragraphs, alt text. It works.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The takeaway
&lt;/h3&gt;

&lt;p&gt;We rebuilt the web to be programmable and forgot it was supposed to be &lt;em&gt;readable&lt;/em&gt;. Before you scaffold a project, ask: could this just be a really good HTML file? Half the time, the answer is yes — and you'll ship it today instead of next month.&lt;/p&gt;

</description>
      <category>html</category>
      <category>webdev</category>
      <category>beginners</category>
      <category>productivity</category>
    </item>
  </channel>
</rss>
