<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en"><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://izar.tortoizthemes.com/feed.xml" rel="self" type="application/atom+xml" /><link href="https://izar.tortoizthemes.com/" rel="alternate" type="text/html" hreflang="en" /><updated>2026-11-13T01:49:51+00:00</updated><id>https://izar.tortoizthemes.com/feed.xml</id><title type="html">Daniel Stephan</title><subtitle>Freelance front-end developer and designer. I design and code beautifully simple things.</subtitle><entry><title type="html">Why I still write plain CSS</title><link href="https://izar.tortoizthemes.com/notes/plain-css-is-enough/" rel="alternate" type="text/html" title="Why I still write plain CSS" /><published>2026-10-28T09:00:00+00:00</published><updated>2026-10-28T09:00:00+00:00</updated><id>https://izar.tortoizthemes.com/notes/plain-css-is-enough</id><content type="html" xml:base="https://izar.tortoizthemes.com/notes/plain-css-is-enough/"><![CDATA[<p>I still write plain CSS for almost every site I build. Clients sometimes expect a framework, and a few have asked me directly why I am not using one. The honest answer is that the browser has caught up with most of what frameworks were built to fix. Here is what I use, and when I reach for something else.</p>

<h2 id="custom-properties-do-the-heavy-lifting">Custom properties do the heavy lifting</h2>

<p>Custom properties are the first thing I set up on every project. I put colours, spacing and type sizes on <code class="language-plaintext highlighter-rouge">:root</code>, and each component reads from them.</p>

<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">:root</span> <span class="p">{</span>
  <span class="py">--color-ink</span><span class="p">:</span> <span class="m">#1d1b18</span><span class="p">;</span>
  <span class="py">--color-paper</span><span class="p">:</span> <span class="m">#f7f4ef</span><span class="p">;</span>
  <span class="py">--space-md</span><span class="p">:</span> <span class="m">1rem</span><span class="p">;</span>
<span class="p">}</span>

<span class="nc">.card</span> <span class="p">{</span>
  <span class="nl">color</span><span class="p">:</span> <span class="n">var</span><span class="p">(</span><span class="n">--color-ink</span><span class="p">);</span>
  <span class="nl">background</span><span class="p">:</span> <span class="n">var</span><span class="p">(</span><span class="n">--color-paper</span><span class="p">);</span>
  <span class="nl">padding</span><span class="p">:</span> <span class="n">var</span><span class="p">(</span><span class="n">--space-md</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<p>If the client changes the brand colour, I edit one line. Dark mode is a second block inside a media query, not a second stylesheet. Unlike Sass variables, custom properties exist at runtime, so a small script can change them too, for example when a visitor flips a theme switch.</p>

<h2 id="cascade-layers-stop-the-specificity-fights">Cascade layers stop the specificity fights</h2>

<p>Most big stylesheets do not go wrong because of the syntax. They go wrong because two rules fight over specificity, and somebody reaches for <code class="language-plaintext highlighter-rouge">!important</code>. Cascade layers fix that. I declare the order once at the top of the file, then put each part of the site into its own layer.</p>

<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">@layer</span> <span class="n">reset</span><span class="p">,</span> <span class="n">base</span><span class="p">,</span> <span class="n">components</span><span class="p">,</span> <span class="n">utilities</span><span class="p">;</span>
</code></pre></div></div>

<p>Anything in a later layer wins, however specific the selector is. My utilities layer sits last, so a one-off margin always works. I have not needed <code class="language-plaintext highlighter-rouge">!important</code> in a client project for a long time.</p>

<h2 id="container-queries-fit-components-not-screens">Container queries fit components, not screens</h2>

<p>Media queries ask about the window. A card in a narrow sidebar and the same card in a wide grid need different layouts, but the window width tells you nothing about either. Container queries ask about the space the component really has. I set <code class="language-plaintext highlighter-rouge">container-type: inline-size</code> on the wrapper, then write rules for the card inside it. The card now looks right wherever I drop it.</p>

<h2 id="when-a-framework-is-worth-it">When a framework is worth it</h2>

<p>I am not against frameworks. A team of six working on a large app, or a design system shared across several products, can gain a lot from a component model and a build pipeline. A five-page site for a dentist does not need either.</p>

<p>My rule is simple. If I can describe the whole design in a page of custom properties and a few layers, I skip the tooling. Every dependency is something to update, debug and pay for later. A plain stylesheet has no build step to break and no generated file to read when something looks wrong. The browser does the work, and the page loads faster for it.</p>

<blockquote>
  <p>Most CSS bugs I have seen came from a rule I forgot I wrote, not from the language itself. Keep the file small, and it stays easy to read.</p>
</blockquote>

<p>I still reach for a framework now and then. Most of the time, though, plain CSS is the quickest route to a site that looks right and stays easy to change.</p>]]></content><author><name></name></author><category term="CSS" /><category term="Front-end" /><summary type="html"><![CDATA[Why I still write plain CSS in 2026, and how custom properties, cascade layers and container queries cover most of what I need.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://izar.tortoizthemes.com/assets/img/og.png" /><media:content medium="image" url="https://izar.tortoizthemes.com/assets/img/og.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">A hundred on PageSpeed, without tricks</title><link href="https://izar.tortoizthemes.com/notes/a-hundred-on-pagespeed/" rel="alternate" type="text/html" title="A hundred on PageSpeed, without tricks" /><published>2026-10-09T09:00:00+00:00</published><updated>2026-10-09T09:00:00+00:00</updated><id>https://izar.tortoizthemes.com/notes/a-hundred-on-pagespeed</id><content type="html" xml:base="https://izar.tortoizthemes.com/notes/a-hundred-on-pagespeed/"><![CDATA[<p>A 100 on PageSpeed is not magic. Most of the time it comes from a short list of decisions made early, before the design is even finished. Here is the list I work through on every client site, and the reason behind each step.</p>

<h2 id="send-the-first-screen-in-the-first-response">Send the first screen in the first response</h2>

<p>A browser cannot paint the page until it has the CSS for it. If that CSS sits in a file the browser has to fetch first, the screen stays blank for longer. I inline the small amount of CSS needed for the top of the page, the critical CSS, straight into the head. The rest of the styles load as a normal stylesheet after that.</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;head&gt;</span>
  <span class="nt">&lt;style&gt;</span>
    <span class="nt">body</span> <span class="p">{</span> <span class="nl">margin</span><span class="p">:</span> <span class="m">0</span><span class="p">;</span> <span class="nl">font-family</span><span class="p">:</span> <span class="s1">"Inter"</span><span class="p">,</span> <span class="nb">sans-serif</span><span class="p">;</span> <span class="p">}</span>
    <span class="nc">.hero</span> <span class="p">{</span> <span class="nl">min-height</span><span class="p">:</span> <span class="m">70vh</span><span class="p">;</span> <span class="nl">display</span><span class="p">:</span> <span class="n">grid</span><span class="p">;</span> <span class="py">place-items</span><span class="p">:</span> <span class="nb">center</span><span class="p">;</span> <span class="p">}</span>
  <span class="nt">&lt;/style&gt;</span>
  <span class="nt">&lt;link</span> <span class="na">rel=</span><span class="s">"stylesheet"</span> <span class="na">href=</span><span class="s">"/css/site.css"</span> <span class="na">media=</span><span class="s">"print"</span> <span class="na">onload=</span><span class="s">"this.media='all'"</span><span class="nt">&gt;</span>
<span class="nt">&lt;/head&gt;</span>
</code></pre></div></div>

<p>The inline block only covers what the visitor sees first. Everything else can wait a few milliseconds.</p>

<h2 id="use-one-web-font-and-only-the-weights-you-need">Use one web font, and only the weights you need</h2>

<p>Web fonts are a common cause of slow text. Each family and weight is another request, and the text can stay invisible while the file loads. I use one family, usually a variable font, in two weights at most. I preload the file that holds the body text, and I set <code class="language-plaintext highlighter-rouge">font-display: swap</code> so visitors can read something straight away.</p>

<p>I also check the fallback font. If its sizes are close to the web font’s, the text barely moves when the real font arrives. That keeps the layout stable, which matters for the score as much as the speed does.</p>

<h2 id="images-as-webp-with-srcset-and-fixed-sizes">Images as WebP, with srcset and fixed sizes</h2>

<p>Most of the page weight on a portfolio or a shop is images. I export everything as WebP at a few widths, then let the browser pick the one that fits the screen. A phone should never download the desktop hero. I give every image a width and height, so the browser can reserve the space before the file arrives. Lazy loading goes on anything below the fold, and never on the hero image, because that one is needed straight away.</p>

<h2 id="hold-back-third-party-scripts-until-someone-asks">Hold back third-party scripts until someone asks</h2>

<p>Chat widgets, video embeds, social feeds and tag managers are where most scores go to die. They load a lot of code, often before the visitor has done anything at all. My default is a plain placeholder. For a chat widget, that means a button that says “Chat with us”. The real script loads when the visitor clicks, so the first paint is not held up by code they may never use.</p>

<p>Analytics is the one thing I keep running from the start. I use a small, privacy-friendly script, load it after the page has finished, and check its cost in the trace.</p>

<h2 id="measure-on-a-phone-not-only-on-my-laptop">Measure on a phone, not only on my laptop</h2>

<p>I run PageSpeed on the mobile test, because it is the harder one. My laptop is quick, so local scores can look fine while the real visitor on a cheap phone waits. Where the client already has traffic, I also check the field data. I change one thing at a time, so I know which change actually helped.</p>

<p>A hundred is a nice number, but the real goal is a page that opens fast for the person who needs it. The score is just the proof that the basics are in place.</p>]]></content><author><name></name></author><category term="Performance" /><category term="Front-end" /><summary type="html"><![CDATA[How I reach a 100 PageSpeed score on client sites with inline critical CSS, one web font, responsive WebP and deferred scripts.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://izar.tortoizthemes.com/assets/img/og.png" /><media:content medium="image" url="https://izar.tortoizthemes.com/assets/img/og.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Designing in the browser</title><link href="https://izar.tortoizthemes.com/notes/design-in-the-browser/" rel="alternate" type="text/html" title="Designing in the browser" /><published>2026-09-17T09:00:00+00:00</published><updated>2026-09-17T09:00:00+00:00</updated><id>https://izar.tortoizthemes.com/notes/design-in-the-browser</id><content type="html" xml:base="https://izar.tortoizthemes.com/notes/design-in-the-browser/"><![CDATA[<p>A few years ago I designed every project in a design tool. Each screen was a frame, each frame was a picture, and the picture never quite matched what the browser showed later. Now most of my design happens in the browser, in the same code that ships. Here is why I changed, and when I still start with a pencil.</p>

<h2 id="why-the-browser-won">Why the browser won</h2>

<p>A design file is a promise. It shows a button at one size, in one frame, with a heading that always fits on one line. The browser does not work like that. Text wraps, images change shape, and the menu breaks at 380 pixels. Finding those problems after the design is approved costs time and trust.</p>

<p>When I design in the browser, those problems show up on day one. I can see how a heading wraps at tablet width and fix it before the client sees anything. The design is the thing that gets built, so there is no handover where details get lost on the way.</p>

<h2 id="what-i-gain-and-what-i-lose">What I gain, and what I lose</h2>

<p>The gains are real. I work with real content, not lorem ipsum. I test spacing with the actual fonts and colours, in light and dark mode. Change a colour token and every page updates at once. The client can open a preview link on their phone and comment on the real thing, not on a picture of it.</p>

<p>I do lose some things. Exploring is slower in code than on a canvas. Nobody wants to write twelve versions of a hero section just to see which one feels right. I also need to be comfortable with a bit of mess, because a browser draft looks rough until the last hour.</p>

<blockquote>
  <p>A design file shows you one picture. A browser shows you every size a visitor might use.</p>
</blockquote>

<h2 id="when-i-still-sketch-first">When I still sketch first</h2>

<p>I still sketch first in three cases. The first is a brand-new concept, when the client does not yet know what they want. A few rough boards on paper keep the conversation about ideas rather than pixels. The second is a complex flow with many steps, such as a booking process. Drawing the path helps me see where people might get stuck before I write any markup. The third is a project where the client needs to approve a direction before any code exists.</p>

<p>Even then, the sketching is short. I spend an hour or two on paper, then move into the browser. I keep the sketches for reference, but the browser decides the final look.</p>

<h2 id="a-simple-way-to-start">A simple way to start</h2>

<p>My first session on a new project is always the same. I write the colour tokens, the type scale and one page filled with real content. Then I resize the window until something breaks, and fix it. By the end of the first day I have a rough site I can show. It is not pretty yet, but it is honest about how the design will behave on the screens people actually use.</p>]]></content><author><name></name></author><category term="Design" /><category term="Process" /><summary type="html"><![CDATA[Why I design in the browser instead of a design tool, and the few cases where I still sketch on paper first.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://izar.tortoizthemes.com/assets/img/og.png" /><media:content medium="image" url="https://izar.tortoizthemes.com/assets/img/og.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">What 165 projects taught me</title><link href="https://izar.tortoizthemes.com/notes/what-165-projects-taught-me/" rel="alternate" type="text/html" title="What 165 projects taught me" /><published>2026-08-30T09:00:00+00:00</published><updated>2026-08-30T09:00:00+00:00</updated><id>https://izar.tortoizthemes.com/notes/what-165-projects-taught-me</id><content type="html" xml:base="https://izar.tortoizthemes.com/notes/what-165-projects-taught-me/"><![CDATA[<p>I have now finished 165 freelance projects, from a single landing page for a local florist to a full site with a shop and a booking system. Most of what I know about running the work came from the ones that went badly. Here are the seven lessons I would give anyone starting out.</p>

<h2 id="the-seven-lessons">The seven lessons</h2>

<ol>
  <li>
    <p><strong>Write the scope down before you quote.</strong> A short list of pages, features and content owners saves more arguments than any contract clause. If a feature is not on the list, it is extra, and it gets its own price.</p>
  </li>
  <li>
    <p><strong>Ask who writes the words.</strong> Most delays I have seen were not in the code. They were waiting for copy. I ask for the text, or for a named person who will write it, before I agree on a launch date.</p>
  </li>
  <li>
    <p><strong>Charge for the extra rounds of feedback.</strong> One round of changes is part of the job, and two is fine on a small project. After that, every round eats into the margin. I say so early and put it in writing.</p>
  </li>
  <li>
    <p><strong>Build the boring parts first.</strong> Forms, contact details and the mobile menu are the things clients test first. I make sure they work with real data in week one, not the night before launch.</p>
  </li>
  <li>
    <p><strong>Keep a note of every decision.</strong> Clients forget what they agreed to, and sometimes so do I. A short email after each call, with the date, saves a lot of trouble at the end.</p>
  </li>
  <li>
    <p><strong>Hand over with a short guide.</strong> Two pages on how to edit the content, where the files live and who to call about hosting. Clients who can edit their own site send fewer emails.</p>
  </li>
  <li>
    <p><strong>Say no to the wrong clients early.</strong> Some enquiries are not a fit. The budget is unclear, nobody can make decisions, or the deadline cannot work. A polite no in the first week is kinder to both sides than a stressful project that nobody enjoys.</p>
  </li>
</ol>

<h2 id="what-changed-for-me">What changed for me</h2>

<p>These lessons did not come from reading books. They came from small failures that were obvious in hindsight. Once I started writing scope notes and asking about copy up front, my projects ran to time more often. My late nights dropped, and so did the tension with clients.</p>

<h2 id="what-i-would-tell-myself-at-the-start">What I would tell myself at the start</h2>

<p>Early on I thought my job was to make the site look good. Most of the work is really about making decisions visible. When everyone agrees on what is being built, who is doing what and when it will be finished, the design almost takes care of itself. I wish somebody had told me that earlier. It would have saved me a lot of evenings spent fixing problems I could have seen coming.</p>]]></content><author><name></name></author><category term="Freelancing" /><category term="Process" /><summary type="html"><![CDATA[Seven lessons from 165 freelance front-end projects on scope, copy, feedback, handover and saying no to the wrong clients.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://izar.tortoizthemes.com/assets/img/og.png" /><media:content medium="image" url="https://izar.tortoizthemes.com/assets/img/og.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry></feed>