<?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: Tom Herni</title>
    <description>The latest articles on DEV Community by Tom Herni (@tomherni).</description>
    <link>https://dev.to/tomherni</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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F331954%2F55d46223-f940-43cd-90d5-1fb954f4df15.png</url>
      <title>DEV Community: Tom Herni</title>
      <link>https://dev.to/tomherni</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/tomherni"/>
    <language>en</language>
    <item>
      <title>Curly Quotes with QuoteQuote</title>
      <dc:creator>Tom Herni</dc:creator>
      <pubDate>Thu, 17 Apr 2025 15:07:06 +0000</pubDate>
      <link>https://dev.to/tomherni/curly-quotes-with-quotequote-cf1</link>
      <guid>https://dev.to/tomherni/curly-quotes-with-quotequote-cf1</guid>
      <description>&lt;p&gt;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 &lt;em&gt;smart quotes&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;QuoteQuote is available on &lt;a href="https://github.com/tomherni/quote-quote" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; and &lt;a href="https://www.npmjs.com/package/quote-quote" rel="noopener noreferrer"&gt;npm&lt;/a&gt;. Download it using your favorite package manager, such as npm, pnpm, or yarn.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;quote-quote
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I've also launched a &lt;a href="https://quote-quote.tomherni.dev/" rel="noopener noreferrer"&gt;website&lt;/a&gt; where you can convert text online.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lightweight and modern
&lt;/h2&gt;

&lt;p&gt;The library is lightweight, modern, tree-shakeable, and supports both ESM and CJS. At the time of writing, it only exports &lt;code&gt;convert()&lt;/code&gt;, which does the most important thing: convert straight quotes to curly quotes.&lt;/p&gt;

&lt;p&gt;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).&lt;/p&gt;

&lt;h2&gt;
  
  
  Why QuoteQuote is necessary
&lt;/h2&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;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, &lt;code&gt;Alt + 0147&lt;/code&gt;    on Windows and &lt;code&gt;Option + [&lt;/code&gt; on macOS for just one specific quotation mark).&lt;/p&gt;

&lt;h2&gt;
  
  
  Why curly quotes
&lt;/h2&gt;

&lt;p&gt;"Curly quotes are the quotation marks used in good typography." says Matthew Butterick in &lt;a href="https://practicaltypography.com/straight-and-curly-quotes.html" rel="noopener noreferrer"&gt;Practical Typography&lt;/a&gt;—and I must say, I agree. Curly quotes are more legible and flow better with the content. They are a &lt;em&gt;must&lt;/em&gt; for blog posts, articles, and professional writing in general.&lt;/p&gt;




&lt;p&gt;Give QuoteQuote a try and let me know what you think! I welcome all feedback, as well as issues and pull requests on &lt;a href="https://github.com/tomherni/quote-quote" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;. And if you'd like to support the library, feel free to star the repository.&lt;/p&gt;

</description>
      <category>quotes</category>
      <category>curly</category>
      <category>smart</category>
      <category>typography</category>
    </item>
    <item>
      <title>The Specificity Of ::slotted()</title>
      <dc:creator>Tom Herni</dc:creator>
      <pubDate>Thu, 08 Feb 2024 15:25:48 +0000</pubDate>
      <link>https://dev.to/tomherni/the-specificity-of-slotted-16d9</link>
      <guid>https://dev.to/tomherni/the-specificity-of-slotted-16d9</guid>
      <description>&lt;p&gt;The &lt;code&gt;::slotted()&lt;/code&gt; 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 &lt;code&gt;::slotted()&lt;/code&gt; lose to global styles.&lt;/p&gt;

&lt;p&gt;Imagine a website with the following markup:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;style&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;p&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="no"&gt;blue&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="c"&gt;/* (0, 0, 1) */&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/style&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;my-element&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;Hello world&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/my-element&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Where &lt;code&gt;&amp;lt;my-element&amp;gt;&lt;/code&gt; is a web component that adds the following styles:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nd"&gt;:host&lt;/span&gt; &lt;span class="nd"&gt;::slotted&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="c"&gt;/* (0, 1, 2) */&lt;/span&gt;
  &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;red&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;The website's global styles will win, and the text will be &lt;strong&gt;blue&lt;/strong&gt;. Even though the specificity of &lt;code&gt;:host ::slotted(p)&lt;/code&gt; (0, 1, 2) is higher than &lt;code&gt;p&lt;/code&gt; (0, 0, 1).&lt;/p&gt;

&lt;p&gt;The web component cannot set any CSS properties on a slotted element that are already set by global styles.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;And in case you were wondering, increasing the argument's specificity of &lt;code&gt;::slotted()&lt;/code&gt; will also not help you win from global styles.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="c"&gt;/* Still loses to global styles */&lt;/span&gt;
&lt;span class="nd"&gt;::slotted&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="nf"&gt;#foo&lt;/span&gt;&lt;span class="o"&gt;)&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="no"&gt;red&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;h2&gt;
  
  
  Encapsulation contexts
&lt;/h2&gt;

&lt;p&gt;When explaining how declarations are sorted by the cascade, the CSS spec says the following about &lt;a href="https://www.w3.org/TR/css-cascade-5/#cascade-context"&gt;cascade contexts&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"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."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This essentially means that, without &lt;code&gt;!important&lt;/code&gt;, a web component's &lt;code&gt;::slotted()&lt;/code&gt; rules are overridden by the outer context (global styles). Specificity pretty much goes out the window.&lt;/p&gt;

&lt;p&gt;The next part is also worth mentioning:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"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."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Using &lt;code&gt;!important&lt;/code&gt; seems to be the official way to enforce slotted styles.&lt;/p&gt;

&lt;h2&gt;
  
  
  Making slotted rules important
&lt;/h2&gt;

&lt;p&gt;As we now know, the only way to make &lt;code&gt;::slotted()&lt;/code&gt; 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 &lt;code&gt;!important&lt;/code&gt; is justified.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="c"&gt;/* Wins from global styles */&lt;/span&gt;
&lt;span class="nd"&gt;::slotted&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="o"&gt;)&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="no"&gt;red&lt;/span&gt; &lt;span class="cp"&gt;!important&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;However, consumers may not appreciate their slotted elements being styled with &lt;code&gt;!important&lt;/code&gt;. Slotted elements are &lt;em&gt;their&lt;/em&gt; elements in &lt;em&gt;their&lt;/em&gt; DOM. And if they want to set a property that is already set with &lt;code&gt;!important&lt;/code&gt;, then they now need to do the same to win the specificity battle.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this behavior is tricky
&lt;/h2&gt;

&lt;p&gt;It's likely that most developers would not anticipate this behavior. They would likely assume that styles set with &lt;code&gt;::slotted()&lt;/code&gt; compete with the specificity of global styles.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;The purpose of &lt;code&gt;::slotted()&lt;/code&gt; 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 &lt;em&gt;too&lt;/em&gt; easily (i.e. unintentionally).&lt;/p&gt;

&lt;h2&gt;
  
  
  How to proceed
&lt;/h2&gt;

&lt;p&gt;Slotted styles without &lt;code&gt;!important&lt;/code&gt; 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:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Set important slotted styles with &lt;code&gt;!important&lt;/code&gt; to enforce them. Document the reason behind this decision and what consumers would have to do to override those styles if necessary.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Steer clear of &lt;code&gt;::slotted()&lt;/code&gt; 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.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Choose an approach that aligns with your philosophy. Make a conscious decision and be consistent.&lt;/p&gt;




&lt;p&gt;It may be worth mentioning that the specificity of &lt;code&gt;::slotted()&lt;/code&gt; 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.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="c"&gt;/* Wins from the selector below (but
   still loses to global styles) */&lt;/span&gt;
&lt;span class="nd"&gt;:host&lt;/span&gt; &lt;span class="nd"&gt;::slotted&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="o"&gt;)&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="no"&gt;red&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;::slotted&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="o"&gt;)&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="no"&gt;green&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;



</description>
      <category>css</category>
      <category>webcomponents</category>
    </item>
  </channel>
</rss>
