<?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: Bilal Malik</title>
    <description>The latest articles on DEV Community by Bilal Malik (@bilalmlkdev).</description>
    <link>https://dev.to/bilalmlkdev</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%2F3960897%2F905216a6-54fb-4549-877f-cfd4ea7792c0.png</url>
      <title>DEV Community: Bilal Malik</title>
      <link>https://dev.to/bilalmlkdev</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/bilalmlkdev"/>
    <language>en</language>
    <item>
      <title>Build a Browser‑Based Favicon Generator with React, TypeScript, and Tailwind</title>
      <dc:creator>Bilal Malik</dc:creator>
      <pubDate>Mon, 03 Aug 2026 13:49:03 +0000</pubDate>
      <link>https://dev.to/bilalmlkdev/build-a-browser-based-favicon-generator-with-react-typescript-and-tailwind-1h5c</link>
      <guid>https://dev.to/bilalmlkdev/build-a-browser-based-favicon-generator-with-react-typescript-and-tailwind-1h5c</guid>
      <description>&lt;p&gt;&lt;strong&gt;Favicraft&lt;/strong&gt; is an open‑source, no‑login favicon generator that runs entirely in the browser. It lets you design text‑based favicons, upload images, or check any website's favicon – and export a complete package with every required size, manifest, and framework snippet. No sign‑up, no server uploads.&lt;/p&gt;

&lt;p&gt;In this article, I'll walk through how Favicraft works, its tech stack, and the key implementation details that make it powerful and easy to use.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why Favicraft?&lt;/strong&gt;&lt;br&gt;
Favicons are a tiny but critical part of web branding. Yet most tools require you to sign up, upload files to a server, or manually resize images. Favicraft solves this by:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Running &lt;strong&gt;100% client‑side&lt;/strong&gt; - your images never leave your device.&lt;/li&gt;
&lt;li&gt;Offering three modes – text, image, and a favicon checker.&lt;/li&gt;
&lt;li&gt;Generating a complete package – ICO, PNGs, Apple Touch, Android Chrome, and &lt;code&gt;site.webmanifest&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Providing framework‑specific install snippets for React, Next.js, Astro, and more.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Features&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Text Mode&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Design a favicon using only text. Customize:&lt;/li&gt;
&lt;li&gt;Font family, weight, color, and size&lt;/li&gt;
&lt;li&gt;Background shape (rounded, circle, square) and color&lt;/li&gt;
&lt;li&gt;Letter spacing, rotation, font style&lt;/li&gt;
&lt;li&gt;Shadows (color, blur, X/Y offset)&lt;/li&gt;
&lt;li&gt;Strokes (color, width)&lt;/li&gt;
&lt;li&gt;Gradients (two colors, angle)&lt;/li&gt;
&lt;li&gt;Border (width, color, style)&lt;/li&gt;
&lt;li&gt;Custom border radius and padding&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;code&gt;All changes are previewed in real time inside browser‑like mockups (tab, Google result, bookmark, home screen).&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Image Mode&lt;/strong&gt;&lt;br&gt;
Upload any PNG, JPG, WebP, or SVG image – Favicraft automatically generates all required sizes (16×16, 32×32, 48×48, 180×180, 192×192, 512×512) and bundles them with site.webmanifest.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Favicon Checker&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Enter any domain (without https://) and get:&lt;/li&gt;
&lt;li&gt;The favicon preview at multiple sizes (via Google's favicon service)&lt;/li&gt;
&lt;li&gt;A list of icon files found on the site (favicon.ico, favicon.png, etc.)&lt;/li&gt;
&lt;li&gt;Page metadata (title, description, OG image, theme color) using Microlink's public API&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;4. Export Panel&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Download individual sizes as PNGs&lt;/li&gt;
&lt;li&gt;Download the full ZIP package with all files and manifest&lt;/li&gt;
&lt;li&gt;Copy install snippet for your framework (HTML, Next.js App Router, React/Vite, Next.js Pages Router, Astro, Nuxt, SvelteKit, WordPress)&lt;/li&gt;
&lt;li&gt;Copy an AI install prompt to let an assistant integrate the favicon into your project&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;5. Tech Stack&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;React - UI library&lt;/li&gt;
&lt;li&gt;TypeScript - Type safety&lt;/li&gt;
&lt;li&gt;Vite - Fast build tool&lt;/li&gt;
&lt;li&gt;Tailwind CSS - Styling&lt;/li&gt;
&lt;li&gt;Lenis - Smooth scrolling&lt;/li&gt;
&lt;li&gt;JSZip - ZIP archive generation&lt;/li&gt;
&lt;li&gt;File‑Saver - File downloads&lt;/li&gt;
&lt;li&gt;Lucide React - Icons&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;6. Key Implementation Details&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Canvas Generation:&lt;/strong&gt;&lt;br&gt;
Favicons are rendered on an HTML  element. For text mode, we apply all styling (font, color, shadow, stroke, gradient, border, etc.) using the Canvas API. The generated PNGs are then included in the ZIP.&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="c1"&gt;// Simplified version&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;generateTextCanvasBlob&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;textVal&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fontFamily&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;canvas&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;canvas&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;size&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;size&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;ctx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2d&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// Apply background, shape, text, shadows, strokes, etc.&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toBlob&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;image/png&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;&lt;strong&gt;7. ZIP Packaging:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;JSZip bundles all files into a single archive. We include:&lt;/li&gt;
&lt;li&gt;favicon-16x16.png, favicon-32x32.png, favicon-48x48.png&lt;/li&gt;
&lt;li&gt;apple-touch-icon.png&lt;/li&gt;
&lt;li&gt;android-chrome-192x192.png, android-chrome-512x512.png&lt;/li&gt;
&lt;li&gt;site.webmanifest (with proper metadata)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The ZIP is then downloaded using file-saver.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;8. Framework Snippets&lt;/strong&gt;&lt;br&gt;
A simple switch statement returns the appropriate HTML/JSX for each framework. For example, for Next.js App Router:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getFrameworkSnippet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fw&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;switch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fw&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Next.js App Router&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="s2"&gt;`// app/layout.tsx
export const metadata = {
  icons: {
    icon: '/favicon.ico',
    shortcut: '/favicon-16x16.png',
    apple: '/apple-touch-icon.png',
  },
  manifest: '/site.webmanifest',
};`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="c1"&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;&lt;strong&gt;9. Favicon Checker - Icon Probing&lt;/strong&gt;&lt;br&gt;
Since the browser can't read a cross‑origin site's HTML directly, we probe for icon files by loading them as images and checking if they load successfully. This reliably tells us whether a given path exists.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;probeImage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;boolean&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;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;resolve&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;img&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Image&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nx"&gt;img&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;img&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;naturalWidth&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;img&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onerror&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;img&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;url&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;&lt;code&gt;Metadata is fetched via Microlink's public API (which acts as a proxy).&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Live Demo &amp;amp; Repository&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Live Demo: &lt;a href="https://favicraft.vercel.app" rel="noopener noreferrer"&gt;favicraft&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;GitHub: &lt;a href="https://github.com/byllzz/favicraft" rel="noopener noreferrer"&gt;Repo Link&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Feel free to star the repo, open issues, or contribute! Favicraft is MIT‑licensed and open for improvements.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What's Next?&lt;/strong&gt;&lt;br&gt;
I'm planning to improve the Text Mode UI (currently disabled pending a redesign), add dark mode support, and possibly integrate AI‑powered suggestions for text favicons. If you have ideas, let me know!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;💬 Conclusion&lt;/strong&gt;&lt;br&gt;
Favicraft is a showcase of how modern web technologies can create a fully functional, offline‑capable tool without any backend. It's built for developers by a developer, and I hope it saves you time and frustration when dealing with favicons.&lt;/p&gt;

&lt;p&gt;If you enjoyed this project, give it a ⭐ on &lt;a href="https://github.com/byllzz/favicraft.git" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; and share it with your fellow developers!&lt;/p&gt;

&lt;p&gt;Happy Coding...!&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>opensource</category>
      <category>webdev</category>
      <category>react</category>
    </item>
    <item>
      <title>I built a no-login color palette app - turns out RLS policies alone don't work</title>
      <dc:creator>Bilal Malik</dc:creator>
      <pubDate>Sun, 02 Aug 2026 04:45:05 +0000</pubDate>
      <link>https://dev.to/bilalmlkdev/i-built-a-no-login-color-palette-app-turns-out-rls-policies-alone-dont-work-2k65</link>
      <guid>https://dev.to/bilalmlkdev/i-built-a-no-login-color-palette-app-turns-out-rls-policies-alone-dont-work-2k65</guid>
      <description>&lt;p&gt;I wanted a place to find and publish color palettes without making anyone sign up. Not "sign up but it's fast," actually zero accounts. Turns out that's a more interesting engineering problem than it sounds.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The core idea&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Every visitor gets a random device ID generated once with &lt;code&gt;crypto.randomUUID()&lt;/code&gt; and stored in localStorage. That ID goes to Supabase whenever someone likes a palette. No email, no password, nothing tied to a real identity - just "this browser liked this palette." Likes and counts are global and live for everyone, but only your own browser sees which ones you've liked.&lt;/p&gt;

&lt;p&gt;It's not a new idea, but building it properly taught me a few things I didn't expect.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Grants aren't the same as RLS policies&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I set up Row Level Security policies thinking that was the whole story. It's not. Postgres also needs an explicit &lt;code&gt;GRANT&lt;/code&gt; on the table for the &lt;code&gt;anon&lt;/code&gt; role before RLS even gets evaluated - otherwise every request 401s with a permission error, and the RLS policies you wrote never even get a chance to run. Two separate layers, both required.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The URL sync race condition that took me a while to catch&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I sync app state to the URL and back so every view is shareable. The bug: clicking a palette from a tagged view would open the detail page for a split second, then snap back to the homepage. Turns out my URL→store sync effect was calling the same "set active tags" action that the UI uses when a person clicks a tag - and that action also resets the current view as a side effect. So opening a palette would correctly set the view to "detail," and then the very next line in the same effect would silently reset it back to "new" because of a stale tag comparison. Fixed it by splitting that into two actions: one for real user clicks (which should change the view), and one for URL sync (which shouldn't).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Making the loading bar actually mean something&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Every state change (switching tags, opening a palette, searching) runs through a small loader animation. Originally the actual data updated instantly and the loading bar was just decorative, running after the fact. Now the state update is deferred until the loader animation completes, using a token so a stale update from an old click can't clobber a newer one. Small detail, but it's the difference between a loading bar that means something and one that's just visual noise.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Stack&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;React, TypeScript, Zustand, Supabase (Postgres, no auth), Tailwind. No backend server - Supabase's REST layer directly from the client, secured entirely through RLS.&lt;/p&gt;

&lt;p&gt;It's free, open source, and live: &lt;a href="//palettesnap.vercel.app"&gt;PaletteSnap&lt;/a&gt;. Repo's here if you want to see how the anonymous-likes/URL-sync stuff is wired: &lt;a href="https://github.com/byllzz/palettesnap" rel="noopener noreferrer"&gt;Github Repo&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>supabase</category>
      <category>opensource</category>
      <category>typescript</category>
    </item>
    <item>
      <title>Building RhythmKey: Creating a Modern Typing Experience with React, Tailwind, and Real-Time Analytics</title>
      <dc:creator>Bilal Malik</dc:creator>
      <pubDate>Fri, 31 Jul 2026 14:51:48 +0000</pubDate>
      <link>https://dev.to/bilalmlkdev/building-rhythmkey-creating-a-modern-typing-experience-with-react-tailwind-and-real-time-26og</link>
      <guid>https://dev.to/bilalmlkdev/building-rhythmkey-creating-a-modern-typing-experience-with-react-tailwind-and-real-time-26og</guid>
      <description>&lt;h1&gt;
  
  
  Introduction
&lt;/h1&gt;

&lt;p&gt;Typing tests are everywhere, but many focus only on one thing:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"How fast can you type?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I wanted to build something different.&lt;/p&gt;

&lt;p&gt;Something that feels like a real typing environment - fast, customizable, and enjoyable.&lt;/p&gt;

&lt;p&gt;That's how &lt;strong&gt;RhythmKey&lt;/strong&gt; was created.&lt;/p&gt;

&lt;p&gt;RhythmKey is an open-source typing application built with &lt;strong&gt;React, Tailwind CSS, and Vite&lt;/strong&gt; that combines:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Real-time typing analytics&lt;/li&gt;
&lt;li&gt;Multiple test modes&lt;/li&gt;
&lt;li&gt;Custom themes&lt;/li&gt;
&lt;li&gt;Mechanical keyboard sounds&lt;/li&gt;
&lt;li&gt;Smooth animations&lt;/li&gt;
&lt;li&gt;Detailed performance tracking&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The goal was simple:&lt;/p&gt;

&lt;p&gt;Create a typing experience that helps users improve while actually enjoying the process.&lt;/p&gt;

&lt;p&gt;In this article, I'll share how I built RhythmKey, the architecture behind it, and the challenges I solved along the way.&lt;/p&gt;

&lt;h1&gt;
  
  
  Features That Make RhythmKey Different
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Multiple Typing Modes
&lt;/h2&gt;

&lt;p&gt;RhythmKey supports different practice styles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Time tests (15s, 30s, 60s, 120s)&lt;/li&gt;
&lt;li&gt;Word challenges&lt;/li&gt;
&lt;li&gt;Stories&lt;/li&gt;
&lt;li&gt;Quotes&lt;/li&gt;
&lt;li&gt;Infinite typing&lt;/li&gt;
&lt;li&gt;Custom text&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Real-Time Statistics
&lt;/h2&gt;

&lt;p&gt;During every test, users can see:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;WPM&lt;/li&gt;
&lt;li&gt;Accuracy&lt;/li&gt;
&lt;li&gt;Mistakes&lt;/li&gt;
&lt;li&gt;Backspaces&lt;/li&gt;
&lt;li&gt;Progress tracking&lt;/li&gt;
&lt;li&gt;Consistency&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The calculations update instantly while typing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Custom Mechanical Keyboard
&lt;/h2&gt;

&lt;p&gt;One of my favorite parts of RhythmKey is the custom keyboard experience.&lt;/p&gt;

&lt;p&gt;The app includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Animated on-screen keyboard&lt;/li&gt;
&lt;li&gt;QWERTY / AZERTY / DVORAK layouts&lt;/li&gt;
&lt;li&gt;Key highlighting&lt;/li&gt;
&lt;li&gt;Custom sound effects&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Instead of generic typing sounds, RhythmKey includes different sound profiles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Mechanical switches&lt;/li&gt;
&lt;li&gt;Click sounds&lt;/li&gt;
&lt;li&gt;Typewriter style&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The goal was to make typing feel more physical.&lt;/p&gt;

&lt;h2&gt;
  
  
  Personalization
&lt;/h2&gt;

&lt;p&gt;Users can customize:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Dark / Light / System themes&lt;/li&gt;
&lt;li&gt;Cursor styles&lt;/li&gt;
&lt;li&gt;Font size&lt;/li&gt;
&lt;li&gt;Sound settings&lt;/li&gt;
&lt;li&gt;Difficulty&lt;/li&gt;
&lt;li&gt;Punctuation&lt;/li&gt;
&lt;li&gt;Numbers&lt;/li&gt;
&lt;li&gt;Symbols&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All settings are saved automatically.&lt;/p&gt;

&lt;h1&gt;
  
  
  Building the Architecture
&lt;/h1&gt;

&lt;p&gt;RhythmKey is built with a component-based React architecture.&lt;/p&gt;

&lt;p&gt;The main idea:&lt;/p&gt;

&lt;p&gt;Keep UI components simple and move complex logic into reusable hooks.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tech Stack
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Technology&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;React&lt;/td&gt;
&lt;td&gt;UI architecture&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Vite&lt;/td&gt;
&lt;td&gt;Fast development&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tailwind CSS&lt;/td&gt;
&lt;td&gt;Styling&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;React Router&lt;/td&gt;
&lt;td&gt;Navigation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LocalStorage&lt;/td&gt;
&lt;td&gt;Data persistence&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SVG&lt;/td&gt;
&lt;td&gt;Performance graphs&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Custom Hooks
&lt;/h2&gt;

&lt;p&gt;The application relies heavily on custom hooks:&lt;/p&gt;

&lt;h3&gt;
  
  
  useTypingTest
&lt;/h3&gt;

&lt;p&gt;Handles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Typing state&lt;/li&gt;
&lt;li&gt;Timer&lt;/li&gt;
&lt;li&gt;Mistakes&lt;/li&gt;
&lt;li&gt;WPM calculation&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  useTypingHandlers
&lt;/h3&gt;

&lt;p&gt;Controls:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Keyboard input&lt;/li&gt;
&lt;li&gt;Shortcuts&lt;/li&gt;
&lt;li&gt;Sound triggers&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  useSettings
&lt;/h3&gt;

&lt;p&gt;Manages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Preferences&lt;/li&gt;
&lt;li&gt;Themes&lt;/li&gt;
&lt;li&gt;Keyboard settings&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  useStats
&lt;/h3&gt;

&lt;p&gt;Stores:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;History&lt;/li&gt;
&lt;li&gt;Best scores&lt;/li&gt;
&lt;li&gt;User progress&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Creating a Real-Time Typing Engine
&lt;/h1&gt;

&lt;p&gt;The hardest part of RhythmKey was creating a typing engine that feels instant.&lt;/p&gt;

&lt;p&gt;Every key press needs to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Check the expected character&lt;/li&gt;
&lt;li&gt;Update the UI&lt;/li&gt;
&lt;li&gt;Track mistakes&lt;/li&gt;
&lt;li&gt;Play sounds&lt;/li&gt;
&lt;li&gt;Update statistics&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A simplified version:&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;expected&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
&lt;span class="nx"&gt;currentText&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;userInput&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&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;key&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;expected&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
   &lt;span class="nf"&gt;setMistakes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;prev&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;prev&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&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;h1&gt;
  
  
  Part 5 - Challenges &amp;amp; Lessons
&lt;/h1&gt;

&lt;h1&gt;
  
  
  Challenges I Solved
&lt;/h1&gt;

&lt;h2&gt;
  
  
  1. Making Typing Feel Natural
&lt;/h2&gt;

&lt;p&gt;The first version felt too static.&lt;/p&gt;

&lt;p&gt;I improved it by adding:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Smooth cursor movement&lt;/li&gt;
&lt;li&gt;Better animations&lt;/li&gt;
&lt;li&gt;Instant feedback&lt;/li&gt;
&lt;li&gt;Sound responses&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  2. Saving User Preferences
&lt;/h2&gt;

&lt;p&gt;Users expect their setup to remain.&lt;/p&gt;

&lt;p&gt;I created a settings system using LocalStorage that remembers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Theme&lt;/li&gt;
&lt;li&gt;Keyboard layout&lt;/li&gt;
&lt;li&gt;Sounds&lt;/li&gt;
&lt;li&gt;Preferences&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  3. Supporting Multiple Keyboard Layouts
&lt;/h2&gt;

&lt;p&gt;Supporting QWERTY, AZERTY, and DVORAK required custom key mapping logic.&lt;/p&gt;

&lt;p&gt;The keyboard automatically adapts based on the selected layout.&lt;/p&gt;

&lt;h1&gt;
  
  
  What I Learned
&lt;/h1&gt;

&lt;p&gt;Building RhythmKey taught me:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Better React state management&lt;/li&gt;
&lt;li&gt;Designing reusable hooks&lt;/li&gt;
&lt;li&gt;Creating smooth user interactions&lt;/li&gt;
&lt;li&gt;Managing complex frontend logic&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Future Improvements
&lt;/h1&gt;

&lt;p&gt;Some ideas for the future:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Multiplayer typing races&lt;/li&gt;
&lt;li&gt;More languages&lt;/li&gt;
&lt;li&gt;Advanced statistics&lt;/li&gt;
&lt;li&gt;Community themes&lt;/li&gt;
&lt;li&gt;Typing lessons&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Try RhythmKey
&lt;/h1&gt;

&lt;p&gt;Live Demo:&lt;br&gt;
&lt;a href="https://rhythmkey.vercel.app/" rel="noopener noreferrer"&gt;https://rhythmkey.vercel.app/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;GitHub:&lt;br&gt;
&lt;a href="https://github.com/byllzz/rhythmkey" rel="noopener noreferrer"&gt;https://github.com/byllzz/rhythmkey&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;RhythmKey is open-source under the MIT License.&lt;/p&gt;

&lt;p&gt;If you enjoyed it, feel free to ⭐ the repository or contribute.&lt;/p&gt;

</description>
      <category>react</category>
      <category>webdev</category>
      <category>opensource</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Moviio: A Pure CSS 3D Carousel Movie Explorer</title>
      <dc:creator>Bilal Malik</dc:creator>
      <pubDate>Wed, 29 Jul 2026 10:21:31 +0000</pubDate>
      <link>https://dev.to/bilalmlkdev/moviio-a-pure-css-3d-carousel-movie-explorer-5d2b</link>
      <guid>https://dev.to/bilalmlkdev/moviio-a-pure-css-3d-carousel-movie-explorer-5d2b</guid>
      <description>&lt;p&gt;I wanted to build a movie discovery app that felt as cinematic as the films it showcases. So I created &lt;strong&gt;Moviio&lt;/strong&gt; - a 3D carousel movie explorer powered by TMDB, built entirely with vanilla JavaScript and CSS (no frameworks, no carousel libraries).&lt;/p&gt;




&lt;h2&gt;
  
  
  The Core: A Custom 3D Wheel
&lt;/h2&gt;

&lt;p&gt;The main feature is a &lt;strong&gt;7-card 3D wheel&lt;/strong&gt;. Each card is placed using CSS custom properties (&lt;code&gt;--slot&lt;/code&gt;) and 3D transforms.&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="nc"&gt;.card-0&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;--slot&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;translateY&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;55px&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;translateX&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;-13px&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;rotate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;-63deg&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.card-3&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;--slot&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;rotate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0deg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;scale&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.card.active&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;grayscale&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0%&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;brightness&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nl"&gt;box-shadow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;20px&lt;/span&gt; &lt;span class="m"&gt;50px&lt;/span&gt; &lt;span class="n"&gt;rgba&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nl"&gt;pointer-events&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;visible&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 center card pops with a parallax tilt effect that reacts to your mouse, and you can swipe, drag, or use arrow keys to rotate the wheel.&lt;/p&gt;




&lt;h2&gt;
  
  
  What It Does
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Trailer Overlay
&lt;/h3&gt;

&lt;p&gt;Click the active card to open a full-screen cinema view with YouTube trailers, runtime, cast, and director details.&lt;/p&gt;

&lt;h3&gt;
  
  
  Persistent Watchlist
&lt;/h3&gt;

&lt;p&gt;Save favorites to &lt;code&gt;localStorage&lt;/code&gt;. The Watchlist filter dims the carousel and shows an empty-state message when nothing is saved.&lt;/p&gt;

&lt;h3&gt;
  
  
  Auto-Rotate &amp;amp; Shuffle
&lt;/h3&gt;

&lt;p&gt;Let the wheel spin automatically (pauses on hover) or jump to a random page of movies.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/?movie_id=...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Keyboard Shortcuts
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;← / →&lt;/strong&gt; - Navigate the carousel&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enter / Space&lt;/strong&gt; - Open the trailer&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Escape&lt;/strong&gt; - Close the overlay&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The Backend (Simple)
&lt;/h2&gt;

&lt;p&gt;API requests go through a Vercel serverless function (&lt;code&gt;/api/tmdb.js&lt;/code&gt;) that acts as a proxy. This keeps my TMDB API key secure while forwarding the real status codes from TMDB.&lt;/p&gt;




&lt;h2&gt;
  
  
  Built With
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;HTML5&lt;/li&gt;
&lt;li&gt;CSS3&lt;/li&gt;
&lt;li&gt;Vanilla JavaScript&lt;/li&gt;
&lt;li&gt;TMDB API&lt;/li&gt;
&lt;li&gt;Vercel&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Visuals
&lt;/h2&gt;

&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%2Fraw.githubusercontent.com%2Fbyllzz%2Fmoviio%2Fmain%2Fassets%2Fcharts%2FprojectBuild-process.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%2Fraw.githubusercontent.com%2Fbyllzz%2Fmoviio%2Fmain%2Fassets%2Fcharts%2FprojectBuild-process.png" alt="Development Roadmap" width="800" height="1089"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Step‑by‑step build journey from concept to deployment - a visual roadmap of how Moviio was constructed.&lt;/em&gt;&lt;/p&gt;

&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%2Fbz5w57cplijxlulwjoq8.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%2Fbz5w57cplijxlulwjoq8.png" alt="Runtime Architecture" width="799" height="394"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;How data flows through the app - from user interaction to TMDB API, local storage, and the trailer overlay.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Try It Out
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Live Demo&lt;/strong&gt;: &lt;a href="https://moviio.vercel.app/" rel="noopener noreferrer"&gt;moviio.vercel.app&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Source Code&lt;/strong&gt;: &lt;a href="https://github.com/byllzz/moviio" rel="noopener noreferrer"&gt;github.com/byllzz/moviio&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;If you enjoy the project, give it a ⭐ on GitHub - it really helps!&lt;/em&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>css</category>
      <category>pwa</category>
    </item>
  </channel>
</rss>
