<?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: Iris Khan</title>
    <description>The latest articles on DEV Community by Iris Khan (@iris_khan_dev).</description>
    <link>https://dev.to/iris_khan_dev</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%2F4053248%2Fff92389d-f06c-4238-9b58-a1f2c2714cdf.jpg</url>
      <title>DEV Community: Iris Khan</title>
      <link>https://dev.to/iris_khan_dev</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/iris_khan_dev"/>
    <language>en</language>
    <item>
      <title>Copy-Paste Fonts Are Not Fonts. Here Is What Broke My Code</title>
      <dc:creator>Iris Khan</dc:creator>
      <pubDate>Wed, 02 Sep 2026 17:27:28 +0000</pubDate>
      <link>https://dev.to/iris_khan_dev/copy-paste-fonts-are-not-fonts-here-is-what-broke-my-code-4kc9</link>
      <guid>https://dev.to/iris_khan_dev/copy-paste-fonts-are-not-fonts-here-is-what-broke-my-code-4kc9</guid>
      <description>&lt;p&gt;This is part 4 of my beginner journey.&lt;/p&gt;

&lt;p&gt;In &lt;a href="https://dev.to/iris_khan_dev/i-have-no-idea-what-to-learn-first-and-heres-how-im-deciding-4f4a"&gt;part 1&lt;/a&gt; I did not know what to learn first. In &lt;a href="https://dev.to/iris_khan_dev/from-overthinking-to-actually-building-something-3ndc"&gt;part 2&lt;/a&gt; I stopped overthinking and picked a project: Fonti, a free copy-paste font generator. In &lt;a href="https://dev.to/iris_khan_dev/i-decided-to-build-it-then-i-had-to-actually-decide-how-86i"&gt;part 3&lt;/a&gt; I chose my tools (Astro, Git, Cloudflare Pages) and promised the next post would be about the actual build.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;NOTE: I renamed the project from Fontly to Fonti since part 3. Same tool, better name.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is that post. My first real code of this series.&lt;/p&gt;

&lt;p&gt;And it starts with me being completely wrong about how these tools work.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I thought I was building
&lt;/h2&gt;

&lt;p&gt;My idea was simple. The user types text. I apply a font. They copy it and paste it into their Instagram bio.&lt;/p&gt;

&lt;p&gt;I spent a few days confused about one part of that plan. How does a font survive a copy-paste? If I load a font on my website, that font lives on my website. Instagram does not have it. So how does the style travel?&lt;/p&gt;

&lt;p&gt;The answer is that it does not. There is no font. There never was.&lt;/p&gt;

&lt;h2&gt;
  
  
  The real trick
&lt;/h2&gt;

&lt;p&gt;When you paste 𝐛𝐨𝐥𝐝 text somewhere, you are not sending style information. You are sending different characters that are shaped like bold letters.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;b&lt;/code&gt; in that bold word is not the letter &lt;code&gt;b&lt;/code&gt;. It is a math symbol that looks like a bold &lt;code&gt;b&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Unicode has a block called &lt;strong&gt;Mathematical Alphanumeric Symbols&lt;/strong&gt;. It lives at &lt;code&gt;U+1D400&lt;/code&gt; to &lt;code&gt;U+1D7FF&lt;/code&gt;. It has 1024 slots, 996 of them are used, and 28 are empty on purpose. It was added in Unicode 3.1 in the year 2001.&lt;/p&gt;

&lt;p&gt;Why does it exist? Because mathematicians need 𝐴 and 𝔸 and 𝒜 to mean three different things in the same paper. In math, the style is part of the meaning. So Unicode gave each style its own real character.&lt;/p&gt;

&lt;p&gt;Social media users then found these characters and started using them for stylish bios. That is the whole industry of "font generators".&lt;/p&gt;

&lt;p&gt;So my tool is not a font tool. It is a lookup table. That is it.&lt;/p&gt;

&lt;p&gt;This also connects back to something from part 3. I chose Astro because it sends very little JavaScript by default, and only sends it where the page really needs it. Now I know exactly what that JavaScript is: one small function that swaps characters. Everything else on the site can stay as plain HTML.&lt;/p&gt;

&lt;h2&gt;
  
  
  My first version
&lt;/h2&gt;

&lt;p&gt;If the styled letters are stored in alphabetical order, then converting text is just simple math.&lt;/p&gt;

&lt;p&gt;Normal &lt;code&gt;A&lt;/code&gt; is at &lt;code&gt;U+0041&lt;/code&gt;. Bold &lt;code&gt;A&lt;/code&gt; is at &lt;code&gt;U+1D400&lt;/code&gt;. So I take the letter's number, subtract the start of the normal alphabet, and add the start of the bold alphabet.&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;OFFSETS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;bold&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;         &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;upper&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;0x1D400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;0x1D41A&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;italic&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;       &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;upper&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;0x1D434&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;0x1D44E&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;       &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;upper&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;0x1D49C&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;0x1D4B6&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;fraktur&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;      &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;upper&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;0x1D504&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;0x1D51E&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;doubleStruck&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;upper&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;0x1D538&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;0x1D552&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;sansSerif&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;upper&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;0x1D5A0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;0x1D5BA&lt;/span&gt; &lt;span class="p"&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;convert&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;style&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="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;upper&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;lower&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;OFFSETS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;style&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="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;ch&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;code&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;charCodeAt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&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;ch&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;A&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;ch&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Z&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="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fromCodePoint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;upper&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;code&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;65&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;ch&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;a&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;ch&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;z&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="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fromCodePoint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;lower&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;code&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;97&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;ch&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&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;I was happy. It felt clean. No font files, no library, no dependency. Just math.&lt;/p&gt;

&lt;p&gt;Bold worked. Italic mostly worked. Sans-serif worked.&lt;/p&gt;

&lt;p&gt;Then I tested script style and got this:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;𝒜&lt;/code&gt; for A. Then an empty box for B.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why it broke
&lt;/h2&gt;

&lt;p&gt;The block has holes in it. And the holes are there on purpose.&lt;/p&gt;

&lt;p&gt;Before this big math block existed, Unicode had already added a few of these styled letters in a &lt;strong&gt;different&lt;/strong&gt; block called &lt;strong&gt;Letterlike Symbols&lt;/strong&gt; at &lt;code&gt;U+2100&lt;/code&gt; to &lt;code&gt;U+214F&lt;/code&gt;. They were added because people already used them as single math symbols. Things like ℝ for real numbers and ℋ for a Hilbert space.&lt;/p&gt;

&lt;p&gt;So when the big block was designed later, those letters were left out. Unicode did not want to encode the same character twice. The official Unicode chart says this directly about the double-struck letters: they are "omitted here to avoid duplicate encoding."&lt;/p&gt;

&lt;p&gt;The result for me is simple. My math formula was pointing at empty slots. Empty slot means empty box on screen.&lt;/p&gt;

&lt;p&gt;I had to build an exception list. Here is every hole that affects normal A to Z text, and where the real character actually lives:&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;EXCEPTIONS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;italic&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;h&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;u210E&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                                    &lt;span class="c1"&gt;// ℎ  (Planck constant)&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;B&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;u212C&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;E&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;u2130&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;F&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;u2131&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;H&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;u210B&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;I&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;u2110&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;L&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;u2112&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;M&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;u2133&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;R&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;u211B&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;u212F&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;g&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;u210A&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;o&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;u2134&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="na"&gt;fraktur&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;C&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;u212D&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;H&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;u210C&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;I&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;u2111&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;R&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;u211C&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;Z&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;u2128&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="na"&gt;doubleStruck&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;C&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;u2102&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;H&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;u210D&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;N&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;u2115&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;P&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;u2119&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;Q&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;u211A&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;R&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;u211D&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;Z&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;u2124&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is 24 exceptions in four styles.&lt;/p&gt;

&lt;p&gt;Now look at which styles are &lt;strong&gt;not&lt;/strong&gt; in that list. Bold, bold italic, bold script, bold fraktur, sans-serif and sans-serif bold are all fine. Their letters sit in one clean run with no gaps. Pure math works perfectly for them.&lt;/p&gt;

&lt;p&gt;Only the four styles that overlap with old math notation are broken. That makes sense once you know the history, and it is impossible to guess before you know it.&lt;/p&gt;

&lt;p&gt;My favourite one is italic. The whole italic alphabet is fine except lowercase &lt;code&gt;h&lt;/code&gt;. Just one letter. Why? Because ℎ at &lt;code&gt;U+210E&lt;/code&gt; was already there as the Planck constant symbol from physics.&lt;/p&gt;

&lt;p&gt;Here is the fixed function. The change is small. Check the exception list first, then fall back to math.&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;convert&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;style&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="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;upper&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;lower&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;OFFSETS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;style&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;gaps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;EXCEPTIONS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&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="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;ch&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;gaps&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;ch&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;gaps&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;ch&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;code&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;charCodeAt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&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;ch&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;A&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;ch&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Z&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="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fromCodePoint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;upper&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;code&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;65&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;ch&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;a&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;ch&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;z&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="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fromCodePoint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;lower&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;code&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;97&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;ch&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&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;h2&gt;
  
  
  One thing I still cannot explain
&lt;/h2&gt;

&lt;p&gt;Script lowercase &lt;code&gt;e&lt;/code&gt;, &lt;code&gt;g&lt;/code&gt; and &lt;code&gt;o&lt;/code&gt; were all skipped, because ℯ, ℊ and ℴ already existed in the older block.&lt;/p&gt;

&lt;p&gt;But script lowercase &lt;code&gt;l&lt;/code&gt; &lt;strong&gt;was&lt;/strong&gt; added to the new block at &lt;code&gt;U+1D4C1&lt;/code&gt;, even though ℓ already existed at &lt;code&gt;U+2113&lt;/code&gt;. Exactly the same situation, opposite decision.&lt;/p&gt;

&lt;p&gt;If someone knows the reason behind that, please tell me in the comments. I looked and I could not find it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The second thing that bit me
&lt;/h2&gt;

&lt;p&gt;These characters live above &lt;code&gt;U+FFFF&lt;/code&gt;. In JavaScript that means one visible character takes two slots inside the string.&lt;/p&gt;

&lt;p&gt;This is why my code uses &lt;code&gt;[...text]&lt;/code&gt; and not &lt;code&gt;text.split("")&lt;/code&gt;. If you use &lt;code&gt;split("")&lt;/code&gt;, it cuts these characters in half and you get broken output.&lt;/p&gt;

&lt;p&gt;Before you write any code here, open your browser console and run this:&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;𝐀&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Whatever number you see there will tell you why string length cannot be trusted in this project.&lt;/p&gt;

&lt;h2&gt;
  
  
  The honest part
&lt;/h2&gt;

&lt;p&gt;I have to say this, because I found it while researching and it changed how I think about my own project.&lt;/p&gt;

&lt;p&gt;Unicode tells people not to do what I am doing. The Unicode Standard, chapter 22, says: "Characters from the Mathematical Alphanumeric symbols block should not be used to represent styling of nonmathematical text."&lt;/p&gt;

&lt;p&gt;These characters were made to be different from normal letters on purpose. Using them as decoration is a misuse of the system.&lt;/p&gt;

&lt;p&gt;And there is a real cost. Spammers use this exact trick to get past keyword filters, because a filter looking for a word will not match the same word written in math symbols. People have documented it in spam emails and even inside URLs.&lt;/p&gt;

&lt;p&gt;So Fonti will ship with a warning next to the output. It will say clearly that this text is not real formatting, that it can break search and filters, and that it is a bad choice anywhere the text needs to be read by a machine.&lt;/p&gt;

&lt;p&gt;One more thing I do not know yet: how screen readers announce these characters. I am not going to guess. I will test it properly and write about what I actually find.&lt;/p&gt;

&lt;h2&gt;
  
  
  Looking back at part 1
&lt;/h2&gt;

&lt;p&gt;In part 1, I was stuck choosing between HTML, CSS, JavaScript, Python and React. I felt like I had to pick the right one before I could start.&lt;/p&gt;

&lt;p&gt;This whole feature is plain JavaScript. A map, a loop, and some numbers. No framework, no library, no React.&lt;/p&gt;

&lt;p&gt;The hard part was never the language. It was understanding the problem. I spent one hour writing the code and three days figuring out that fonts were not involved at all.&lt;/p&gt;

&lt;p&gt;That is the thing I wish someone had told me in part 1.&lt;/p&gt;

&lt;h2&gt;
  
  
  Two questions
&lt;/h2&gt;

&lt;p&gt;First: the script lowercase &lt;code&gt;l&lt;/code&gt; inconsistency above. Does anyone know the story?&lt;/p&gt;

&lt;p&gt;Second: are there other Unicode ranges with reserved holes like this, where offset math quietly produces invalid characters? Letterlike Symbols is the only overlap I found, but I doubt it is the only one that exists.&lt;/p&gt;

&lt;p&gt;Next post: building the actual UI and the copy button, which turned out to have its own surprise.&lt;/p&gt;




&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://unicode.org/charts/PDF/U1D400.pdf" rel="noopener noreferrer"&gt;Mathematical Alphanumeric Symbols code chart (PDF)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.unicode.org/charts/nameslist/n_1D400.html" rel="noopener noreferrer"&gt;Mathematical Alphanumeric Symbols names list&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.unicode.org/versions/Unicode16.0.0/core-spec/chapter-22/" rel="noopener noreferrer"&gt;Unicode Standard, Chapter 22&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.alanwood.net/unicode/letterlike_symbols.html" rel="noopener noreferrer"&gt;Letterlike Symbols character table&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>javascript</category>
      <category>unicode</category>
      <category>webdev</category>
      <category>beginners</category>
    </item>
    <item>
      <title>I Decided to Build It. Then I Had to Actually Decide How.</title>
      <dc:creator>Iris Khan</dc:creator>
      <pubDate>Sun, 30 Aug 2026 12:29:30 +0000</pubDate>
      <link>https://dev.to/iris_khan_dev/i-decided-to-build-it-then-i-had-to-actually-decide-how-86i</link>
      <guid>https://dev.to/iris_khan_dev/i-decided-to-build-it-then-i-had-to-actually-decide-how-86i</guid>
      <description>&lt;p&gt;Last time, I &lt;a href="https://dev.to/iris_khan_dev/from-overthinking-to-actually-building-something-3ndc"&gt;decided to stop overthinking and start building&lt;/a&gt; Fontly, a free copy-paste font generator. Before writing a single line of the actual tool, I had to make a decision I thought would be quick and ended up being the thing I spent the most time on: the tech stack.&lt;/p&gt;

&lt;p&gt;Here's how that thinking actually went.&lt;/p&gt;

&lt;h2&gt;
  
  
  Starting point: "just make it a static site"
&lt;/h2&gt;

&lt;p&gt;My first instinct was the simplest possible option: a plain static site. No framework, no build step, just HTML/CSS/JS. For a small tool like this, that felt like the obvious beginner-friendly choice, and honestly, most "how to build your first project" advice points exactly there.&lt;/p&gt;

&lt;p&gt;For a single page with one tool on it, that instinct isn't wrong. But the more I thought about where this project needed to go, the more that plan started to strain.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where plain static started to break down
&lt;/h2&gt;

&lt;p&gt;A few things I kept running into when I actually mapped out what Fontly needed, beyond just "the tool works":&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;This isn't going to stay one page:&lt;/strong&gt; To be findable at all, a font generator needs supporting content: explainer pages, FAQs, maybe category-specific pages down the line, and hand-writing and hand-linking a growing pile of HTML files gets messy fast, with no structure keeping it consistent.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SEO isn't just meta tags:&lt;/strong&gt; Clean semantic markup, sitemaps, fast load times, and consistent page structure all matter, and doing all of that by hand, correctly, on every new page is exactly the kind of repetitive work that's easy to get subtly wrong as a beginner.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Content and code kept wanting to be separate:&lt;/strong&gt; The actual words on the page (headings, FAQ answers, descriptions) are things I want to be able to edit and expand without touching layout code every time.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That last point was the real turning point. I didn't need "a framework" for the sake of having one, I needed a way to manage &lt;em&gt;content&lt;/em&gt; that wasn't tangled up with markup.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Astro specifically
&lt;/h2&gt;

&lt;p&gt;Once I framed it as a content-management problem rather than an app-building problem, Astro made sense for a few specific reasons:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Content collections:&lt;/strong&gt; Astro lets me define structured content (pages, FAQ entries, future blog posts) separately from how they're rendered, so adding a new page later means adding content, not rewriting HTML.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ships minimal JavaScript by default:&lt;/strong&gt; Unlike a full app framework, Astro renders to static HTML and only sends JavaScript for the parts that actually need interactivity, like the font-conversion tool itself. That matters for both load speed and SEO.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SEO fundamentals are built in&lt;/strong&gt;, not bolted on: sitemap generation, clean output, and a structure that doesn't fight against good practices by default.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It's still simple underneath:&lt;/strong&gt; The interactive logic is still plain TypeScript/JavaScript, Astro isn't replacing that, it's just handling the structure and content around it in a way that scales better than hand-written HTML would have.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The honest tradeoff
&lt;/h2&gt;

&lt;p&gt;This wasn't a free upgrade. Astro means a build step, a bit of a learning curve, and more moving pieces than plain HTML/CSS/JS, genuinely more than I needed for day one. I went with it anyway because I was planning for where the project needed to go (more content, better SEO structure, room to grow) rather than just what got something on screen fastest.&lt;/p&gt;

&lt;h2&gt;
  
  
  Version control: Git and GitHub, before there was much to version
&lt;/h2&gt;

&lt;p&gt;I set up Git and pushed to GitHub before I'd really written anything worth backing up. That felt slightly premature at the time, but a few reasons made it worth doing early instead of "later, once there's real code":&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Cursor makes mistakes, and I make more:&lt;/strong&gt; Since I'm leaning heavily on an AI coding assistant while still learning, I wanted the ability to undo a bad change without panicking, so I could commit often and roll back if needed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It's the foundation everything else plugs into:&lt;/strong&gt; Automatic deployment (more on that below) needs a repo to deploy &lt;em&gt;from&lt;/em&gt;. Setting it up early meant that piece was just already there when I needed it, instead of being one more thing to configure later under pressure.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Habit-forming:&lt;/strong&gt; As a beginner, if I don't build the habit of committing early, I don't build it at all. Starting on day one made it the default instead of an afterthought.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Nothing fancy here, just a repo, sensible commits, and a &lt;code&gt;.gitignore&lt;/code&gt; so generated files and dependencies don't get tracked.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hosting: why Cloudflare Pages specifically
&lt;/h2&gt;

&lt;p&gt;For hosting, I wanted something that matched the "boring and reliable" philosophy of the rest of the stack. I picked &lt;strong&gt;Cloudflare Pages&lt;/strong&gt; for a few concrete reasons:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Git-connected deploys:&lt;/strong&gt; It builds and deploys straight from my GitHub repo: push to &lt;code&gt;main&lt;/code&gt;, and the live site updates automatically. No manual uploads, no separate deploy step to remember.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Built for static/content sites like this one:&lt;/strong&gt; Since Astro outputs mostly static files, Cloudflare Pages is a natural fit, it's designed around exactly that kind of output rather than needing a server running.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fast by default, globally:&lt;/strong&gt; Cloudflare's network means the site loads quickly regardless of where a visitor is, without me having to configure a CDN separately, which matters for both user experience and page-speed-related SEO signals.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Generous free tier:&lt;/strong&gt; For a project at this stage, I don't want infrastructure cost to be something I'm thinking about yet. Cloudflare Pages' free tier is enough that I can put that concern aside entirely for now.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The combination, GitHub for version control and Cloudflare Pages for deployment, means my workflow is just: write code, commit, push, and the live site updates on its own a minute later. No manual steps in between to forget or mess up.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's next
&lt;/h2&gt;

&lt;p&gt;With that decision made, the next posts get into the actual build, starting with hosting and workflow decisions, then the tool itself.&lt;/p&gt;

&lt;p&gt;Anyone else gone back and forth between "keep it simple" and "build it for where it's going"? Where do you usually land?&lt;/p&gt;

</description>
      <category>discuss</category>
      <category>beginners</category>
      <category>learning</category>
      <category>career</category>
    </item>
    <item>
      <title>From Overthinking to Actually Building Something</title>
      <dc:creator>Iris Khan</dc:creator>
      <pubDate>Fri, 28 Aug 2026 12:39:06 +0000</pubDate>
      <link>https://dev.to/iris_khan_dev/from-overthinking-to-actually-building-something-3ndc</link>
      <guid>https://dev.to/iris_khan_dev/from-overthinking-to-actually-building-something-3ndc</guid>
      <description>&lt;p&gt;I'm Iris, new to web development. A little while back, 2 days ago probably, I wrote about &lt;a href="https://dev.to/iris_khan_dev/i-have-no-idea-what-to-learn-first-and-heres-how-im-deciding-4f4a"&gt;not knowing what to learn first&lt;/a&gt;. The comments on that post were really encouraging, and they pushed me to stop overthinking it and just take action, start building something, even if it's not perfect, even if I don't know everything yet.&lt;/p&gt;

&lt;p&gt;That "something" is Fontly, a free copy-paste font generator. It's not some big, ambitious project. It's small, and I'm just getting started. But it's mine, and it's real progress. This post is an update on where that decision has taken me so far: what I've built, what I've learned, and where it's headed.&lt;/p&gt;

&lt;h2&gt;
  
  
  The idea
&lt;/h2&gt;

&lt;p&gt;If you've ever tried to make your Instagram bio, WhatsApp status, or a social post look a little different, you've probably searched for a "fancy text generator." Most of the ones out there are cluttered with ads, slow to load, or hide the good fonts behind a paywall.&lt;/p&gt;

&lt;p&gt;That's the gap I'm trying to fill with Fontly, a clean, fast, completely free tool where you type your text once and instantly get a bunch of stylish font variations you can copy and paste anywhere. No sign-up, no clutter, no nonsense.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where I'm at
&lt;/h2&gt;

&lt;p&gt;I'm still early in my web dev journey, so this project is as much a learning exercise as it is a product. Right now I'm:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Finalizing the site's core functionality&lt;/li&gt;
&lt;li&gt;Working through the basics of clean UI and page speed&lt;/li&gt;
&lt;li&gt;Learning how SEO and discoverability actually work before launch, instead of scrambling after&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Doing the planning work up front, thinking about structure, naming, and how people will actually find the tool, has honestly taught me more than the coding itself.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's been challenging
&lt;/h2&gt;

&lt;p&gt;A few things I didn't expect to spend so much time on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Naming and branding: landing on "Fontly" took longer than I thought, and I wanted a name that would still make sense if I build more tools later&lt;/li&gt;
&lt;li&gt;Thinking about SEO pre-launch: most guides assume your site is already live; figuring out what to do before that was a bit of trial and error&lt;/li&gt;
&lt;li&gt;Balancing "keep it simple" with "make it actually good": as a beginner it's tempting to either overbuild or underbuild&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  - What's next
&lt;/h2&gt;

&lt;p&gt;I'm continuing to build out Fontly and documenting the process as I go, the wins, the mistakes, all of it. If you're curious or want to follow along, I'll be posting updates here as things progress.&lt;/p&gt;

&lt;p&gt;Would love to hear from anyone who's built a small tool/side-project as a beginner, what do you wish you'd known earlier?&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>discuss</category>
      <category>buildinpublic</category>
      <category>webdev</category>
    </item>
    <item>
      <title>I Have No Idea What to Learn First And Here's How I'm Deciding</title>
      <dc:creator>Iris Khan</dc:creator>
      <pubDate>Thu, 27 Aug 2026 07:45:09 +0000</pubDate>
      <link>https://dev.to/iris_khan_dev/i-have-no-idea-what-to-learn-first-and-heres-how-im-deciding-4f4a</link>
      <guid>https://dev.to/iris_khan_dev/i-have-no-idea-what-to-learn-first-and-heres-how-im-deciding-4f4a</guid>
      <description>&lt;p&gt;So I've decided I want to learn web development. Cool. Great. Except then I actually sat down to start and realized... I don't even know what to start with.&lt;/p&gt;

&lt;p&gt;HTML? CSS? JavaScript? Python? React? Back-end stuff? People keep saying different things and every video has a different "you should start with THIS" opinion. It's honestly kind of overwhelming for someone who hasn't written a single line of real code yet.&lt;/p&gt;

&lt;p&gt;So instead of learning anything, what I actually did was open like 15 tabs, watch parts of a bunch of videos, and close my laptop feeling more confused than when I started lol.&lt;/p&gt;

&lt;h2&gt;
  
  
  what I've looked at so far
&lt;/h2&gt;

&lt;p&gt;Not much tbh. A couple YouTube videos comparing front-end and back-end (still not 100% sure I get the difference). One video tried explaining what JavaScript actually does on a page and I understood maybe half of it. And I've been scrolling posts here on DEV a lot, just reading how other people started. That part actually helped the most, not because anyone gave me THE answer, but because everyone started differently and that made me feel less stressed about "picking wrong."&lt;/p&gt;

&lt;h2&gt;
  
  
  how I'm picking
&lt;/h2&gt;

&lt;p&gt;No real system, just vibes basically:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;HTML/CSS feels the least scary to start with because you can actually see something change on the screen right away, and that feels good when you're this new to everything&lt;/li&gt;
&lt;li&gt;if a lot of people keep saying "start here" for beginners, I figure there's probably something to it&lt;/li&gt;
&lt;li&gt;and mostly I just need to pick SOMETHING and stop researching forever. I think the real mistake isn't choosing the "wrong" language first, it's never choosing anything at all&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I'm not trying to get this decision perfect, I just want to actually start building stuff instead of just watching people build stuff.&lt;/p&gt;

&lt;p&gt;so if you've been where I am right now, if you were starting completely over, what would YOU learn first?&lt;/p&gt;

&lt;p&gt;not looking for a "correct" answer since i don't know if there even is one, just curious what actually worked for real people instead of another "top 10 languages to learn in 2026" article lol&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>discuss</category>
      <category>learning</category>
      <category>career</category>
    </item>
    <item>
      <title>10 Free Websites Every Web Developer Should Bookmark</title>
      <dc:creator>Iris Khan</dc:creator>
      <pubDate>Wed, 29 Jul 2026 13:17:49 +0000</pubDate>
      <link>https://dev.to/iris_khan_dev/10-free-websites-every-web-developer-should-bookmark-5fde</link>
      <guid>https://dev.to/iris_khan_dev/10-free-websites-every-web-developer-should-bookmark-5fde</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fszni6o8a2kh9qcsij605.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fszni6o8a2kh9qcsij605.png" alt=" " width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;10 Free Websites Every Web Developer Should Bookmark&lt;br&gt;
Whether you're just starting your web development journey or have been coding for years, the right tools can save you time and make your workflow much smoother.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Here are 10 free websites I think every web developer should know.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. MDN Web Docs
&lt;/h2&gt;

&lt;p&gt;The best place to learn HTML, CSS, and JavaScript. Clear explanations with reliable documentation.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. CodePen
&lt;/h2&gt;

&lt;p&gt;A great playground for testing HTML, CSS, and JavaScript without setting up a project.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Can I Use
&lt;/h2&gt;

&lt;p&gt;Check browser compatibility before using modern CSS or JavaScript features.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. GitHub
&lt;/h2&gt;

&lt;p&gt;Perfect for hosting projects, collaborating with others, and exploring open-source code.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Regex101
&lt;/h2&gt;

&lt;p&gt;Makes writing and debugging regular expressions much easier.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. TinyPNG
&lt;/h2&gt;

&lt;p&gt;Compress images to improve website loading speed while maintaining quality.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Excalidraw
&lt;/h2&gt;

&lt;p&gt;A simple online whiteboard for sketching UI ideas and system designs.&lt;/p&gt;

&lt;h2&gt;
  
  
  8. DevDocs
&lt;/h2&gt;

&lt;p&gt;A fast documentation browser that brings multiple programming docs together.&lt;/p&gt;

&lt;h2&gt;
  
  
  9. JSON Formatter
&lt;/h2&gt;

&lt;p&gt;Useful when working with APIs and debugging JSON responses.&lt;/p&gt;

&lt;h2&gt;
  
  
  10. CSS-Tricks
&lt;/h2&gt;

&lt;p&gt;An excellent resource for CSS tips, layout techniques, and front-end development articles.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;These are some of the free resources I regularly use while learning and building web projects.&lt;/p&gt;

&lt;p&gt;Do you have a favorite developer website that isn't on this list? I'd love to discover more—share it in the comments!&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>tools</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
