<?xml version="1.0" encoding="utf-8"?>
    <rss
      version="2.0"
      xmlns:dc="http://purl.org/dc/elements/1.1/"
      xmlns:content="http://purl.org/rss/1.0/modules/content/"
      xmlns:atom="http://www.w3.org/2005/Atom"
    >
      <channel>
        <title>Posts on tomherni.dev</title>
        <link>https://deploy-preview-26--tomherni.netlify.app</link>
        <atom:link href="https://deploy-preview-26--tomherni.netlify.app/rss.xml" rel="self" type="application/rss+xml" />
        <description>Blog posts written by Tom Herni</description>
        <language>en-US</language>
        <category>Front-end</category>
        <category>Development</category>
        <category>Web</category>
        <lastBuildDate>Sun, 14 Jun 2026 17:30:31 GMT</lastBuildDate>
         
            <item>
              <title>Curly Quotes with QuoteQuote</title>
              <link>https://deploy-preview-26--tomherni.netlify.app/blog/curly-quotes-with-quote-quote/</link>
              <guid>https://deploy-preview-26--tomherni.netlify.app/blog/curly-quotes-with-quote-quote/</guid>
              <pubDate>Thu, 17 Apr 2025 10:16:53 GMT</pubDate>
              <description>I’m pleased to announce the first stable release of QuoteQuote—a library that converts boring, straight quotes to beautiful, typographically correct curly quotes, also known as smart quotes.></description>
              <content:encoded><![CDATA[<p>I’m pleased to announce the first stable release of QuoteQuote—a library that converts boring, straight quotes to beautiful, typographically correct curly quotes, also known as <em>smart quotes</em>.</p>
<p>QuoteQuote is available on <a target="_blank" rel="noopener noreferrer" href="https://github.com/tomherni/quote-quote">GitHub</a> and <a target="_blank" rel="noopener noreferrer" href="https://www.npmjs.com/package/quote-quote">npm</a>. Download it using your favorite package manager, such as npm, pnpm, or yarn.</p>
<pre><code class="language-sh"><span class="token function">npm</span> <span class="token function">install</span> quote-quote</code></pre><p>I’ve also launched a <a target="_blank" rel="noopener noreferrer" href="https://quote-quote.tomherni.dev/">website</a> where you can convert text online.</p>
<h2 id="lightweight-and-modern">Lightweight and modern<a href="#lightweight-and-modern" class="anchor">#</a></h2>
<p>The library is lightweight, modern, tree-shakeable, and supports both ESM and CJS. At the time of writing, it only exports <code>convert()</code>, which does the most important thing: convert straight quotes to curly quotes.</p>
<p>I have plans for additional features, such as support for Markdown and the DOM. Development for these features will begin soon and will be added to the library in accordance with SemVer (Semantic Versioning).</p>
<h2 id="why-quotequote-is-necessary">Why QuoteQuote is necessary<a href="#why-quotequote-is-necessary" class="anchor">#</a></h2>
<p>There are similar libraries out there, but they all have different issues. Some haven’t been updated in years and have open bugs that are fixable. Others don’t support ESM or CJS, lack TypeScript support, or are simply outdated. Issues and pull requests are mostly met with silence.</p>
<p>Also, using curly quotes manually is just too difficult. Keyboards typically offer straight quotes, and only allow curly quotes if you happen to know the right key combinations (for example, <code>Alt + 0147</code>	on Windows and <code>Option + [</code> on macOS for just one specific quotation mark).</p>
<h2 id="why-curly-quotes">Why curly quotes<a href="#why-curly-quotes" class="anchor">#</a></h2>
<p>“Curly quotes are the quotation marks used in good typography.” says Matthew Butterick in <a target="_blank" rel="noopener noreferrer" href="https://practicaltypography.com/straight-and-curly-quotes.html">Practical Typography</a>—and I must say, I agree. Curly quotes are more legible and flow better with the content. They are a <em>must</em> for blog posts, articles, and professional writing in general.</p>
<hr>
<p>Give QuoteQuote a try and let me know what you think! I welcome all feedback, as well as issues and pull requests on <a target="_blank" rel="noopener noreferrer" href="https://github.com/tomherni/quote-quote">GitHub</a>. And if you’d like to support the library, feel free to star the repository.</p>
]]></content:encoded>
              <author>tomherni@gmail.com (Tom Herni)</author>
              <enclosure url="https://deploy-preview-26--tomherni.netlify.app/blog/curly-quotes-with-quote-quote/social.jpg" length="0" type="image/jpg"/>
            </item>
           
            <item>
              <title>The Specificity Of ::slotted()</title>
              <link>https://deploy-preview-26--tomherni.netlify.app/blog/the-specificity-of-slotted/</link>
              <guid>https://deploy-preview-26--tomherni.netlify.app/blog/the-specificity-of-slotted/</guid>
              <pubDate>Thu, 08 Feb 2024 15:34:00 GMT</pubDate>
              <description>The ::slotted() pseudo-element allows you to style elements that are slotted into your web component. But, there is something that may catch you off guard: styles applied with ::slotted() lose to global styles.></description>
              <content:encoded><![CDATA[<p>The <code>::slotted()</code> pseudo-element allows you to style elements that are slotted into your web component. But, there is something that may catch you off guard: styles applied with <code>::slotted()</code> lose to global styles.</p>
<p>Imagine a website with the following markup:</p>
<pre><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>style</span><span class="token punctuation">></span></span><span class="token style"><span class="token language-css">
  <span class="token selector">p</span> <span class="token punctuation">{</span> <span class="token property">color</span><span class="token punctuation">:</span> blue <span class="token punctuation">}</span> <span class="token comment">/* (0, 0, 1) */</span>
</span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>style</span><span class="token punctuation">></span></span>

<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>my-element</span><span class="token punctuation">></span></span>
  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span><span class="token punctuation">></span></span>Hello world<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>my-element</span><span class="token punctuation">></span></span></code></pre><p>Where <code>&lt;my-element&gt;</code> is a web component that adds the following styles:</p>
<pre><code class="language-css"><span class="token selector">:host ::slotted(p)</span> <span class="token punctuation">{</span> <span class="token comment">/* (0, 1, 2) */</span>
  <span class="token property">color</span><span class="token punctuation">:</span> red<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre><p>The website’s global styles will win, and the text will be <strong>blue</strong>. Even though the specificity of <code>:host ::slotted(p)</code> (0, 1, 2) is higher than <code>p</code> (0, 0, 1).</p>
<p>The web component cannot set any CSS properties on a slotted element that are already set by global styles.</p>
<p>This becomes an even bigger issue when global styles include a “reset” stylesheet (like Normalize.css). Reset stylesheets touch the styling of many HTML elements, making it even less likely for slotted styles to be applied.</p>
<p>And in case you were wondering, increasing the argument’s specificity of <code>::slotted()</code> will also not help you win from global styles.</p>
<pre><code class="language-css"><span class="token comment">/* Still loses to global styles */</span>
<span class="token selector">::slotted(p#foo)</span> <span class="token punctuation">{</span>
  <span class="token property">color</span><span class="token punctuation">:</span> red<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre><h2 id="encapsulation-contexts">Encapsulation contexts<a href="#encapsulation-contexts" class="anchor">#</a></h2>
<p>When explaining how declarations are sorted by the cascade, the CSS spec says the following about <a target="_blank" rel="noopener noreferrer" href="https://www.w3.org/TR/css-cascade-5/#cascade-context">cascade contexts</a>:</p>
<blockquote>
<p>“When comparing two declarations that are sourced from different encapsulation contexts, then for normal rules the declaration from the outer context wins, and for important rules the declaration from the inner context wins.”</p>
</blockquote>
<p>This essentially means that, without <code>!important</code>, a web component’s <code>::slotted()</code> rules are overridden by the outer context (global styles). Specificity pretty much goes out the window.</p>
<p>The next part is also worth mentioning:</p>
<blockquote>
<p>“This effectively means that normal declarations belonging to an encapsulation context can set defaults that are easily overridden by the outer context, while important declarations belonging to an encapsulation context can enforce requirements that cannot be overridden by the outer context.”</p>
</blockquote>
<p>Using <code>!important</code> seems to be the official way to enforce slotted styles.</p>
<h2 id="making-slotted-rules-important">Making slotted rules important<a href="#making-slotted-rules-important" class="anchor">#</a></h2>
<p>As we now know, the only way to make <code>::slotted()</code> win is to make rules important. It’s not pretty, but considering it’s the only way, I suppose this is a case where using <code>!important</code> is justified.</p>
<pre><code class="language-css"><span class="token comment">/* Wins from global styles */</span>
<span class="token selector">::slotted(p)</span> <span class="token punctuation">{</span>
  <span class="token property">color</span><span class="token punctuation">:</span> red <span class="token important">!important</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre><p>However, consumers may not appreciate their slotted elements being styled with <code>!important</code>. Slotted elements are <em>their</em> elements in <em>their</em> DOM. And if they want to set a property that is already set with <code>!important</code>, then they now need to do the same to win the specificity battle.</p>
<h2 id="why-this-behavior-is-tricky">Why this behavior is tricky<a href="#why-this-behavior-is-tricky" class="anchor">#</a></h2>
<p>It’s likely that most developers would not anticipate this behavior. They would likely assume that styles set with <code>::slotted()</code> compete with the specificity of global styles.</p>
<p>Additionally, when a web component is developed and tested in an environment without (conflicting) global styles, then this issue is easy to miss before it makes its way to production.</p>
<p>The purpose of <code>::slotted()</code> is to set default styles that can be easily overridden. But consumers can have reset stylesheets, or import stylesheets over which they have no control (particularly in larger corporate environments). In those cases, slotted styles are overridden <em>too</em> easily (i.e. unintentionally).</p>
<h2 id="how-to-proceed">How to proceed<a href="#how-to-proceed" class="anchor">#</a></h2>
<p>Slotted styles without <code>!important</code> may not end up being applied as expected. If this is an issue for your web component, then it’s time to make a decision:</p>
<ol>
<li><p>Set important slotted styles with <code>!important</code> to enforce them. Document the reason behind this decision and what consumers would have to do to override those styles if necessary.</p>
</li>
<li><p>Steer clear of <code>::slotted()</code> altogether. Instead, document which styles consumers are recommended to set when slotting elements. Argument could be made that consumers should remain solely responsible for styling their elements.</p>
</li>
</ol>
<p>Choose an approach that aligns with your philosophy. Make a conscious decision and be consistent.</p>
<hr>
<p>It may be worth mentioning that the specificity of <code>::slotted()</code> still works as expected within the same encapsulation context. It helps determine which declarations are applied by the web component, even if they were to eventually be overridden by global styles.</p>
<pre><code class="language-css"><span class="token comment">/* Wins from the selector below (but
   still loses to global styles) */</span>
<span class="token selector">:host ::slotted(p)</span> <span class="token punctuation">{</span>
  <span class="token property">color</span><span class="token punctuation">:</span> red<span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token selector">::slotted(p)</span> <span class="token punctuation">{</span>
  <span class="token property">color</span><span class="token punctuation">:</span> green<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>]]></content:encoded>
              <author>tomherni@gmail.com (Tom Herni)</author>
              <enclosure url="https://deploy-preview-26--tomherni.netlify.app/blog/the-specificity-of-slotted/social.jpg" length="0" type="image/jpg"/>
            </item>
           
            <item>
              <title>And So It Begins</title>
              <link>https://deploy-preview-26--tomherni.netlify.app/blog/and-so-it-begins/</link>
              <guid>https://deploy-preview-26--tomherni.netlify.app/blog/and-so-it-begins/</guid>
              <pubDate>Wed, 03 Jan 2024 17:00:00 GMT</pubDate>
              <description>I was thinking about launching a blog for a while. Initially, my aim was to get up and running with minimal effort. But the more I thought about it, the clearer it became—I didn’t just want to have a blog, I also just really wanted to build one.></description>
              <content:encoded><![CDATA[<p>I was thinking about launching a blog for a while. Initially, my aim was to get up and running with minimal effort. But the more I thought about it, the clearer it became—I didn’t just want to <em>have</em> a blog, I also just really wanted to <em>build</em> one.</p>
<p>And so, that’s what I did. Because what else are you supposed to do on Saturdays, right? Anyway… Just like that, the “minimal effort” plan turned into “let’s build it all from scratch.”</p>
<p>What excited me most about building my blog was the idea of having full control over every aspect of it, from the design to the technical implementation. There are still things I want to add and improve, but I’m happy enough with the result so far to put it online. Also, at some point, you just have to stop tinkering and start writing.</p>
<p>The primary focus of my blog will be on web development, design, and other technical things that interest me. I have several ideas for posts I want to write first. One of them is about how I built my blog, focusing on the design and technical decisions.</p>
<p>I’d like to conclude my first post with a brief personal introduction. My name is Tom, and I’m a front-end engineer passionate about designing and building creative solutions for the web. I’ve been into design and web development since I was a kid, and I’m fortunate enough to have turned my hobby into my career. I live in The Netherlands with my girlfriend, Stephanie, and we enjoy traveling, movies, and an occasional (and slightly competitive) game of Rummikub.</p>
]]></content:encoded>
              <author>tomherni@gmail.com (Tom Herni)</author>
              <enclosure url="https://deploy-preview-26--tomherni.netlify.app/blog/and-so-it-begins/social.jpg" length="0" type="image/jpg"/>
            </item>
          
      </channel>
    </rss>
  