<?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: Heghine</title>
    <description>The latest articles on DEV Community by Heghine (@zeroaninea_8bec34a4e7d029).</description>
    <link>https://dev.to/zeroaninea_8bec34a4e7d029</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%2F2614675%2Fff18a61a-6023-4f1a-833c-adae0c63c6af.png</url>
      <title>DEV Community: Heghine</title>
      <link>https://dev.to/zeroaninea_8bec34a4e7d029</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/zeroaninea_8bec34a4e7d029"/>
    <language>en</language>
    <item>
      <title>My Portfolio Development Roadmap for Freelance Web Development</title>
      <dc:creator>Heghine</dc:creator>
      <pubDate>Thu, 14 May 2026 22:19:32 +0000</pubDate>
      <link>https://dev.to/zeroaninea_8bec34a4e7d029/my-portfolio-development-roadmap-for-freelance-web-development-3g0</link>
      <guid>https://dev.to/zeroaninea_8bec34a4e7d029/my-portfolio-development-roadmap-for-freelance-web-development-3g0</guid>
      <description>&lt;h2&gt;
  
  
  Source
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;GitHub: &lt;a href="https://github.com/ZeroaNinea" rel="noopener noreferrer"&gt;https://github.com/ZeroaNinea&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;One of the most important lessons I've learned is that potential clients want to see real examples of your work.&lt;/p&gt;

&lt;p&gt;Knowing many technologies is valuable because it allows you to apply to a wider range of projects and work with different technical stacks.&lt;/p&gt;

&lt;p&gt;At the same time, technologies alone are not enough.&lt;/p&gt;

&lt;p&gt;Most clients are not developers. They usually do not understand the technical details of software development and are primarily interested in whether you can solve their business problem.&lt;/p&gt;

&lt;p&gt;That is why portfolio projects are so important.&lt;/p&gt;

&lt;p&gt;A strong portfolio provides concrete evidence that you can build real products such as business websites, SaaS applications, e-commerce stores, and complex web applications.&lt;/p&gt;

&lt;p&gt;In practice, both factors matter:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Technical knowledge helps you qualify for more opportunities.&lt;/li&gt;
&lt;li&gt;Portfolio projects demonstrate that you can deliver real results.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;My goal is to build projects that showcase both technical breadth and practical business value.&lt;/p&gt;




&lt;h2&gt;
  
  
  Projects I Have Already Built
&lt;/h2&gt;

&lt;p&gt;I already have built several projects for my portfolio, you can see them in my profile &lt;code&gt;README.md&lt;/code&gt; on GitHub.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Animated draggable modal component in React with a rainbow button;&lt;/li&gt;
&lt;li&gt;Reusable Command Palette component with color customization component in Angular;&lt;/li&gt;
&lt;li&gt;Real-Time Chat Application similar to Discord (Angular + Angular Material + Node.js + Redis + Socket.io);&lt;/li&gt;
&lt;li&gt;Site Portfolio (Angular + Angular Material + custom SVG animations + Three.js);&lt;/li&gt;
&lt;li&gt;Real Estate Agency website in Vue.js (currently using mocked data, with a backend planned).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But they are still not enough. I have accomplished only the half of my initial plan. I still need more reusable components and full-fledged projects.&lt;/p&gt;

&lt;h3&gt;
  
  
  Modal Window in React
&lt;/h3&gt;

&lt;p&gt;It is a draggable modal window in React. I used Tailwind and Vite as additional technologies.&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.amazonaws.com%2Fuploads%2Farticles%2Few5wz7y4mfvu0ec796qr.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.amazonaws.com%2Fuploads%2Farticles%2Few5wz7y4mfvu0ec796qr.png" alt="Modal Window in React" width="800" height="405"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Rainbow Button
&lt;/h4&gt;

&lt;p&gt;When the user opens the page they first see a button with a rainbow gradient that infinitely moves to the left. The button increases its scale on hover, and plays a ripple effect when clicked.&lt;/p&gt;

&lt;p&gt;I have created a separate component for the ripple button, so it's reusable. The button is completely customizable, and it was intended to use it with Tailwind.&lt;/p&gt;

&lt;h4&gt;
  
  
  Modal Window
&lt;/h4&gt;

&lt;p&gt;After the click the application opens the modal window. The user can drag it using the icon with arrows, and close it by clicking on the cross icon or on the background overlay.&lt;/p&gt;

&lt;p&gt;The modal window appears and disappears with animations, and on subsequent clicks on the button, the window appears in the same place where it was the previous time.&lt;/p&gt;

&lt;h4&gt;
  
  
  Command Palette Component in Angular
&lt;/h4&gt;

&lt;p&gt;It is command palette component where the user can choose custom commands that it should execute. There is also a color palette customization component in this project that allows the user to control the colors of the website. I did not use Angular Material for this project, so all components here are implemented customly with SCSS.&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.amazonaws.com%2Fuploads%2Farticles%2Fh910hc7dgbtaduh81dxy.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.amazonaws.com%2Fuploads%2Farticles%2Fh910hc7dgbtaduh81dxy.png" alt="Command Palette Component — Angular" width="800" height="405"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The demonstration starts with a page with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"Open Command Palette" button;&lt;/li&gt;
&lt;li&gt;"View on GitHub" button;&lt;/li&gt;
&lt;li&gt;A short text with usage guide;&lt;/li&gt;
&lt;li&gt;And a Color Theme Setup component that allows the user to manipulate with all the colors on the website reactively.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  "Open Command Palette" Button
&lt;/h4&gt;

&lt;p&gt;When the user clicks on this button, it plays a ripple effect, and opens the &lt;code&gt;CommandPalette&lt;/code&gt; component.&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.amazonaws.com%2Fuploads%2Farticles%2Fp8rghunjv4gvdflhedik.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.amazonaws.com%2Fuploads%2Farticles%2Fp8rghunjv4gvdflhedik.png" alt="Command Palette Component — Opened" width="800" height="405"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The user can select commands with Up and Down Arrow keys, and execute them with the Enter key.&lt;/p&gt;

&lt;p&gt;All commands are custom functions that user should have to create earlier. When the user inputs something, it filters the commands.&lt;/p&gt;

&lt;p&gt;The user can close it my clicking on the overlay or entering a command.&lt;/p&gt;

&lt;h4&gt;
  
  
  "View on GitHub" Button
&lt;/h4&gt;

&lt;p&gt;When the user hovers on the "View on GitHub" button, it changes its &lt;code&gt;background-color&lt;/code&gt; and becomes lighter.&lt;/p&gt;

&lt;p&gt;When the user clicks on the "View on GitHub" button, the application plays a ripple effects, and redirects them to the source code of this project on GitHub.&lt;/p&gt;

&lt;h4&gt;
  
  
  Color Setup
&lt;/h4&gt;

&lt;p&gt;Here the user can input a color in hex format or pick the colors with the browser color picker.&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.amazonaws.com%2Fuploads%2Farticles%2Fwteiyvom1zme6dqjlnoo.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.amazonaws.com%2Fuploads%2Farticles%2Fwteiyvom1zme6dqjlnoo.png" alt="Color Setup" width="800" height="405"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Real-Time Chat Application
&lt;/h3&gt;

&lt;p&gt;I have built this project in Angular and Node.js. I used Angular Material for the frontend. I store sessions in a Redis database, and provide real-time interactions in Socket.io.&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.amazonaws.com%2Fuploads%2Farticles%2Fv2tqm0su0xtcfjtz0x2a.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.amazonaws.com%2Fuploads%2Farticles%2Fv2tqm0su0xtcfjtz0x2a.png" alt="Real-Time Chat Application — Staff-Room" width="800" height="404"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Currently the backend of this project does not work because of problems with my MongoDB clusters. But I am still working on it. Recently I added a dark mode.&lt;/p&gt;

&lt;p&gt;I also have issues that Tenor stopped being supported by Google, so I should switch to GIPHY.&lt;/p&gt;

&lt;p&gt;This project allows users:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;to create accounts, and manipulate with their account data;&lt;/li&gt;
&lt;li&gt;to create chat rooms and channels;&lt;/li&gt;
&lt;li&gt;to add friends and create private chat rooms with their friends;&lt;/li&gt;
&lt;li&gt;to pick emojis;&lt;/li&gt;
&lt;li&gt;to pick GIFs and add or remove them from favorites;&lt;/li&gt;
&lt;li&gt;to create custom roles for public chat rooms with different permissions.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Site Portfolio
&lt;/h3&gt;

&lt;p&gt;I have build this website in Angular and Angular Material. I used CSS animations, custom SVG animations and Three.js for visual effects in this project.&lt;/p&gt;

&lt;p&gt;This project contains five sections that have individual color palettes. They have fade in and fade out effects controlled by single animation service called in the &lt;code&gt;AppComponent&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;There is also an animated sticky header at the top of the screen.&lt;/p&gt;

&lt;h4&gt;
  
  
  Home: Orange Light
&lt;/h4&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.amazonaws.com%2Fuploads%2Farticles%2Fsxao5cid23j96nkucik3.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.amazonaws.com%2Fuploads%2Farticles%2Fsxao5cid23j96nkucik3.png" alt="Hero Section — Portfolio Site" width="800" height="405"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This section has animated triangles on the background of three colors: blue, green and pink.&lt;/p&gt;

&lt;p&gt;There is a "Hi, I'm Heghine." text as a title, and "Full-stack web developer." text as a subtitle. The subtitle animates with fade in bottom effect when the page reloads.&lt;/p&gt;

&lt;p&gt;There is a down arrow icon at the bottom of the &lt;code&gt;HeroComponent&lt;/code&gt;. If the user clicks on it, it smoothly scrolls down to the "About" section.&lt;/p&gt;

&lt;h4&gt;
  
  
  About: Blue Dark
&lt;/h4&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.amazonaws.com%2Fuploads%2Farticles%2F4tkx1unvrhvktbm9et96.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.amazonaws.com%2Fuploads%2Farticles%2F4tkx1unvrhvktbm9et96.png" alt="About Section — Portfolio Site" width="800" height="405"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Each paragraph of the about section and the entire section animates with a fade in right animation, when the user scrolls to the component.&lt;/p&gt;

&lt;p&gt;There is a "Building interactive apps with style" text with typewriter effect there. The effect activates with an intersection observer too.&lt;/p&gt;

&lt;p&gt;There are six orbiting parallelepipeds with pulsing blue shadow effect on the left side of the component. When the user hovers on them, they move further apart and the background shadow turns pink. There is also a button with a car icon that appears at the center of parallelepipeds only when the user hovers on them.&lt;/p&gt;

&lt;p&gt;If the user clicks on the car button, the application scrolls down to the car section that appears only on click. That section displays a 3D model of BMW M3 GTS.&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.amazonaws.com%2Fuploads%2Farticles%2Fwa0seewhngb6w7wxrx9d.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.amazonaws.com%2Fuploads%2Farticles%2Fwa0seewhngb6w7wxrx9d.png" alt="BMW M3 GTS — Portfolio Site" width="800" height="407"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Tech Stack: Green Light
&lt;/h4&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.amazonaws.com%2Fuploads%2Farticles%2Fpyawc0mk914t4z67n9ox.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.amazonaws.com%2Fuploads%2Farticles%2Fpyawc0mk914t4z67n9ox.png" alt="Tech Stack Section — Portfolio Site" width="800" height="405"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The "Tech Stack" section has animated cyber lines effect on its background. It is divided by tabs. There are six of them:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fronted,&lt;/li&gt;
&lt;li&gt;Backend,&lt;/li&gt;
&lt;li&gt;Full-stack,&lt;/li&gt;
&lt;li&gt;Real-time&lt;/li&gt;
&lt;li&gt;DevOps,&lt;/li&gt;
&lt;li&gt;and Testing &amp;amp; Quality.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each tab contains related major technologies that I know. Each technology icon is placed inside a technology element. Each technology element is a Drag &amp;amp; Drop Angular Material component. The user can drag and drop them, only in a framework of one tab.&lt;/p&gt;

&lt;p&gt;If the use clicks on a technology element it expands, and shows a shot explanation of that specific technology. The user can close it by clicking on the same button again, or on different a different button. If the user clicks on a different button, the new one opens, and the old one closes.&lt;/p&gt;

&lt;p&gt;The tab buttons are located at the left part of the component under the title. There is also a cross icon button at the left side of the title.&lt;/p&gt;

&lt;p&gt;If the user clicks on the cross icon button, it changes its icon from cross to three vertical dots, and closes tab buttons. The user can open them again by clicking on the three vertical dots icon button again.&lt;/p&gt;

&lt;h4&gt;
  
  
  Projects: Pink Dark
&lt;/h4&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.amazonaws.com%2Fuploads%2Farticles%2Fwotbzuok1n78jjh8lx73.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.amazonaws.com%2Fuploads%2Farticles%2Fwotbzuok1n78jjh8lx73.png" alt="BMW M3 GTS — About Section" width="800" height="405"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Currently there are only two projects in this section, but I plan to add my other projects here soon.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Real-Time Chat App;&lt;/li&gt;
&lt;li&gt;UI Elements.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I have built custom unique card elements to for the projects.&lt;/p&gt;

&lt;p&gt;Each card contains, a title, two 3D cube elements rotating in the same place, a pulsing background shadow effect, and a drifting plane element with a screenshot of that project.&lt;/p&gt;

&lt;p&gt;When the user hovers on a card, the cubes increase their scale, a pink like appears at the bottom of the title, and the background shadow becomes pink.&lt;/p&gt;

&lt;p&gt;When the user clicks on a card, the application plays a ripple effect, and redirects them to the project.&lt;/p&gt;

&lt;h4&gt;
  
  
  Contacts: Yellow Light
&lt;/h4&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.amazonaws.com%2Fuploads%2Farticles%2F1z0yus72te8so5r7znbt.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.amazonaws.com%2Fuploads%2Farticles%2F1z0yus72te8so5r7znbt.png" alt="Contact Section — Portfolio Site" width="800" height="405"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The "Contacts" section also should be updated. It only has two circular ripple buttons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;GitHub;&lt;/li&gt;
&lt;li&gt;Gmail.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I already have a lot of contacts, and I want to add them too.&lt;/p&gt;

&lt;h3&gt;
  
  
  Real Estate Agency Website
&lt;/h3&gt;

&lt;p&gt;This is a real estate agency project in Vue.js and Bootstrap. It's intended to have an admin panel with CURD functionality for properties. But currently I am mocking the data.&lt;/p&gt;

&lt;p&gt;Currently, it only has two pages — "Home" and "Listings."&lt;/p&gt;

&lt;p&gt;The home page has following components:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Header,&lt;/li&gt;
&lt;li&gt;Hero,&lt;/li&gt;
&lt;li&gt;Featured Properties,&lt;/li&gt;
&lt;li&gt;Why Choose Us,&lt;/li&gt;
&lt;li&gt;What Our Clients Say,&lt;/li&gt;
&lt;li&gt;Footer.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The header is a burger sticky header similar to the one in my portfolio site.&lt;/p&gt;

&lt;p&gt;I got the photos from this website: &lt;a href="https://static.photos/" rel="noopener noreferrer"&gt;https://static.photos/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I have also created two directives &lt;code&gt;v-reveal&lt;/code&gt; and &lt;code&gt;v-ripple&lt;/code&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Hero
&lt;/h4&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.amazonaws.com%2Fuploads%2Farticles%2Fdggxc2o2x9034gnctcfk.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.amazonaws.com%2Fuploads%2Farticles%2Fdggxc2o2x9034gnctcfk.png" alt="Hero Section — Real Estate Agency" width="800" height="405"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The "Hero" section contains a title: "\"Find Your Dream Home\"; a subtitle: \"Browse properties in your area\"; a search form; and a background with property image and an overlay animated with a transparent black gradient."&lt;/p&gt;

&lt;h4&gt;
  
  
  Featured Properties
&lt;/h4&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.amazonaws.com%2Fuploads%2Farticles%2Fv5c6lnr4s7y7bi6pf5m0.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.amazonaws.com%2Fuploads%2Farticles%2Fv5c6lnr4s7y7bi6pf5m0.png" alt="Featured Properties Section — Real Estate Agency" width="800" height="406"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This section has custom cards, when the user hovers on them they zoom their image. All buttons on this website have a ripple effect.&lt;/p&gt;

&lt;p&gt;The "View All Properties" redirects to the "Listings" page, but the "View Details" button does not redirect anywhere yet. However, I will start creating a page for a single property soon.&lt;/p&gt;

&lt;p&gt;Each card is animated with &lt;code&gt;v-reveal&lt;/code&gt; directive. When the user scrolls to one of them it appears with a fade in effect.&lt;/p&gt;

&lt;h4&gt;
  
  
  Why Choose Us &amp;amp; What Our Clients Say
&lt;/h4&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.amazonaws.com%2Fuploads%2Farticles%2Fqxahlar11pgr78ryiocs.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.amazonaws.com%2Fuploads%2Farticles%2Fqxahlar11pgr78ryiocs.png" alt="Why Choose Us Section — Real Estate Agency" width="800" height="240"&gt;&lt;/a&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.amazonaws.com%2Fuploads%2Farticles%2F67p68oef0s1sauu8kgoq.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.amazonaws.com%2Fuploads%2Farticles%2F67p68oef0s1sauu8kgoq.png" alt="What Our Clients Say Section — Real Estate Agency" width="800" height="227"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Both of these sections contain elements that move up cast a shadow on hover.&lt;/p&gt;

&lt;h4&gt;
  
  
  Footer
&lt;/h4&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.amazonaws.com%2Fuploads%2Farticles%2Fzqodar5lh8xo59byg5qu.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.amazonaws.com%2Fuploads%2Farticles%2Fzqodar5lh8xo59byg5qu.png" alt="Footer Section — Real Estate Agency" width="800" height="176"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The footer has a title and subtitle: "\"Find Your Perfect Home Today\" and \"Browse our listings or get in touch with our team for personalized help.\" And it has two ripple buttons: \"Brows Properties\" and \"Contact Us.\" And a blue to purple gradient on the background."&lt;/p&gt;

&lt;p&gt;And the second "Listings" page contains the following content:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Header,&lt;/li&gt;
&lt;li&gt;Hero,&lt;/li&gt;
&lt;li&gt;Filter,&lt;/li&gt;
&lt;li&gt;Listings,&lt;/li&gt;
&lt;li&gt;Pagination,&lt;/li&gt;
&lt;li&gt;Footer.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The header and the footer are the same as in the "Home" page.&lt;/p&gt;

&lt;h4&gt;
  
  
  Header, Hero and Filters
&lt;/h4&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.amazonaws.com%2Fuploads%2Farticles%2Feh1emcs817cxwkh1e2kj.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.amazonaws.com%2Fuploads%2Farticles%2Feh1emcs817cxwkh1e2kj.png" alt="Header, Hero and Filters of the Second Page — Real Estate Agency" width="800" height="233"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The filters work reactively and the "Hero" has the same animation as the "Hero" section on the "Home" page.&lt;/p&gt;

&lt;h4&gt;
  
  
  Listings &amp;amp; Pagination
&lt;/h4&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.amazonaws.com%2Fuploads%2Farticles%2Fnsksev4tge99rafpclcr.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.amazonaws.com%2Fuploads%2Farticles%2Fnsksev4tge99rafpclcr.png" alt="Listings and Pagination Sections — Real Estate Agency" width="720" height="490"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;These are the same cards as in the &lt;code&gt;FeaturedProperties&lt;/code&gt; component.&lt;/p&gt;




&lt;h3&gt;
  
  
  My Portfolio Goals
&lt;/h3&gt;

&lt;p&gt;My goal is to build a portfolio that demonstrates my ability to create different types of websites and web applications that businesses commonly need.&lt;/p&gt;

&lt;p&gt;I want potential clients to see concrete examples of my work and quickly understand that I can build solutions for wide range of use cases.&lt;/p&gt;

&lt;p&gt;The main types of projects I want to include in my portfolio are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Business websites — such as estate agency website with listings, filtering, and search functionality.&lt;/li&gt;
&lt;li&gt;Landing pages — modern marketing pages destined to present products and services clearly and effectively.&lt;/li&gt;
&lt;li&gt;SaaS applications — full-stack software products with authentication, dashboards, and scalable backend architecture.&lt;/li&gt;
&lt;li&gt;E-commerce websites — online stories built either with custom technologies or platforms such as Shopify.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In addition to complete applications, I also enjoy creating reusable UI components and more technically complex projects, such as real-time applications.&lt;/p&gt;

&lt;p&gt;By building projects in these categories, I aim to demonstrate both technical versatility and the ability to solve practical business problems.&lt;/p&gt;

&lt;p&gt;My long-term goal is to work directly with clients as a freelance web developer and eventually create my own software products.&lt;/p&gt;




&lt;h3&gt;
  
  
  Long-Term Vision
&lt;/h3&gt;

&lt;p&gt;My immediate goal is to start working directly with clients as a freelance web developer.&lt;/p&gt;

&lt;p&gt;I want to build websites and web applications that help businesses solve real problems while continuing to expand my technical skills and professional experience.&lt;/p&gt;

&lt;p&gt;In the long term, I plan to use the knowledge and experience I gain from client work to create my own software products.&lt;/p&gt;

&lt;p&gt;For me, portfolio projects are more than practice exercises. They are steps toward financial independence, professional growth, and the ability to build software that provides real value to others.&lt;/p&gt;

</description>
      <category>devrel</category>
      <category>petproject</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Creating a Command Palette Component in Angular (Part 2)</title>
      <dc:creator>Heghine</dc:creator>
      <pubDate>Sun, 26 Apr 2026 22:43:23 +0000</pubDate>
      <link>https://dev.to/zeroaninea_8bec34a4e7d029/creating-a-command-palette-component-in-angular-part-2-2em6</link>
      <guid>https://dev.to/zeroaninea_8bec34a4e7d029/creating-a-command-palette-component-in-angular-part-2-2em6</guid>
      <description>&lt;h2&gt;
  
  
  Command Palette
&lt;/h2&gt;

&lt;h2&gt;
  
  
  Sources:
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;GitHub: &lt;a href="https://github.com/ZeroaNinea/Command-Palette-Angular" rel="noopener noreferrer"&gt;https://github.com/ZeroaNinea/Command-Palette-Angular&lt;/a&gt;&lt;br&gt;
GitHub Pages: &lt;a href="https://zeroaninea.github.io/Command-Palette-Angular/" rel="noopener noreferrer"&gt;https://zeroaninea.github.io/Command-Palette-Angular/&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In this part, I implement the actual &lt;code&gt;CommandPalette&lt;/code&gt; component and connect it to the dynamic theming system from Part 1. The result is a reusable, keyboard-driven UI component that can execute custom commands.&lt;/p&gt;




&lt;h2&gt;
  
  
  Command Model and Examples
&lt;/h2&gt;

&lt;p&gt;Each command is an object that combines metadata (label, keywords) with behavior (handler). This allows the palette to remain generic while executing arbitrary logic.&lt;/p&gt;

&lt;p&gt;There is a simple list of commands in the &lt;code&gt;app.ts&lt;/code&gt; file: two alerts and commands that change the colors of palettes.&lt;/p&gt;

&lt;p&gt;I'm planning to create more UI elements in the future, and I will add them as commands in the &lt;code&gt;CommandPalette&lt;/code&gt;.&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="nx"&gt;commands&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Command&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="c1"&gt;// id: string;&lt;/span&gt;
    &lt;span class="c1"&gt;// label: string;&lt;/span&gt;
    &lt;span class="c1"&gt;// keywords?: string[];&lt;/span&gt;
    &lt;span class="c1"&gt;// shortcut?: string;&lt;/span&gt;
    &lt;span class="c1"&gt;// payload?: unknown;&lt;/span&gt;
    &lt;span class="c1"&gt;// handler?: (payload?: unknown) =&amp;gt; void;&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;alert-1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Alert 1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;keywords&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;alert&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="na"&gt;shortcut&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;a1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;handler&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="nf"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Alert 1&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;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;alert-2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Alert 2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;keywords&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;alert&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="na"&gt;shortcut&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;a2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;handler&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="nf"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Alert 2&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;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;primary-red&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Set Primary to Red&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;handler&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;primary&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#ff4d4f&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;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;primary-blue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Set Primary to Blue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;handler&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;primary&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#4FC3F7&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;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;reset&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Reset Colors&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;handler&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;primary&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#4FC3F7&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;secondary&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#2196F3&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tertiary&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#086CBC&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;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Importing the &lt;code&gt;CommandPalette&lt;/code&gt; Component
&lt;/h2&gt;

&lt;p&gt;The commands' list is passed to the &lt;code&gt;CommandPalette&lt;/code&gt; component as the &lt;code&gt;commands&lt;/code&gt; array.&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;app-command-palette&lt;/span&gt;
    &lt;span class="na"&gt;[style.--cp-bg]=&lt;/span&gt;&lt;span class="s"&gt;"neutralPalette().bg"&lt;/span&gt;
    &lt;span class="na"&gt;[style.--cp-surface]=&lt;/span&gt;&lt;span class="s"&gt;"neutralPalette().surface"&lt;/span&gt;
    &lt;span class="na"&gt;[style.--cp-border]=&lt;/span&gt;&lt;span class="s"&gt;"neutralVariantPalette().border"&lt;/span&gt;
    &lt;span class="na"&gt;[style.--cp-text]=&lt;/span&gt;&lt;span class="s"&gt;"neutralVariantPalette()['100']"&lt;/span&gt;
    &lt;span class="na"&gt;[style.--cp-accent]=&lt;/span&gt;&lt;span class="s"&gt;"primaryPalette().accent"&lt;/span&gt;
    &lt;span class="na"&gt;[style.--cp-hover]=&lt;/span&gt;&lt;span class="s"&gt;"neutralPalette().hover"&lt;/span&gt;
    &lt;span class="na"&gt;[isOpen]=&lt;/span&gt;&lt;span class="s"&gt;"isOpen()"&lt;/span&gt;
    &lt;span class="na"&gt;[commands]=&lt;/span&gt;&lt;span class="s"&gt;"commands"&lt;/span&gt;
    &lt;span class="na"&gt;(isOpenChange)=&lt;/span&gt;&lt;span class="s"&gt;"isOpen.set($event)"&lt;/span&gt;
    &lt;span class="na"&gt;(commandSelected)=&lt;/span&gt;&lt;span class="s"&gt;"onCommand($event)"&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;gt;&amp;lt;/app-command-palette&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;CommandPalette&lt;/code&gt; component also emits &lt;code&gt;isOpenChange&lt;/code&gt; and commandSelected events. Accordingly:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;isOpenChange&lt;/code&gt; event is fired when the user changes the state of the &lt;code&gt;CommandPalette&lt;/code&gt; component from shown to hidden, and back. The &lt;code&gt;isOpenChange&lt;/code&gt; event is used to notify the parent when the palette should close (e.g., after selecting a command or clicking outside).&lt;/li&gt;
&lt;li&gt;And the &lt;code&gt;commandSelected&lt;/code&gt; event works when the user selects a command. It emits an entire element of the command from the &lt;code&gt;commands&lt;/code&gt; array, and passes the required &lt;code&gt;.payload&lt;/code&gt; to the command handler if it exists.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;  &lt;span class="nf"&gt;onCommand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cmd&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Command&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;commandSelected&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;emit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cmd&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;cmd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;?.(&lt;/span&gt;&lt;span class="nx"&gt;cmd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isOpenChange&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;emit&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="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isOpenChange&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;emit&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Command Filtering and Keyboard Navigation
&lt;/h2&gt;

&lt;p&gt;Not all commands are visible at the same time. If the user inputs something into the command palette, it filters visible commands. There are two properties that used for command filtering inside the &lt;code&gt;CommandPalette&lt;/code&gt; component that participate in the filtering logic: &lt;code&gt;query&lt;/code&gt; and &lt;code&gt;filtered&lt;/code&gt;.&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="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Input&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="nx"&gt;commands&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Command&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="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Input&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="nx"&gt;isOpen&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Output&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="nx"&gt;commandSelected&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;EventEmitter&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Command&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Output&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="nx"&gt;isOpenChange&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;EventEmitter&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="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;ViewChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;searchInput&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ElementRef&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;HTMLInputElement&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nx"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;filtered&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Command&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="nx"&gt;activeIndex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;query&lt;/code&gt;: contains the value that user inputs into the component input.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;filtered&lt;/code&gt;: it's the array of filtered commands.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;code&gt;visibleCommands&lt;/code&gt; getter returns the &lt;code&gt;filtered&lt;/code&gt; array if the user did not input queries. Otherwise, it will return the &lt;code&gt;commands&lt;/code&gt; array received from the parent component.&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;get&lt;/span&gt; &lt;span class="nf"&gt;visibleCommands&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nx"&gt;Command&lt;/span&gt;&lt;span class="p"&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="o"&gt;!&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;query&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;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;commands&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;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;filtered&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 filter filters commands by their label and keyboards.&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="nf"&gt;filter&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;q&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toLowerCase&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;filtered&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;commands&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cmd&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;cmd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toLowerCase&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;q&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
        &lt;span class="nx"&gt;cmd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;keywords&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;some&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;k&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;k&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toLowerCase&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;q&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;activeIndex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&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 &lt;code&gt;filter&lt;/code&gt; method is bound to the &lt;code&gt;input&lt;/code&gt; event, and it triggers only when the user inputs something.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;@if (isOpen) {
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;(click)=&lt;/span&gt;&lt;span class="s"&gt;"close()"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"overlay"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;(click)=&lt;/span&gt;&lt;span class="s"&gt;"$event.stopPropagation()"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"palette"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt;
        &lt;span class="na"&gt;#searchInput&lt;/span&gt;
        &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"search"&lt;/span&gt;
        &lt;span class="na"&gt;placeholder=&lt;/span&gt;&lt;span class="s"&gt;"Type a command..."&lt;/span&gt;
        &lt;span class="na"&gt;[(ngModel)]=&lt;/span&gt;&lt;span class="s"&gt;"query"&lt;/span&gt;
        &lt;span class="na"&gt;(input)=&lt;/span&gt;&lt;span class="s"&gt;"filter()"&lt;/span&gt;
        &lt;span class="na"&gt;(keydown)=&lt;/span&gt;&lt;span class="s"&gt;"onKeydown($event)"&lt;/span&gt;
      &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"list"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        @for (command of visibleCommands; track $index; let i = $index) {
          &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"item"&lt;/span&gt; &lt;span class="na"&gt;[class.active]=&lt;/span&gt;&lt;span class="s"&gt;"i === activeIndex"&lt;/span&gt; &lt;span class="na"&gt;(click)=&lt;/span&gt;&lt;span class="s"&gt;"onCommand(command)"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            {{ command.label }}
          &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
        }
      &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There is also an &lt;code&gt;onKeydown&lt;/code&gt; method that is bound to the &lt;code&gt;keydown&lt;/code&gt; event. It works when the user focuses on the &lt;code&gt;input&lt;/code&gt; and uses any key. But its logic only uses the &lt;code&gt;ArrowDown&lt;/code&gt;, &lt;code&gt;ArrowUp&lt;/code&gt;, &lt;code&gt;Enter&lt;/code&gt; and &lt;code&gt;Escape&lt;/code&gt; keys.&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="nf"&gt;onKeydown&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;KeyboardEvent&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;list&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;visibleCommands&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="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;list&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;return&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;e&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ArrowDown&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="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;preventDefault&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;activeIndex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;activeIndex&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="o"&gt;%&lt;/span&gt; &lt;span class="nx"&gt;list&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="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;e&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ArrowUp&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="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;preventDefault&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;activeIndex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;activeIndex&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;list&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="o"&gt;%&lt;/span&gt; &lt;span class="nx"&gt;list&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="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;e&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Enter&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="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;preventDefault&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;onCommand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;activeIndex&lt;/span&gt;&lt;span class="p"&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;e&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Escape&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nf"&gt;setTimeout&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;el&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;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.item.active&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;scrollIntoView&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;block&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;nearest&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;The class &lt;code&gt;.active&lt;/code&gt; is assigned to the element that has the same index in the &lt;code&gt;visibleCommands&lt;/code&gt; array as the value of the &lt;code&gt;activeIndex&lt;/code&gt; method.&lt;/p&gt;

&lt;p&gt;When the user presses the &lt;code&gt;ArrowDown&lt;/code&gt; or &lt;code&gt;ArrowUp&lt;/code&gt; keys, the code decrements and increments the &lt;code&gt;activeIndex&lt;/code&gt;, but it cannot be less than 0 and more than the length of the &lt;code&gt;visibleCommands&lt;/code&gt; array.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;Enter&lt;/code&gt; key executes the function and closes the &lt;code&gt;CommandPalette&lt;/code&gt; component. And the &lt;code&gt;Escape&lt;/code&gt; key just closes it.&lt;/p&gt;

&lt;p&gt;Then after iterating the conditional operations the application scrolls to the new active element.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;ngOnChanges&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;ngOnChanges&lt;/code&gt; lifecycle hook is used to react to changes in input properties. In this case, it listens for changes to the &lt;code&gt;isOpen&lt;/code&gt; input.&lt;/p&gt;

&lt;p&gt;When the command palette is opened, its internal state is reset:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the search query is cleared,&lt;/li&gt;
&lt;li&gt;the filtered commands list is reset,&lt;/li&gt;
&lt;li&gt;the active index is set to the first item.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This ensures that every time the palette opens, it starts in a clean and predictable state instead of preserving the previous search.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;ngAfterViewChecked&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;ngAfterViewChecked&lt;/code&gt; lifecycle hook runs after Angular updates the component’s view.&lt;/p&gt;

&lt;p&gt;It is used here to automatically focus the input field when the command palette is opened.&lt;/p&gt;

&lt;p&gt;This allows the user to start typing immediately without needing to click on the input manually.&lt;/p&gt;




&lt;h2&gt;
  
  
  Afterword
&lt;/h2&gt;

&lt;p&gt;In this part, I implemented the core logic of the command palette: command definition, filtering, and keyboard navigation. The component is fully functional and can execute custom commands while remaining reusable and independent from specific business logic.&lt;/p&gt;

&lt;p&gt;One of the key ideas behind this implementation is treating commands as objects that combine metadata (label, keywords, shortcut) with behavior (handler). This allows the component to stay generic while supporting a wide range of use cases.&lt;/p&gt;

&lt;p&gt;The command palette is also integrated with the dynamic theming system created in the previous part, which makes it easy to adapt its appearance without changing the component itself.&lt;/p&gt;

&lt;p&gt;There are still many possible improvements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;grouping commands by category,&lt;/li&gt;
&lt;li&gt;adding icons,&lt;/li&gt;
&lt;li&gt;highlighting matched search text,&lt;/li&gt;
&lt;li&gt;improving accessibility and keyboard support.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I plan to continue expanding this project and use the command palette as a central interaction system for future UI components.&lt;/p&gt;

</description>
      <category>angular</category>
      <category>ui</category>
      <category>tutorial</category>
      <category>uidesign</category>
    </item>
    <item>
      <title>Creating a Command Palette Component in Angular (Part 1)</title>
      <dc:creator>Heghine</dc:creator>
      <pubDate>Tue, 21 Apr 2026 22:17:18 +0000</pubDate>
      <link>https://dev.to/zeroaninea_8bec34a4e7d029/creating-a-command-palette-component-in-angular-part-1-bh1</link>
      <guid>https://dev.to/zeroaninea_8bec34a4e7d029/creating-a-command-palette-component-in-angular-part-1-bh1</guid>
      <description>&lt;h2&gt;
  
  
  Dynamic Palette Generation
&lt;/h2&gt;

&lt;h2&gt;
  
  
  Sources:
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;GitHub: &lt;a href="https://github.com/ZeroaNinea/Command-Palette-Angular" rel="noopener noreferrer"&gt;https://github.com/ZeroaNinea/Command-Palette-Angular&lt;/a&gt;&lt;br&gt;
GitHub Pages: &lt;a href="https://zeroaninea.github.io/Command-Palette-Angular/" rel="noopener noreferrer"&gt;https://zeroaninea.github.io/Command-Palette-Angular/&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;This is the first part of a series where I build a reusable and customizable command palette in Angular.&lt;/p&gt;

&lt;p&gt;Since the component needs to support different themes, I started by creating a dynamic color palette generator. It takes a set of base colors and produces consistent palettes for primary, secondary, tertiary, neutral, and error roles.&lt;/p&gt;

&lt;h2&gt;
  
  
  Palette Generation
&lt;/h2&gt;

&lt;p&gt;I've created a palette generation utility in the project's &lt;code&gt;src/app/shared/utils/palette.util.ts&lt;/code&gt; directory. It uses the &lt;code&gt;chroma-js&lt;/code&gt; library, and exports a single function &lt;code&gt;createPalette&lt;/code&gt; that takes one parameter the base color of a string type, then it returns an object of &lt;code&gt;Palette&lt;/code&gt; type.&lt;/p&gt;

&lt;p&gt;The function generates a scale of 10 colors (from 50 to 900), similar to common design systems. These are then mapped to semantic roles like background, surface, text, and accent.&lt;/p&gt;

&lt;p&gt;Here is the example of the code:&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;import&lt;/span&gt; &lt;span class="nx"&gt;chroma&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;chroma-js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Palette&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../../../types/palette.alias&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;createPalette&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;base&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="nx"&gt;Palette&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;scale&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;chroma&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;scale&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nf"&gt;chroma&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;base&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;brighten&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nx"&gt;base&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;chroma&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;base&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;darken&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)])&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;lab&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="nf"&gt;colors&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&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="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;scale&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="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;scale&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;scale&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;scale&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;scale&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;scale&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="mi"&gt;600&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;scale&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="mi"&gt;700&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;scale&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="mi"&gt;800&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;scale&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="mi"&gt;900&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;scale&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;

    &lt;span class="c1"&gt;// Semantic roles.&lt;/span&gt;
    &lt;span class="na"&gt;bg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;scale&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="na"&gt;surface&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;scale&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;surfaceAlt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;scale&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;scale&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;chroma&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;contrast&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scale&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#fff&lt;/span&gt;&lt;span class="dl"&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="mf"&gt;4.5&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#fff&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#000&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;textMuted&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;scale&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;accent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;scale&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;accentHover&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;scale&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;accentActive&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;scale&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;

    &lt;span class="c1"&gt;// State colors.&lt;/span&gt;
    &lt;span class="na"&gt;hover&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;chroma&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;base&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;brighten&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;hex&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="na"&gt;active&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;chroma&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;base&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;darken&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;hex&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="na"&gt;focus&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;chroma&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;base&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;saturate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;hex&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;

    &lt;span class="c1"&gt;// Base&lt;/span&gt;
    &lt;span class="na"&gt;base&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;base&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;h2&gt;
  
  
  Updating the Palettes Reactively
&lt;/h2&gt;

&lt;p&gt;And then in the, in the root of the project, in the &lt;code&gt;app.ts&lt;/code&gt; file. I passed six base colors to the &lt;code&gt;createPalette&lt;/code&gt; function as default values. I used &lt;code&gt;signal&lt;/code&gt;s and the &lt;code&gt;computed&lt;/code&gt; function to reactively update the palettes later, when the user inputs new colors.&lt;/p&gt;

&lt;p&gt;Here's how palettes are computed reactively using Angular signals:&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;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;computed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;signal&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;RouterOutlet&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/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;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createPalette&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./shared/utils/palette.util&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Palette&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../types/palette.alias&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;CommandPalette&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./command-palette/command-palette&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ColorInput&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./color-input/color-input&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="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;app-root&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;imports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;RouterOutlet&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;CommandPalette&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ColorInput&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;templateUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./app.html&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;styleUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./app.scss&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="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;App&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Command-Palette-Angular&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nx"&gt;primary&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#4FC3F7&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;secondary&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#2196F3&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;tertiary&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#086CBC&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;neutral&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#929CA6&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;neutralVariant&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#6E8E9D&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#E01B24&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nx"&gt;primaryPalette&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;computed&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Palette&lt;/span&gt;&lt;span class="o"&gt;&amp;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;createPalette&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;primary&lt;/span&gt;&lt;span class="p"&gt;()));&lt;/span&gt;
  &lt;span class="nx"&gt;secondaryPalette&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;computed&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Palette&lt;/span&gt;&lt;span class="o"&gt;&amp;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;createPalette&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;secondary&lt;/span&gt;&lt;span class="p"&gt;()));&lt;/span&gt;
  &lt;span class="nx"&gt;tertiaryPalette&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;computed&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Palette&lt;/span&gt;&lt;span class="o"&gt;&amp;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;createPalette&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tertiary&lt;/span&gt;&lt;span class="p"&gt;()));&lt;/span&gt;
  &lt;span class="nx"&gt;neutralPalette&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;computed&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Palette&lt;/span&gt;&lt;span class="o"&gt;&amp;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;createPalette&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;neutral&lt;/span&gt;&lt;span class="p"&gt;()));&lt;/span&gt;
  &lt;span class="nx"&gt;neutralVariantPalette&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;computed&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Palette&lt;/span&gt;&lt;span class="o"&gt;&amp;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;createPalette&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;neutralVariant&lt;/span&gt;&lt;span class="p"&gt;()));&lt;/span&gt;
  &lt;span class="nx"&gt;errorPalette&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;computed&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Palette&lt;/span&gt;&lt;span class="o"&gt;&amp;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;createPalette&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&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;After that I added the &lt;code&gt;ColorInput&lt;/code&gt; component that reactively updates the chosen colors and emits them back to the root of the project.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;ColorInput&lt;/code&gt; component contains four elements positioned absolutely, &lt;code&gt;label&lt;/code&gt;, &lt;code&gt;.text-overlay&lt;/code&gt;, &lt;code&gt;.ripple&lt;/code&gt; and &lt;code&gt;.color-picker-overlay&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;label&lt;/code&gt;: it's the label of the &lt;code&gt;input&lt;/code&gt; element it raises up when user hovers or focuses on the input.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;.text-overlay&lt;/code&gt;: overlays the text of the &lt;code&gt;input&lt;/code&gt; when it is lowered, and disappears on focus or hover, when the &lt;code&gt;label&lt;/code&gt; raises up.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;.ripple&lt;/code&gt;: provides a ripple effect.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;.color-picker-overlay&lt;/code&gt;: overlays the color picker input, but it has a &lt;code&gt;pointer-events: none&lt;/code&gt;, it's only used for changing the color of the &lt;code&gt;input&lt;/code&gt; on hover.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It also has two inputs &lt;code&gt;.color-text&lt;/code&gt; and &lt;code&gt;.color-picker&lt;/code&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;.color-text&lt;/code&gt; is needed to input colors as a text.&lt;/li&gt;
&lt;li&gt;And the &lt;code&gt;.color-picker&lt;/code&gt; uses the default browser color picker to input colors with a visual interface.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;They are both working and there is no big difference which of them to use. They are both emitting the chosen color back to the &lt;code&gt;App&lt;/code&gt; component.&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;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"input-container"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;label&amp;gt;&lt;/span&gt;{{ label }}&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"text-overlay"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"input"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt;
      &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt;
      &lt;span class="na"&gt;[value]=&lt;/span&gt;&lt;span class="s"&gt;"value"&lt;/span&gt;
      &lt;span class="na"&gt;(input)=&lt;/span&gt;&lt;span class="s"&gt;"onInput($any($event.target).value)"&lt;/span&gt;
      &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"color-text"&lt;/span&gt;
    &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"color-picker-wrapper"&lt;/span&gt; &lt;span class="na"&gt;(click)=&lt;/span&gt;&lt;span class="s"&gt;"createRipple($event)"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt;
        &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"color"&lt;/span&gt;
        &lt;span class="na"&gt;[value]=&lt;/span&gt;&lt;span class="s"&gt;"value"&lt;/span&gt;
        &lt;span class="na"&gt;(input)=&lt;/span&gt;&lt;span class="s"&gt;"onInput($any($event.target).value)"&lt;/span&gt;
        &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"color-picker"&lt;/span&gt;
      &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"ripple"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt;
        &lt;span class="na"&gt;[style.background]=&lt;/span&gt;&lt;span class="s"&gt;"
          'color-mix(in oklab, var(--cp-base) 0%, ' + getColorPickerOverlayColor() + ' 20%)'
        "&lt;/span&gt;
        &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"color-picker-overlay"&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The most interesting part is that the &lt;code&gt;.color-picker-overlay&lt;/code&gt;'s color is calculated dynamically based on contrast. If the base color is dark, a lighter overlay is used, and vice versa. This ensures the hover effect remains visible regardless of the selected color.. It takes the base color, that is actually the value of the input element, and checks how light or dark it is. If it's light then it will make the overlay darker, and if it's dark the overlay will become lighter.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;.ts&lt;/code&gt; file of the &lt;code&gt;ColorInput&lt;/code&gt; component looks like this:&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;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;EventEmitter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Output&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;chroma&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;chroma-js&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="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;app-color-input&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;imports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
  &lt;span class="na"&gt;templateUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./color-input.html&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;styleUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./color-input.scss&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="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ColorInput&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Input&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="nx"&gt;label&lt;/span&gt; &lt;span class="o"&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;span class="nd"&gt;Input&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#4FC3F7&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="nd"&gt;Output&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="nx"&gt;valueChange&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;EventEmitter&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="nf"&gt;getColorPickerOverlayColor&lt;/span&gt;&lt;span class="p"&gt;()&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;chroma&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;contrast&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#fff&lt;/span&gt;&lt;span class="dl"&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="mf"&gt;4.5&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#fff&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#000&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="nf"&gt;onInput&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&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="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;valueChange&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;emit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;createRipple&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;MouseEvent&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;wrapper&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;currentTarget&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;HTMLElement&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;ripple&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;wrapper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.ripple&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;HTMLElement&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;rect&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;wrapper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getBoundingClientRect&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;size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;rect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&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;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clientX&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;rect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;left&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;size&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&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;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clientY&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;rect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;top&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;size&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nx"&gt;ripple&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="nx"&gt;width&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ripple&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="nx"&gt;height&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&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="s2"&gt;px`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;ripple&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="nx"&gt;left&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;px`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;ripple&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="nx"&gt;top&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;px`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nx"&gt;ripple&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;active&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nx"&gt;ripple&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;offsetWidth&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;ripple&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;active&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;And finally, I call the &lt;code&gt;ColorInput&lt;/code&gt; component in the &lt;code&gt;App&lt;/code&gt; component’s template six times, and pass them the CSS variables, the base color and accept the emitted new color.&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;app-color-input&lt;/span&gt;
          &lt;span class="na"&gt;label=&lt;/span&gt;&lt;span class="s"&gt;"Primary"&lt;/span&gt;
          &lt;span class="na"&gt;[value]=&lt;/span&gt;&lt;span class="s"&gt;"primary()"&lt;/span&gt;
          &lt;span class="na"&gt;(valueChange)=&lt;/span&gt;&lt;span class="s"&gt;"primary.set($event)"&lt;/span&gt;
          &lt;span class="na"&gt;[style.--cp-bg]=&lt;/span&gt;&lt;span class="s"&gt;"neutralPalette().bg"&lt;/span&gt;
          &lt;span class="na"&gt;[style.--cp-text]=&lt;/span&gt;&lt;span class="s"&gt;"primaryPalette().text"&lt;/span&gt;
          &lt;span class="na"&gt;[style.--cp-accent]=&lt;/span&gt;&lt;span class="s"&gt;"primaryPalette().accent"&lt;/span&gt;
          &lt;span class="na"&gt;[style.--cp-border]=&lt;/span&gt;&lt;span class="s"&gt;"primaryPalette().border"&lt;/span&gt;
          &lt;span class="na"&gt;[style.--cp-surface]=&lt;/span&gt;&lt;span class="s"&gt;"primaryPalette().surface"&lt;/span&gt;
          &lt;span class="na"&gt;[style.--cp-base]=&lt;/span&gt;&lt;span class="s"&gt;"primaryPalette().base"&lt;/span&gt;
          &lt;span class="na"&gt;[style.--cp-input-border]=&lt;/span&gt;&lt;span class="s"&gt;"primaryPalette()['300']"&lt;/span&gt;
          &lt;span class="na"&gt;[style.--cp-input-label]=&lt;/span&gt;&lt;span class="s"&gt;"primaryPalette()['900']"&lt;/span&gt;
          &lt;span class="na"&gt;[style.--cp-label-muted]=&lt;/span&gt;&lt;span class="s"&gt;"neutralPalette()['600']"&lt;/span&gt;
          &lt;span class="na"&gt;[style.--cp-input-text]=&lt;/span&gt;&lt;span class="s"&gt;"neutralVariantPalette()['900']"&lt;/span&gt;
          &lt;span class="na"&gt;[style.--cp-input-ripple]=&lt;/span&gt;&lt;span class="s"&gt;"primaryPalette()['100']"&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;gt;&amp;lt;/app-color-input&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Afterword
&lt;/h2&gt;

&lt;p&gt;In the next part, I'll build the actual command palette (similar to VS Code's Ctrl + Shift + P), using this theming system. The goal is to make it fully customizable and reusable. It will allow users to create their own custom commands. They should name them and pass functions that they want to execute when the command it entered.&lt;/p&gt;

&lt;p&gt;That component will be completely customizable. That's why I needed to create a color theme setup component first.&lt;/p&gt;

</description>
      <category>angular</category>
      <category>webdev</category>
      <category>tutorial</category>
      <category>uidesign</category>
    </item>
    <item>
      <title>I Applied to Dozens of Freelance Developer Jobs on LinkedIn — Here's what Happened</title>
      <dc:creator>Heghine</dc:creator>
      <pubDate>Wed, 08 Apr 2026 06:15:25 +0000</pubDate>
      <link>https://dev.to/zeroaninea_8bec34a4e7d029/i-applied-to-dozens-of-freelance-developer-jobs-on-linkedin-heres-what-happened-255e</link>
      <guid>https://dev.to/zeroaninea_8bec34a4e7d029/i-applied-to-dozens-of-freelance-developer-jobs-on-linkedin-heres-what-happened-255e</guid>
      <description>&lt;p&gt;This week, my LinkedIn profile appeared 877 times.&lt;/p&gt;

&lt;p&gt;I spent the last few days actively applying to freelance developer posts — sending messages, writing emails, and reaching out to people for React, Angular, and full-stack developers.&lt;/p&gt;

&lt;p&gt;For long time, I didn't think deeply about the process of getting a job. I thought that building projects and learning new technologies would somehow help me start working as a freelancer.&lt;/p&gt;

&lt;p&gt;It wasn't.&lt;/p&gt;

&lt;p&gt;So instead of continuing to learn endlessly, I decided to do something different: I started treating LinkedIn like a real opportunity source — and began applying consistently.&lt;/p&gt;

&lt;p&gt;What happened next was surprisingly different from what I expected.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I Actually Did
&lt;/h2&gt;

&lt;p&gt;Instead of just scrolling or learning passively, I started taking action:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Applied to multiple freelance posts every day;&lt;/li&gt;
&lt;li&gt;Sent emails and LikedIn DMs;&lt;/li&gt;
&lt;li&gt;Shared my work (my Real-Time Chat App and portfolio);&lt;/li&gt;
&lt;li&gt;Shared conversations with potential clients.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I don't want to feel "ready." I just started.&lt;/p&gt;

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

&lt;h3&gt;
  
  
  1. Most People Don't Reply
&lt;/h3&gt;

&lt;p&gt;This was the first thing I noticed. You can send multiple messages, and get no response at all. At first. it feels like rejection. But it's not.&lt;/p&gt;

&lt;p&gt;I just how things work.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;People are busy;&lt;/li&gt;
&lt;li&gt;Posts get flooded with messages;&lt;/li&gt;
&lt;li&gt;Many applications are ignored.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is normal.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Timing Matters More Than I Expected
&lt;/h3&gt;

&lt;p&gt;Applying early makes a big difference.&lt;/p&gt;

&lt;p&gt;Posts that are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Recently published;&lt;/li&gt;
&lt;li&gt;Have fewer comments.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Are much more likely to get a response.&lt;/p&gt;

&lt;p&gt;If you apply late, you're just one of many.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Messaging Matters
&lt;/h3&gt;

&lt;p&gt;At first, I wrote very genetic messages. They didn't work well. What I worked better was being clear and direct:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What I can build;&lt;/li&gt;
&lt;li&gt;What I've already built;&lt;/li&gt;
&lt;li&gt;How I can help with their project.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;People don't want long introductions. They want to understand quickly if you're useful.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. A Board Tech Stack Helps — But Not in the Way I Expected
&lt;/h3&gt;

&lt;p&gt;One unexpected advantage I had was knowing multiple frameworks. Since I've worked with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Angular;&lt;/li&gt;
&lt;li&gt;React;&lt;/li&gt;
&lt;li&gt;React Native;&lt;/li&gt;
&lt;li&gt;Next.js;&lt;/li&gt;
&lt;li&gt;Vue.js;&lt;/li&gt;
&lt;li&gt;Nest.js;&lt;/li&gt;
&lt;li&gt;Node.js.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I didn't have to skip many posts.&lt;/p&gt;

&lt;p&gt;I could apply to wide range of opportunities:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Frontend;&lt;/li&gt;
&lt;li&gt;Full-stack;&lt;/li&gt;
&lt;li&gt;Sometimes even backend-focused roles.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But this doesn't mean you need to learn everything. What actually matters is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Being able to build real things.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The tech stack just gives you more entry points.&lt;/p&gt;




&lt;h3&gt;
  
  
  Real Conversations Started to Happen
&lt;/h3&gt;

&lt;p&gt;After some time, I started getting responses.&lt;/p&gt;

&lt;p&gt;Not many — but enough to notice a pattern.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I had a conversation about a JAMStack role and went through an interview.&lt;/li&gt;
&lt;li&gt;I connected with other developers.&lt;/li&gt;
&lt;li&gt;Some people told me they might reach out in the future.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Nothing immediate came out of it, but it felt like I was no longer just sending messages into nothing.&lt;/p&gt;

&lt;p&gt;Eventually, I started having actual conversations with potential clients.&lt;/p&gt;

&lt;p&gt;In one of them, I was asked:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"What are your charges for an app?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It wasn't surprising — just a bit unexpected how quickly the conversation moved to pricing.&lt;/p&gt;

&lt;p&gt;I tried to understand the project first and asked about the scope.&lt;/p&gt;

&lt;p&gt;But they asked again.&lt;/p&gt;

&lt;p&gt;So I gave a simple range:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;basic MVP: around $300–800&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;They said they would get back to me. And maybe they will, maybe they won't. But that wasn't the important part.&lt;/p&gt;

&lt;p&gt;The important part was that the process had changed:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;From applying to having real discussions.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h3&gt;
  
  
  Advice If You're Starting
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Don't wait until you feel ready;&lt;/li&gt;
&lt;li&gt;Apply even if you don't match every requirement;&lt;/li&gt;
&lt;li&gt;Build at least one real project;&lt;/li&gt;
&lt;li&gt;Keep sending messages, even if most don’t reply.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Consistency matters more than perfection.&lt;/p&gt;




&lt;h3&gt;
  
  
  Afterword
&lt;/h3&gt;

&lt;p&gt;I'm currently building and sharing my work publicly, including a Real-Time Chat Application and other ongoing projects.&lt;/p&gt;

&lt;p&gt;I'm also open to freelance opportunities.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;GitHub: &lt;a href="https://github.com/ZeroaNinea/" rel="noopener noreferrer"&gt;https://github.com/ZeroaNinea/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Portfolio: &lt;a href="https://heghine-app.netlify.app" rel="noopener noreferrer"&gt;https://heghine-app.netlify.app&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>linkedin</category>
      <category>webdev</category>
      <category>devrel</category>
    </item>
    <item>
      <title>I Built a Real-Time Chat App From Scratch — Then Realized That Wasn't Enough</title>
      <dc:creator>Heghine</dc:creator>
      <pubDate>Mon, 30 Mar 2026 05:52:48 +0000</pubDate>
      <link>https://dev.to/zeroaninea_8bec34a4e7d029/i-built-a-real-time-chat-app-from-scratch-then-realized-that-wasnt-enough-5515</link>
      <guid>https://dev.to/zeroaninea_8bec34a4e7d029/i-built-a-real-time-chat-app-from-scratch-then-realized-that-wasnt-enough-5515</guid>
      <description>&lt;h2&gt;
  
  
  Source
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;GitHub: &lt;a href="https://github.com/ZeroaNinea/Real-Time-Chat-App" rel="noopener noreferrer"&gt;https://github.com/ZeroaNinea/Real-Time-Chat-App&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Why I Built It
&lt;/h2&gt;

&lt;p&gt;I started this project as a simple pet project to help me get a job.&lt;/p&gt;

&lt;p&gt;I built a real-time chat application from scratch, solving problems step by step and turning it into something that actually worked.&lt;/p&gt;

&lt;p&gt;But after finishing it, I didn't immediately start applying.&lt;/p&gt;

&lt;p&gt;Instead, I thought:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"I need to learn more first."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So I spent the next seven months learning frameworks like React, Vue, Next.js, and Nest.js.&lt;/p&gt;

&lt;p&gt;Only later did I realize something important:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I could have already started looking for a job.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;After that, I spent two more months on LinkedIn and writing posts, but still didn't get results.&lt;/p&gt;

&lt;p&gt;That's when it became clear:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Building projects is important.&lt;/li&gt;
&lt;li&gt;Learning is important.&lt;/li&gt;
&lt;li&gt;But knowing how to present yourself and reach people matters just as much.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The Biggest Challenge: WebSockets and Structure
&lt;/h2&gt;

&lt;p&gt;One of the hardest parts of the project was working with WebSockets using Socket.io.&lt;/p&gt;

&lt;p&gt;At the time:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I didn't fully understand how it worked.&lt;/li&gt;
&lt;li&gt;I couldn't properly connect users to rooms.&lt;/li&gt;
&lt;li&gt;all my Socket.io logic was in one huge file.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It quickly became difficult to manage.&lt;/p&gt;

&lt;p&gt;The real issue wasn't just bugs — it was structure.&lt;/p&gt;

&lt;p&gt;After refactoring:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I split the logic into smaller parts&lt;/li&gt;
&lt;li&gt;separated responsibilities&lt;/li&gt;
&lt;li&gt;made the system easier to understand&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That was a turning point.&lt;/p&gt;




&lt;h2&gt;
  
  
  Mistakes I Made
&lt;/h2&gt;

&lt;p&gt;A major mistake was not clearly separating real-time and non-real-time logic.&lt;/p&gt;

&lt;p&gt;I relied heavily on WebSockets early on, which led to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Mixing different types of functionality.&lt;/li&gt;
&lt;li&gt;Unclear architecture.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If I were to rebuild it, I would:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use REST APIs for standard operations.&lt;/li&gt;
&lt;li&gt;Use WebSockets only where real-time updates are necessary.&lt;/li&gt;
&lt;li&gt;Define clear boundaries from the beginning.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  What I'd Improve
&lt;/h2&gt;

&lt;p&gt;There's still a lot I want to improve:&lt;/p&gt;

&lt;h3&gt;
  
  
  Features
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;File uploads (images, videos, text files).&lt;/li&gt;
&lt;li&gt;Highlighting the current channel.&lt;/li&gt;
&lt;li&gt;Customizable themes (including dark mode variants).&lt;/li&gt;
&lt;li&gt;Movable UI elements.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  UX
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Better message display and layout&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Architecture
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;End-to-end encryption.&lt;/li&gt;
&lt;li&gt;A separate service for encryption key rotation.&lt;/li&gt;
&lt;/ul&gt;




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

&lt;p&gt;The biggest lesson wasn't technical.&lt;/p&gt;

&lt;p&gt;It was this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Building something real is necessary — but it's not enough on its own.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You also need to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Show your work.&lt;/li&gt;
&lt;li&gt;Communicate clearly.&lt;/li&gt;
&lt;li&gt;Reach the right people.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Otherwise, even a solid project can go unnoticed.&lt;/p&gt;




&lt;h2&gt;
  
  
  Afterword
&lt;/h2&gt;

&lt;p&gt;This project became more than just a chat app.&lt;/p&gt;

&lt;p&gt;It taught me:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How to build real systems.&lt;/li&gt;
&lt;li&gt;How to deal with complexity.&lt;/li&gt;
&lt;li&gt;And how important visibility is.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you're learning development, don't just build —&lt;br&gt;
make sure people can actually see what you've built.&lt;/p&gt;

</description>
      <category>angular</category>
      <category>node</category>
      <category>socketio</category>
      <category>devrel</category>
    </item>
    <item>
      <title>Build a Modern Stack with Vite, Tailwind, i18next, and TanStack Router</title>
      <dc:creator>Heghine</dc:creator>
      <pubDate>Fri, 20 Mar 2026 13:43:58 +0000</pubDate>
      <link>https://dev.to/zeroaninea_8bec34a4e7d029/build-a-modern-stack-with-vite-tailwind-i18next-and-tanstack-router-447d</link>
      <guid>https://dev.to/zeroaninea_8bec34a4e7d029/build-a-modern-stack-with-vite-tailwind-i18next-and-tanstack-router-447d</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Source:&lt;/strong&gt; &lt;a href="https://github.com/ZeroaNinea/React-Vite-i18next--tanstack-react-router-Tailwind-Example" rel="noopener noreferrer"&gt;https://github.com/ZeroaNinea/React-Vite-i18next--tanstack-react-router-Tailwind-Example&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Setting up a modern React project can quickly turn into dependency chaos — especially when combining routing, styling, and internationalization.&lt;/p&gt;

&lt;p&gt;In this guide, you'll build a clean, production-ready setup using:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;⚛️ React&lt;/li&gt;
&lt;li&gt;⚡ Vite&lt;/li&gt;
&lt;li&gt;🎨 Tailwind CSS&lt;/li&gt;
&lt;li&gt;🌍 i18next&lt;/li&gt;
&lt;li&gt;🧭 TanStack Router&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  ⚠️ Warning (Version Compatibility)
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;Depending on your Vite version, some dependencies may not install cleanly.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you run into issues:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i &lt;span class="nt"&gt;--force&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or consider using a slightly older version of Vite.&lt;/p&gt;

&lt;h2&gt;
  
  
  🚀 Step 1 — Create the Project
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm create vite@latest React-Vite-i18next-@tanstack/react-router-Tailwind-Example &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="nt"&gt;--template&lt;/span&gt; react-ts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  🎨 Step 2 — Install Tailwind CSS
&lt;/h2&gt;

&lt;p&gt;Install Talwind and its Vite plugin.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i tailwindcss @tailwindcss/vite
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If installation fails:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i &lt;span class="nt"&gt;--force&lt;/span&gt; @tailwindcss/vite
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Configure Vite
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;defineConfig&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;vite&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;react&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@vitejs/plugin-react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;tailwindcss&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@tailwindcss/vite&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nf"&gt;defineConfig&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;react&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nf"&gt;tailwindcss&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;h3&gt;
  
  
  Import Tailwind
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;tailwindcss&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  🧭 Step 3 — Install TanStack Router
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i @tanstack/react-router @tanstack/router-devtools
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  🔌 Step 4 — Integrate the Router
&lt;/h2&gt;

&lt;p&gt;Create:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;src/router.tsx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then connect it in &lt;code&gt;main.tsx&lt;/code&gt;:&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;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;StrictMode&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createRoot&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react-dom/client&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./index.css&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;AppRouter&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./router.tsx&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nf"&gt;createRoot&lt;/span&gt;&lt;span class="p"&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;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;root&lt;/span&gt;&lt;span class="dl"&gt;'&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="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;StrictMode&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;AppRouter&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/StrictMode&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;,
&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  🌍 Step 5 — Add Internationalization (i18next)
&lt;/h2&gt;

&lt;p&gt;Install:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i i18next react-i18next
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Setup i18n
&lt;/h3&gt;

&lt;p&gt;Create:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;src/utils/i18n/index.ts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add your translation JSON files there.&lt;/p&gt;

&lt;p&gt;Then import it once in your app:&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;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./utils/i18n&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, integrate it inside your components (e.g. &lt;code&gt;App.tsx&lt;/code&gt;).&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Stack?
&lt;/h2&gt;

&lt;p&gt;This combination is surprisingly powerful:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Vite:&lt;/strong&gt; instant dev server, fast builds.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tailwind:&lt;/strong&gt; no context switching for styling.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TanStack Router:&lt;/strong&gt; type-safe, modern routing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;i18next:&lt;/strong&gt; scalable internationalization.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Together, they form a minimal but production-ready architecture.&lt;/p&gt;

</description>
      <category>react</category>
      <category>vite</category>
      <category>tutorial</category>
      <category>setup</category>
    </item>
  </channel>
</rss>
