<?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: Karelle Hofler</title>
    <description>The latest articles on DEV Community by Karelle Hofler (@itskarelleh).</description>
    <link>https://dev.to/itskarelleh</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%2F557303%2Fed2ad15e-c0c0-45de-8e65-37169a6d592a.png</url>
      <title>DEV Community: Karelle Hofler</title>
      <link>https://dev.to/itskarelleh</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/itskarelleh"/>
    <language>en</language>
    <item>
      <title>How I Built and Published My First NPM Package in 3 Weeks</title>
      <dc:creator>Karelle Hofler</dc:creator>
      <pubDate>Wed, 10 Dec 2025 16:00:00 +0000</pubDate>
      <link>https://dev.to/itskarelleh/my-3-week-journey-to-publish-my-first-npm-package-2184</link>
      <guid>https://dev.to/itskarelleh/my-3-week-journey-to-publish-my-first-npm-package-2184</guid>
      <description>&lt;p&gt;Sometimes the smallest changes can throw your whole project into chaos. For me, that moment came with SoundCloud.&lt;/p&gt;

&lt;p&gt;I was using SoundCloud for music integration in my major project, VibeShift, and everything was running smoothly—until one day, an API update changed everything. SoundCloud was migrating to HLS (HTTP Live Streaming) for their music, and while progressive downloads (MP3s, WAVs, etc.) were fine before, the update broke them.&lt;/p&gt;

&lt;p&gt;I found myself scrambling to get a working solution. Nine hours later, I had something semi-functional—but it wasn’t sustainable. Most solutions I found online were either outdated, confusing, overly bloated, or not maintained. I realized: I didn’t want to rely on someone else’s unstable library. &lt;/p&gt;

&lt;p&gt;That’s when I started Oddysee, my own HLS audio player library.&lt;/p&gt;

&lt;h2&gt;
  
  
  Planning &amp;amp; Architecture: Week One
&lt;/h2&gt;

&lt;p&gt;The first week was all about thinking long-term. I asked myself:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Do I make this React-only?&lt;/li&gt;
&lt;li&gt;Should it eventually support Vue or Svelte?&lt;/li&gt;
&lt;li&gt;What dependencies do I actually need?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I landed on a core TypeScript package, framework-agnostic so anyone could build a wrapper for their preferred framework. Since I work with React, I started with a React hook version.&lt;/p&gt;

&lt;p&gt;The planning phase was crucial. I wanted Oddysee to be something I could trust, maintain, and eventually let others contribute to—without being tied to my personal brand.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building Phase: Weeks Two &amp;amp; Three
&lt;/h2&gt;

&lt;p&gt;Implementing HLS was hard as hell. HLS.js is a huge low-level library with tons of configurations, and wrapping it in a clean, readable API was tricky.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Challenges included:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Designing an intuitive, readable API&lt;/li&gt;
&lt;li&gt;Testing it in example projects and in VibeShift locally&lt;/li&gt;
&lt;li&gt;Dealing with edge cases I didn’t anticipate&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At one point, I came across Verdaccio, which is a really cool way to test your npm packages locally, but that’s a story for another article.&lt;/p&gt;

&lt;h2&gt;
  
  
  Publishing Oddysee
&lt;/h2&gt;

&lt;p&gt;After three weekends of deep work, it was time to publish. And then reality hit: naming and scoping an NPM package is way harder than it looks.&lt;/p&gt;

&lt;p&gt;I thought I could just publish a scope and be done—but finding a unique name took hours. Eventually, I landed on Oddysee. Plus, every other variation I tried was already taken. And when that failed, I decided to make it unscoped. &lt;/p&gt;

&lt;p&gt;I also made the conscious decision not to attach my full name to the project. Not out of fear—rather, but to allow Oddysee to live independently, and eventually let someone else maintain or contribute without friction.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reflection &amp;amp; Next Steps
&lt;/h2&gt;

&lt;p&gt;Now that Oddysee is live, I can honestly say I feel confident about its next phase: allowing contributions, improving long-term stability, and filling a gap that HLS libraries currently have for developers. Adding audio is already hard but with music and podcast providers moving over to HLS I want the adoption to be easier compared to what I went through.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What I learned:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Outdated libraries are a risk—even if “everything works” for now.&lt;/li&gt;
&lt;li&gt;Planning for long-term maintainability pays off.&lt;/li&gt;
&lt;li&gt;Building a framework-agnostic core makes contributions easier.&lt;/li&gt;
&lt;li&gt;Naming and publishing can sometimes be harder than coding.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Oddysee might be a small solution, but it’s built to last—and I can’t wait to see how it grows.&lt;/p&gt;

&lt;p&gt;

&lt;/p&gt;
&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/itskarelleh" rel="noopener noreferrer"&gt;
        itskarelleh
      &lt;/a&gt; / &lt;a href="https://github.com/itskarelleh/oddysee-hls-audio-player" rel="noopener noreferrer"&gt;
        oddysee-hls-audio-player
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Oddysee is an HLS audio library that gives control back to the developer. Built with TypeScript and HLS.js
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Oddysee - HLS Audio Player&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;A library API that wraps around HLS.js for a better developer experience. Because audio streaming shouldn't require a PhD in streaming protocols.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;
⚠️ Status: Beta&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;Oddysee is actively evolving. APIs may change, and breaking changes can occur between minor releases.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Background&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;I built this because I needed an audio player that supported HLS, but all the solutions I came across were either bloated or not maintained.
Then I discovered HLS.js. It saved me but it wasn't easy! The developer experience feels like you're configuring a spaceship when all you want to do is play some music.&lt;/p&gt;
&lt;p&gt;This library wraps all that complexity into a simple, audio-focused API that actually makes sense.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Packages&lt;/h2&gt;

&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/itskarelleh/oddysee-hls-audio-player/./packages/oddysee/typescript/README.md" rel="noopener noreferrer"&gt;oddysee-typescript&lt;/a&gt;&lt;/strong&gt; - Core player library (framework-agnostic)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/itskarelleh/oddysee-hls-audio-player/./packages/oddysee/react/README.md" rel="noopener noreferrer"&gt;oddysee-react&lt;/a&gt;&lt;/strong&gt; - React hooks wrapper&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Installation&lt;/h2&gt;

&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Core (Vanilla JS, or any framework)&lt;/h3&gt;

&lt;/div&gt;
&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;npm install oddysee-typescript
&lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; or&lt;/span&gt;
yarn add oddysee-typescript
&lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; or&lt;/span&gt;
pnpm add oddysee-typescript&lt;/pre&gt;

&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;React Hook&lt;/h3&gt;…&lt;/div&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/itskarelleh/oddysee-hls-audio-player" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;




</description>
      <category>npm</category>
      <category>software</category>
      <category>devjournal</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Build a Custom Context Menu with Tailwind and JavaScript</title>
      <dc:creator>Karelle Hofler</dc:creator>
      <pubDate>Sat, 25 Nov 2023 21:25:02 +0000</pubDate>
      <link>https://dev.to/itskarelleh/build-a-custom-context-menu-with-tailwind-and-javascript-10hb</link>
      <guid>https://dev.to/itskarelleh/build-a-custom-context-menu-with-tailwind-and-javascript-10hb</guid>
      <description>&lt;p&gt;Sometimes when you have a project that is so great and the theme is just so darn amazing, you want everything about the theme to be on point. Or maybe you would like to give your users more options when they right-click a certain element in your app. So how do you customize the context menu? When you right-click on a page in your web browser, what's the menu you see? Since it's built-in you have to do a bit of work around but It's a lot easier than it seems. In this article, you will build a custom context menu using Tailwind.css and JavaScript. I will also be using the icon library Iconoir(it's cute )&lt;/p&gt;

&lt;p&gt;Step One: Build and Style the Context Menu&lt;br&gt;
First, set up your document. Here's how your HTML document should be set up to add Tailwind and Iconoir.&lt;/p&gt;

&lt;p&gt;If your project is being done locally on your machine don't forget to add &amp;lt;!DOCTYPE html&amp;gt; but if you're using something like Codepen or iCodeThis like I am, it's not necessary.&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;html&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;charset=&lt;/span&gt;&lt;span class="s"&gt;"UTF-8"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"viewport"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"width=device-width, initial-scale=1.0"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="c"&gt;&amp;lt;!--Iconoir--&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"stylesheet"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://cdn.jsdelivr.net/gh/iconoir-icons/iconoir@main/css/iconoir.css"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="c"&gt;&amp;lt;!-- Tailwind.css --&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://cdn.tailwindcss.com"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
    &lt;span class="c"&gt;&amp;lt;!-- Config to add dark mode --&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
        &lt;span class="nx"&gt;tailwind&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;darkMode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;class&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;style &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text/tailwindcss"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;/style&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;body&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"bg-gray-300 dark:bg-gray-900 text-gray-800 dark:text-gray-100 flex justify-center items-center min-h-screen overflow-hidden"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Right-click to see menu&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
    &lt;span class="c"&gt;&amp;lt;!--context menu goes here --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This template includes the CDN to Tailwind, the CDN to Iconoir, the tailwind config that adds dark mode, and a style tag with the type "text/tailwindcss" to add further customization with Tailwind. Now that your document is set up, let's build the layout for the context menu.&lt;/p&gt;

&lt;p&gt;You need three things:&lt;/p&gt;

&lt;p&gt;A parent div for hiding and showing the context menu&lt;/p&gt;

&lt;p&gt;ul - the actual context menu container&lt;/p&gt;

&lt;p&gt;li - the context menu item with its own label and action&lt;/p&gt;

&lt;p&gt;The context menu will have only three items; new tab option, dark mode toggle, and print option.&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;id=&lt;/span&gt;&lt;span class="s"&gt;"contextMenu"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"hidden absolute"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;ul&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"menu flex flex-col rounded-md shadow-xl overflow-hidden"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="c"&gt;&amp;lt;!--Opens a new tab on click--&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;li&lt;/span&gt; &lt;span class="na"&gt;onclick=&lt;/span&gt;&lt;span class="s"&gt;"window.open('/code/104')"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"context-menu-item"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;i&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"iconoir-open-in-window mr-1"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/i&amp;gt;&lt;/span&gt;
            Open in New Tab
        &lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
        &lt;span class="c"&gt;&amp;lt;!-- toggles light mode and dark mode--&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;li&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"dark-toggle"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"context-menu-item"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;i&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"iconoir-half-moon mr-1"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/i&amp;gt;&lt;/span&gt;
            Turn On Dark Mode
        &lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
        &lt;span class="c"&gt;&amp;lt;!--Opens the print page option--&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;li&lt;/span&gt; &lt;span class="na"&gt;onclick=&lt;/span&gt;&lt;span class="s"&gt;"window.print()"&lt;/span&gt;
        &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"context-menu-item"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
           &lt;span class="nt"&gt;&amp;lt;i&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"iconoir-printer"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/i&amp;gt;&lt;/span&gt;
           Print
         &lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/ul&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;Each item in the context menu will have the same exact style so for convenience, add the following code to the style tag.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;style &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text/tailwindcss"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;@layer&lt;/span&gt; &lt;span class="n"&gt;components&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;.context-menu-item&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="err"&gt;@apply&lt;/span&gt; &lt;span class="err"&gt;cursor-pointer&lt;/span&gt; &lt;span class="err"&gt;bg-white&lt;/span&gt; &lt;span class="py"&gt;dark&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;bg-gray-800&lt;/span&gt; &lt;span class="n"&gt;hover&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;bg-gray-200&lt;/span&gt; &lt;span class="n"&gt;transition-all&lt;/span&gt; &lt;span class="n"&gt;ease-linear&lt;/span&gt; &lt;span class="n"&gt;dark&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;hover&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;bg-gray-800&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="m"&gt;50&lt;/span&gt; &lt;span class="n"&gt;p-4&lt;/span&gt; &lt;span class="n"&gt;w-full&lt;/span&gt; &lt;span class="n"&gt;h-full&lt;/span&gt; &lt;span class="n"&gt;text-gray-800&lt;/span&gt; &lt;span class="n"&gt;dark&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;text-gray-200&lt;/span&gt; &lt;span class="nb"&gt;relative&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="nt"&gt;&amp;lt;/style&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the styles have been added, it is now time to add functionality to the context menu.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step Two: Adding Functionality with JavaScript
&lt;/h2&gt;

&lt;p&gt;In order for this to work, you will first need to disable the built-in context menu. You can't modify so hide it and then implement a function so that when the user right-clicks on the page, the custom context menu is displayed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;//hides the menu if its open&lt;/span&gt;
&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onclick&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;hideMenu&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;//opens the context menu if already &lt;/span&gt;
&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;oncontextmenu&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;rightClick&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;hideMenu&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;menu&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="s2"&gt;hidden&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;menu&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="s2"&gt;block&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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;showMenu&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;menu&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="s2"&gt;block&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;menu&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="s2"&gt;hidden&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="c1"&gt;//Opens the context menu&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;rightClick&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="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;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;menu&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;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;block&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;hideMenu&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Calculate the dimensions of the menu&lt;/span&gt;
        &lt;span class="nx"&gt;menu&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;display&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;block&lt;/span&gt;&lt;span class="dl"&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;menuWidth&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;menu&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;menuHeight&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;menu&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;offsetHeight&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nx"&gt;menu&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;display&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="c1"&gt;// Determine position for the menu&lt;/span&gt;
        &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;posX&lt;/span&gt; &lt;span class="o"&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;pageX&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;posY&lt;/span&gt; &lt;span class="o"&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;pageY&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="c1"&gt;// Check if the menu goes beyond the right edge of the window&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;posX&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;menuWidth&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerWidth&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;posX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerWidth&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;menuWidth&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="c1"&gt;// Check if the menu goes beyond the bottom edge of the window&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;posY&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;menuHeight&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHeight&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;posY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHeight&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;menuHeight&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="c1"&gt;// Set the position of the menu&lt;/span&gt;
        &lt;span class="nx"&gt;menu&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="nx"&gt;posX&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;px&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nx"&gt;menu&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="nx"&gt;posY&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;px&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="nf"&gt;showMenu&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;So what's happening here is that when the user right-clicks on the page, it first checks if the context menu is displayed or not. If it is, it will hide the menu and if it's not it will show the menu but before it shows the menu it does the following:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Calculating the dimension of the context menu - The positioning is being found from where the user right-clicked on the page&lt;/li&gt;
&lt;li&gt;Make sure that the context menu does not go out of bounds so if the user right-clicked close to the edge it stays within the bounds of the page &lt;/li&gt;
&lt;li&gt; Set the position of the menu &lt;/li&gt;
&lt;li&gt;Finally, show the menu&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The Results&lt;br&gt;
You should get something like the following in this code pen.&lt;br&gt;
&lt;iframe height="600" src="https://codepen.io/itskarelleh/embed/JjxORMM?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Customizing a context menu for your web application can enhance user experience and provide additional functionality. By following the steps outlined in this article, you've learned how to create a custom context menu using Tailwind.css and JavaScript.&lt;/p&gt;

</description>
      <category>css</category>
      <category>tailwindcss</category>
      <category>html</category>
      <category>javascript</category>
    </item>
  </channel>
</rss>
