<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Codexa]]></title><description><![CDATA[Codexa]]></description><link>https://blog.krishna.codeistari.com</link><generator>RSS for Node</generator><lastBuildDate>Sat, 02 May 2026 16:25:10 GMT</lastBuildDate><atom:link href="https://blog.krishna.codeistari.com/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Custom Fonts and Colors in Tailwind CSS v4]]></title><description><![CDATA[I started working with Tailwind CSS v4 and immediately noticed how effortlessly it flows compared to v3. If you’ve been building with Tailwind especially in a Vue.js project you’ll likely spot some changes that take a moment to get used to.
Whether y...]]></description><link>https://blog.krishna.codeistari.com/custom-fonts-and-colors-in-tailwind-css-v4</link><guid isPermaLink="true">https://blog.krishna.codeistari.com/custom-fonts-and-colors-in-tailwind-css-v4</guid><category><![CDATA[CSS Theming]]></category><category><![CDATA[Tailwind css v4.0]]></category><category><![CDATA[Vue.js]]></category><category><![CDATA[custom fonts]]></category><dc:creator><![CDATA[Krishna Akbari]]></dc:creator><pubDate>Tue, 22 Jul 2025 18:40:33 GMT</pubDate><content:encoded><![CDATA[<p>I started working with Tailwind CSS v4 and immediately noticed how effortlessly it flows compared to v3. If you’ve been building with Tailwind especially in a Vue.js project you’ll likely spot some changes that take a moment to get used to.</p>
<p>Whether you’re refreshing an old site or starting something brand new, understanding how Tailwind v4 handles fonts and colors will save you a lot of time and frustration</p>
<p>Ready to start? Let’s go!</p>
<ul>
<li><p>What’s new in Tailwind v4 compared to the old version</p>
</li>
<li><p>How to create our colors using the new <code>@theme</code> feature</p>
</li>
<li><p>Adding custom fonts, including Google Fonts</p>
</li>
<li><p>Real examples with Vue.js, and responsive fonts</p>
</li>
</ul>
<h2 id="heading-why-fonts-and-colors-matter">Why Fonts and Colors Matter</h2>
<p>Fonts and colors do more than just make your site look good they set the tone and help people remember who you are. In earlier versions of Tailwind, tweaking these required jumping back and forth between your CSS and a JavaScript config file, which quickly became a hassle.</p>
<p>Tailwind v4 made this a lot simpler. Now you define your fonts and colors right inside your CSS using a new rule called <code>@theme</code>. This means we don’t have to jump around our files anymore. I liked this a lot because everything I need is in one place.</p>
<h2 id="heading-whats-changed-in-tailwind-v4">What’s Changed in Tailwind v4?</h2>
<p>Before (in version 3), you did something like this in your config:</p>
<pre><code class="lang-css"><span class="hljs-selector-tag">module</span><span class="hljs-selector-class">.exports</span> = {
  <span class="hljs-attribute">theme</span>: {
    extend: {
      colors: {
        brand: <span class="hljs-string">"#48A9FA"</span>, // custom blue for our brand
      },
      <span class="hljs-selector-tag">fontFamily</span>: {
        <span class="hljs-attribute">heading</span>: [<span class="hljs-string">"Space Grotesk"</span>, <span class="hljs-string">"sans-serif"</span>], // font for headings
        body: [<span class="hljs-string">"Inter"</span>, <span class="hljs-string">"sans-serif"</span>],            // font for body text
      },
    },
  },
};
</code></pre>
<p>But now in v4, we write this instead, inside our CSS:</p>
<pre><code class="lang-css"><span class="hljs-keyword">@import</span> <span class="hljs-string">"tailwindcss"</span>;

<span class="hljs-keyword">@theme</span> {
    <span class="hljs-selector-tag">--color-primary</span>: <span class="hljs-selector-id">#181E2A</span>;
    <span class="hljs-selector-tag">--font-heading</span>: "<span class="hljs-selector-tag">Space</span> <span class="hljs-selector-tag">Grotesk</span>", "<span class="hljs-selector-tag">sans-serif</span>";
}
</code></pre>
<p>At first, I thought it was strange not to have the JS config, but the CSS approach felt more natural after a while. It lets you see all your theme stuff in one place, and it works better for switching colors for dark mode or different themes.</p>
<h2 id="heading-how-i-created-my-colors">How I Created My Colors</h2>
<p>To set my colors, I put this block in my main CSS file:</p>
<pre><code class="lang-css"><span class="hljs-keyword">@theme</span> {
    <span class="hljs-selector-tag">--color-primary</span>: <span class="hljs-selector-id">#181E2A</span>;
    <span class="hljs-selector-tag">--color-accent</span>: <span class="hljs-selector-id">#48A9FA</span>;
    <span class="hljs-selector-tag">--color-gold</span>: <span class="hljs-selector-id">#FFCF69</span>;
    <span class="hljs-selector-tag">--color-background</span>: <span class="hljs-selector-id">#F5F7FA</span>;
    <span class="hljs-selector-tag">--color-muted</span>: <span class="hljs-selector-id">#8E91A7</span>;
}
</code></pre>
<p>This makes utility classes like bg-primary, text-primary, and border-primary work automatically.</p>
<p>If you want different shades, just add more variables like:</p>
<pre><code class="lang-css"><span class="hljs-selector-tag">--color-primary-100</span>: <span class="hljs-selector-id">#24324a</span>;
<span class="hljs-selector-tag">--color-primary-200</span>: <span class="hljs-selector-id">#2d3b59</span>;
</code></pre>
<p>I also added colors for success, errors, warnings, and info messages:</p>
<pre><code class="lang-css"><span class="hljs-keyword">@theme</span> {
    <span class="hljs-selector-tag">--color-success</span>: <span class="hljs-selector-id">#22c55e</span>;
    <span class="hljs-selector-tag">--color-error</span>: <span class="hljs-selector-id">#ef4444</span>;
    <span class="hljs-selector-tag">--color-warning</span>: <span class="hljs-selector-id">#f59e42</span>;
    <span class="hljs-selector-tag">--color-info</span>: <span class="hljs-selector-id">#3b82f6</span>;
}
</code></pre>
<p>And don’t worry, Tailwind’s default colors still work alongside our custom ones.</p>
<h2 id="heading-how-i-add-fonts-in-style">How I Add Fonts In Style</h2>
<p>Here’s how I usually bring fonts into my Tailwind setup gives the site its own personality:</p>
<p>First, I declare my font variables in my CSS:</p>
<pre><code class="lang-css"><span class="hljs-keyword">@theme</span> {
    <span class="hljs-selector-tag">--font-heading</span>: "<span class="hljs-selector-tag">Space</span> <span class="hljs-selector-tag">Grotesk</span>", "<span class="hljs-selector-tag">sans-serif</span>";
    <span class="hljs-selector-tag">--font-body</span>: "<span class="hljs-selector-tag">Inter</span>", "<span class="hljs-selector-tag">sans-serif</span>";
}
</code></pre>
<p>Then I load the Google fonts in my <code>&lt;head&gt;</code>:</p>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">link</span>
  <span class="hljs-attr">href</span>=<span class="hljs-string">"https://fonts.googleapis.com/css2?
    family=Inter:wght@400;700&amp;
    family=Space+Grotesk:wght@400;700&amp;
    display=swap"</span>
  <span class="hljs-attr">rel</span>=<span class="hljs-string">"stylesheet"</span>
/&gt;</span>
</code></pre>
<p>If you’d rather import directly from your CSS, you can do:</p>
<pre><code class="lang-css"><span class="hljs-keyword">@import</span> url(
  <span class="hljs-string">'https://fonts.googleapis.com/css2?
   family=Inter:wght@400;700&amp;
   family=Space+Grotesk:wght@400;700&amp;
   display=swap'</span>
);
</code></pre>
<p>Want to use a custom font instead? Drop in an <code>@font-face</code>:</p>
<pre><code class="lang-css"><span class="hljs-keyword">@font-face</span> {
    <span class="hljs-attribute">font-family</span>: <span class="hljs-string">'Lobster'</span>;
    <span class="hljs-attribute">src</span>: <span class="hljs-built_in">url</span>(<span class="hljs-string">'/fonts/Lobster-Regular.woff2'</span>) <span class="hljs-built_in">format</span>(<span class="hljs-string">'woff2'</span>);
    <span class="hljs-attribute">font-weight</span>: <span class="hljs-number">400</span>;
    <span class="hljs-attribute">font-style</span>: normal;
}

<span class="hljs-keyword">@theme</span> {
    <span class="hljs-selector-tag">--font-heading</span>: '<span class="hljs-selector-tag">Lobster</span>', <span class="hljs-selector-tag">cursive</span>;
}
</code></pre>
<p>Then use the font classes like this:</p>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">h1</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"font-heading text-4xl"</span>&gt;</span>
  Big, bold headline
<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"font-body text-base"</span>&gt;</span>
  A nice chunk of body text to read.
<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
</code></pre>
<blockquote>
<p>Quick tip: Only load the font weights you actually use — it helps your site load faster.</p>
</blockquote>
<h2 id="heading-some-real-examples-i-use-in-vuejs">Some Real Examples I Use in Vue.js</h2>
<p>This is how I use my custom fonts and colors in a Vue.js component:</p>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">template</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"bg-brand text-white font-heading p-6 rounded-lg max-w-sm mx-auto shadow-lg"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">h1</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-2xl font-bold text-primary mb-2"</span>&gt;</span>Welcome to My Portfolio<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"font-body text-muted mb-4"</span>&gt;</span>
            Hi, I’m a frontend developer passionate about building clean and responsive websites using Vue.js and Tailwind CSS.
        <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"mt-4 bg-accent text-white px-5 py-2 rounded hover:bg-purple transition duration-300"</span>&gt;</span>
            Contact Me
        <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">template</span>&gt;</span>
</code></pre>
<h2 id="heading-responsive-text-easy">Responsive Text? Easy!</h2>
<p>Tailwind makes it super simple to adjust text size for different screen sizes. It just works out of the box. Here’s a real example from a small personal blog header and paragraph that adapt based on the device you’re using:</p>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">h1</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"font-heading text-2xl md:text-3xl lg:text-5xl"</span>&gt;</span>
    My Journey with Tailwind CSS
<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-base md:text-lg"</span>&gt;</span>
    This blog shares tips and tricks I learned switching from Tailwind v3 to v4—responsive and easy going.
<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
</code></pre>
<h2 id="heading-a-few-tips-that-helped-me">A Few Tips That Helped Me</h2>
<ul>
<li><p>Use clear prefixes like --color- and --font- so your theme variables make sense</p>
</li>
<li><p>Keep a simple list of your colors and fonts for yourself and your team</p>
</li>
<li><p>Use Tailwind’s purge tool to remove unused CSS and keep your files small</p>
</li>
<li><p>If you have several projects, share your theme CSS so it’s easier to reuse</p>
</li>
<li><p>Check color contrast for accessibility — it’s important!</p>
</li>
<li><p>Use browser dev tools to check if your fonts and colors apply</p>
</li>
</ul>
<h2 id="heading-to-wrap-up">To Wrap Up</h2>
<p>Switching to Tailwind v4 made styling way easier for me. It took a bit to get used to, but having fonts and colors set inside CSS files feels more natural. Plus, builds are faster, which is always nice. If you’re coming from v3, don’t try to change everything at once start with one component and go from there.</p>
]]></content:encoded></item><item><title><![CDATA[Role-Based Access Control (RBAC) in Vue 3: A Complete Guide]]></title><description><![CDATA[If you’re working on a medium or large Vue 3 project, sooner or later, someone’s going to ask, Can we hide the settings page from normal users? or How come editors see the admin stuff? I’ve been there copy–pasting v-if checks all over the place, and ...]]></description><link>https://blog.krishna.codeistari.com/role-based-access-control-rbac-in-vue-3-a-complete-guide</link><guid isPermaLink="true">https://blog.krishna.codeistari.com/role-based-access-control-rbac-in-vue-3-a-complete-guide</guid><category><![CDATA[rolebasedaccess]]></category><category><![CDATA[Vue.js]]></category><category><![CDATA[frontend]]></category><category><![CDATA[access control]]></category><category><![CDATA[rbac]]></category><dc:creator><![CDATA[Krishna Akbari]]></dc:creator><pubDate>Fri, 04 Jul 2025 14:33:22 GMT</pubDate><content:encoded><![CDATA[<p>If you’re working on a medium or large Vue 3 project, sooner or later, someone’s going to ask, Can we hide the settings page from normal users? or How come editors see the admin stuff? I’ve been there copy–pasting v-if checks all over the place, and honestly, it’s a mess.</p>
<p>So, I finally decided to set up <strong>real role-based access control (RBAC)</strong>. Here, I’ll show you an easy way to set up role-based access control, so users only see what they’re allowed to see.</p>
<h2 id="heading-what-is-rbac">What is RBAC?</h2>
<p>RBAC (role-based access control) just means you group your users by roles, means only certain people can see or perform certain things Like:</p>
<ul>
<li><p><strong>Admin:</strong> Can basically do everything</p>
</li>
<li><p><strong>Editor:</strong> Can mess with content, but not settings</p>
</li>
<li><p><strong>Viewer:</strong> Can only look, not change anything</p>
</li>
</ul>
<p>RBAC helps you keep your app neat and secure, since <strong>all the rules live in one place.</strong></p>
<h2 id="heading-step-1-setup-new-project-fresh-folders">Step 1. Setup: New Project, Fresh Folders</h2>
<p>Open your terminal and run below commands to create and setup a new project:</p>
<pre><code class="lang-bash">npm create vue@latest vue-rbac-app
<span class="hljs-built_in">cd</span> vue-rbac-app
npm install
npm install vue-router@4
</code></pre>
<p>I like keeping things cleaner. Here’s a good structure:</p>
<pre><code class="lang-xml">src/
  views/
  router/
  composables/
  components/
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1753011538565/c4b0bb69-a1e2-4371-8dd8-43ac3ebda8bc.png" alt /></p>
<h2 id="heading-step-2-routing">Step 2. Routing</h2>
<p>Here’s my <code>src/router/index.js</code>. Note the <code>meta</code> field with roles!</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { createRouter, createWebHistory } <span class="hljs-keyword">from</span> <span class="hljs-string">"vue-router"</span>;
<span class="hljs-keyword">import</span> Home <span class="hljs-keyword">from</span> <span class="hljs-string">"../views/Home.vue"</span>;
<span class="hljs-keyword">import</span> Admin <span class="hljs-keyword">from</span> <span class="hljs-string">"../views/Admin.vue"</span>;
<span class="hljs-keyword">import</span> Editor <span class="hljs-keyword">from</span> <span class="hljs-string">"../views/Editor.vue"</span>;
<span class="hljs-keyword">import</span> NotAuthorized <span class="hljs-keyword">from</span> <span class="hljs-string">"../views/NotAuthorized.vue"</span>;

<span class="hljs-keyword">const</span> routes = [
    { 
        <span class="hljs-attr">path</span>: <span class="hljs-string">"/"</span>, 
        <span class="hljs-attr">name</span>: <span class="hljs-string">"Home"</span>, 
        <span class="hljs-attr">component</span>: Home 
    },
    {
        <span class="hljs-attr">path</span>: <span class="hljs-string">"/admin"</span>,
        <span class="hljs-attr">name</span>: <span class="hljs-string">"Admin"</span>,
        <span class="hljs-attr">component</span>: Admin,
        <span class="hljs-attr">meta</span>: { 
            <span class="hljs-attr">requiresAuth</span>: <span class="hljs-literal">true</span>, 
            <span class="hljs-attr">roles</span>: [<span class="hljs-string">"admin"</span>] 
        }
    },
    {
        <span class="hljs-attr">path</span>: <span class="hljs-string">"/editor"</span>,
        <span class="hljs-attr">name</span>: <span class="hljs-string">"Editor"</span>,
        <span class="hljs-attr">component</span>: Editor,
        <span class="hljs-attr">meta</span>: { 
            <span class="hljs-attr">requiresAuth</span>: <span class="hljs-literal">true</span>, 
            <span class="hljs-attr">roles</span>: [<span class="hljs-string">"admin"</span>, <span class="hljs-string">"editor"</span>] 
        }
    },
    { 
        <span class="hljs-attr">path</span>: <span class="hljs-string">"/unauthorized"</span>, 
        <span class="hljs-attr">name</span>: <span class="hljs-string">"Unauthorized"</span>, 
        <span class="hljs-attr">component</span>: NotAuthorized 
    }
];
<span class="hljs-keyword">const</span> router = createRouter(
    { 
        <span class="hljs-attr">history</span>: createWebHistory(), 
        routes 
    }
);

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> router;
</code></pre>
<p>I use <code>createWebHistory()</code> because who wants <code>#/</code> in their URLs.</p>
<h2 id="heading-step-3-fake-auth-for-testing-no-backend-drama-yet">Step 3. Fake Auth for Testing (No Backend Drama Yet)</h2>
<p>Before anyone logs in for real, I just fake my users with a composable.</p>
<p>Make a file, <code>src/composables/useAuth.js</code>:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">useAuth</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-comment">// Change the role to try different stuff!</span>
  <span class="hljs-keyword">return</span> { <span class="hljs-attr">isAuthenticated</span>: <span class="hljs-literal">true</span>, <span class="hljs-attr">role</span>: <span class="hljs-string">'editor'</span> }; <span class="hljs-comment">// try admin/viewer</span>
}
</code></pre>
<h2 id="heading-step-4-setting-up-route-guards">Step 4. Setting Up Route Guards</h2>
<p>Here comes the fun: before any page loads, check if they’re allowed.</p>
<p>Edit <code>src/main.js</code> :</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { createApp } <span class="hljs-keyword">from</span> <span class="hljs-string">"vue"</span>;
<span class="hljs-keyword">import</span> App <span class="hljs-keyword">from</span> <span class="hljs-string">"./App.vue"</span>;
<span class="hljs-keyword">import</span> router <span class="hljs-keyword">from</span> <span class="hljs-string">"./router"</span>;
<span class="hljs-keyword">import</span> { useAuth } <span class="hljs-keyword">from</span> <span class="hljs-string">"./composables/useAuth"</span>;

<span class="hljs-keyword">const</span> app = createApp(App);

<span class="hljs-comment">// Global navigation guard to check authentication and authorization</span>
router.beforeEach(<span class="hljs-function">(<span class="hljs-params">to, <span class="hljs-keyword">from</span>, next</span>) =&gt;</span> {
    <span class="hljs-keyword">const</span> user = useAuth();

    <span class="hljs-keyword">if</span> (to.meta.requiresAuth) {
        <span class="hljs-keyword">if</span> (!user.isAuthenticated) <span class="hljs-keyword">return</span> next(<span class="hljs-string">"/"</span>);

        <span class="hljs-keyword">if</span> (to.meta.roles &amp;&amp; !to.meta.roles.includes(user.role)) {
            <span class="hljs-keyword">return</span> next(<span class="hljs-string">"/unauthorized"</span>);
        }
    }
    next();
});

app.use(router);
app.mount(<span class="hljs-string">"#app"</span>);
</code></pre>
<p>Heads up: This is dead simple for now. Replace with a real store/hooks as you get fancy.</p>
<h2 id="heading-step-5-make-some-pages-to-test">Step 5. Make Some Pages to Test</h2>
<p>Just basic views in <code>/src/views/</code>:</p>
<p><strong>Home.vue</strong></p>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">template</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>
    Welcome to Home Page
  <span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">template</span>&gt;</span>
</code></pre>
<p><strong>Admin.vue</strong></p>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">template</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>
    Admin Dashboard For Admins Only
  <span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">template</span>&gt;</span>
</code></pre>
<p><strong>Editor.vue</strong></p>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">template</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>
    Editor Page - Editors and Admins
  <span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">template</span>&gt;</span>
</code></pre>
<p><strong>NotAuthorized.vue</strong></p>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">template</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>
    Access Denied—You’re Not Allowed Here
  <span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">template</span>&gt;</span>
</code></pre>
<h2 id="heading-step-6-only-show-buttons-for-right-roles">Step 6. Only Show Buttons For Right Roles</h2>
<p>Let’s say only admins can delete stuff.</p>
<p><strong>components/DeleteButton.vue</strong></p>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">template</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"role === 'admin'"</span>&gt;</span>
    Delete Post
  <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">template</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">script</span>&gt;</span><span class="javascript">
<span class="hljs-keyword">import</span> { useAuth } <span class="hljs-keyword">from</span> <span class="hljs-string">'../composables/useAuth'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> {
  <span class="hljs-attr">name</span>: <span class="hljs-string">'DeleteButton'</span>,
  <span class="hljs-attr">computed</span>: {
    <span class="hljs-comment">// get user role from the auth composable</span>
    role() {
      <span class="hljs-keyword">return</span> useAuth().role;
    }
  }
};
</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
</code></pre>
<h2 id="heading-step-7-smarter-sidebar-no-more-dead-links">Step 7. Smarter Sidebar (No More Dead Links)</h2>
<p>No one likes seeing a menu link that just gives you “Access Denied.”<br />Here’s my basic trick:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> sidebarItems = [
    { 
        <span class="hljs-attr">name</span>: <span class="hljs-string">"Home"</span>, 
        <span class="hljs-attr">path</span>: <span class="hljs-string">"/"</span> 
    },
    { 
        <span class="hljs-attr">name</span>: <span class="hljs-string">"Admin Panel"</span>, 
        <span class="hljs-attr">path</span>: <span class="hljs-string">"/admin"</span>, 
        <span class="hljs-attr">roles</span>: [<span class="hljs-string">"admin"</span>] 
    },
    { 
        <span class="hljs-attr">name</span>: <span class="hljs-string">"Edit Articles"</span>, 
        <span class="hljs-attr">path</span>: <span class="hljs-string">"/editor"</span>, 
        <span class="hljs-attr">roles</span>: [<span class="hljs-string">"admin"</span>, <span class="hljs-string">"editor"</span>] 
    }
];

<span class="hljs-keyword">const</span> user = useAuth();
<span class="hljs-keyword">const</span> visibleLinks = sidebarItems.filter(<span class="hljs-function">(<span class="hljs-params">item</span>) =&gt;</span> {
    <span class="hljs-keyword">return</span> !item.roles || item.roles.includes(user.role);
});
</code></pre>
<p><strong>Sidebar.vue</strong></p>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">template</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">ul</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">v-for</span>=<span class="hljs-string">"link in visibleLinks"</span> <span class="hljs-attr">:key</span>=<span class="hljs-string">"link.path"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">router-link</span> <span class="hljs-attr">:to</span>=<span class="hljs-string">"link.path"</span>&gt;</span>{{ link.name }}<span class="hljs-tag">&lt;/<span class="hljs-name">router-link</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">ul</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">template</span>&gt;</span>
</code></pre>
<h2 id="heading-step-8-cleaner-role-checksa-lthasrole-gt-wrapper">Step 8. Cleaner Role Checks—A &lt;HasRole /&gt; Wrapper</h2>
<p>Let’s not repeat ourselves. Here’s a wrapper for role-only content:</p>
<p><strong>components/HasRole.vue:</strong></p>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">template</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">slot</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"hasRole"</span> /&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">template</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">script</span>&gt;</span><span class="javascript">
<span class="hljs-keyword">import</span> { useAuth } <span class="hljs-keyword">from</span> <span class="hljs-string">'../composables/useAuth'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> {
  <span class="hljs-attr">name</span>: <span class="hljs-string">'RoleWrapper'</span>,

  <span class="hljs-attr">props</span>: {
    <span class="hljs-attr">roles</span>: {
      <span class="hljs-attr">type</span>: <span class="hljs-built_in">Array</span>,
      <span class="hljs-attr">required</span>: <span class="hljs-literal">true</span>
    }
  },

  data() {
    <span class="hljs-keyword">return</span> {
      <span class="hljs-attr">user</span>: useAuth()  <span class="hljs-comment">// get current user info</span>
    };
  },

  <span class="hljs-attr">computed</span>: {
    <span class="hljs-comment">// check if user's role is in the allowed roles</span>
    hasRole() {
      <span class="hljs-keyword">return</span> <span class="hljs-built_in">this</span>.roles.includes(<span class="hljs-built_in">this</span>.user.role);
    }
  }
};
</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
</code></pre>
<p><strong>How to use:</strong></p>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">HasRole</span> <span class="hljs-attr">:roles</span>=<span class="hljs-string">"['admin']"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">button</span>&gt;</span>Delete Post<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">HasRole</span>&gt;</span>
</code></pre>
<p>One line and done.</p>
<h2 id="heading-what-next">What Next?</h2>
<ul>
<li><p><strong>Connect real auth:</strong> Firebase, Auth0, or your own backend (Pinia/Vuex to save state)</p>
</li>
<li><p><strong>Add tests:</strong> Because something always breaks</p>
</li>
<li><p><strong>Move role logic out of the router:</strong> If things get big, split it up</p>
</li>
</ul>
<blockquote>
<p>I tried this pattern in both Vue 2 and 3, and it holds up surprisingly well. Just don’t rely on hardcoding roles in production.</p>
</blockquote>
<h2 id="heading-final-thoughts">FINAL THOUGHTS</h2>
<p>Look, RBAC isn’t rocket science. But getting it right early will save you SO much pain as your app grows. Spend 30 minutes setting this up—future-you will be grateful.</p>
]]></content:encoded></item></channel></rss>