Refactoring Laravel with conditional classes - @class

Refactoring Laravel with conditional classes - @class

— 8 min read

Have you ever taken a second look at your code and felt like you could do it in a better way, maybe cleaner... Or, have you ever looked at an old project after learning new techniques and being like... well... "I could really apply my new knowledge to this and make it 🎵 better faster stronger 🎵".
If the answer is yes then you have the refactoring bug!

What's this all about

In this series of posts (Refactoring Laravel), I will be exploring the sudorealm codebase and refactoring bits of code here and there with new techniques and Laravel findings.

The @class Blade Directive

From the Laravel Docs: The @class directive conditionally compiles a CSS class string. The directive accepts an array of classes where the array key contains the class or classes you wish to add, while the value is a boolean expression. If the array element has a numeric key, it will always be included in the rendered class list:

@php
    $isActive = false;
    $hasError = true;
@endphp
 
<span @class([
    'p-4',
    'font-bold' => $isActive,
    'text-gray-500' => ! $isActive,
    'bg-red' => $hasError,
])></span>
 
<span class="p-4 text-gray-500 bg-red"></span>

So what you are telling me is, that p-4 will always be rendered and then the we get the other classes based on the value of the boolean.

"Well... ok fair enough, that's cool and all but where could I implement this?" Was my initial thought, therefore I started scribbling down the Sudorealm code until I found the following snippet.

@if ($hasCrowned)

{{-- CROWNED --}}
<button wire:click.prevent="crown"
    class="flex items-center justify-center w-10 h-10 text-yellow-400 transition duration-150 ease-in bg-indigo-500 border border-indigo-500 rounded-full shadow cursor-pointer hover:bg-indigo-50 hover:shadow-none hover:text-indigo-200">
    <svg class="w-5 h-auto fill-current" viewBox="0 0 24 24">
        <path fill-rule="evenodd" clip-rule="evenodd"
            d="M2.5 6.09143L7.21997 10.8114L12.0005 6.03088L16.7811 10.8114L21.5 6.09245V14.9691C21.5 16.626 20.1569 17.9691 18.5 17.9691H5.5C3.84314 17.9691 2.5 16.626 2.5 14.9691V6.09143ZM19.5 10.9087V14.9691C19.5 15.5214 19.0523 15.9691 18.5 15.9691H5.5C4.94771 15.9691 4.5 15.5214 4.5 14.9691V10.9077L7.21997 13.6277L12.0005 8.84717L16.7811 13.6277L19.5 10.9087Z" />
    </svg>
</button>

@else

{{-- NOT CROWNED --}}
<button wire:click.prevent="crown"
    class="flex items-center justify-center w-10 h-10 transition duration-150 ease-in border border-indigo-500 rounded-full shadow cursor-pointer bg-indigo-50 hover:bg-indigo-500 hover:shadow-none hover:text-yellow-400">
    <svg class="w-5 h-auto fill-current" viewBox="0 0 24 24">
        <path fill-rule="evenodd" clip-rule="evenodd"
            d="M2.5 6.09143L7.21997 10.8114L12.0005 6.03088L16.7811 10.8114L21.5 6.09245V14.9691C21.5 16.626 20.1569 17.9691 18.5 17.9691H5.5C3.84314 17.9691 2.5 16.626 2.5 14.9691V6.09143ZM19.5 10.9087V14.9691C19.5 15.5214 19.0523 15.9691 18.5 15.9691H5.5C4.94771 15.9691 4.5 15.5214 4.5 14.9691V10.9077L7.21997 13.6277L12.0005 8.84717L16.7811 13.6277L19.5 10.9087Z" />
    </svg>
</button>

@endif

👹 It's a monstrosity of a code, and it does a very simple thing actually. The logic follows: "Has the user crowned the post? If yes show the crown button with the appropriate CSS to let them know". That's it... If you've roamed around the realm a bit you should have come across it, it looks something like this,

crowning animation on sudorealm

We should refactor that monster to something cleaner! We have the refactoring bug after all!

Refactoring monster code!

Let's think about it a little... What differs between the two classes?

class="flex items-center justify-center w-10 h-10 text-yellow-400 transition duration-150 ease-in bg-indigo-500 border border-indigo-500 rounded-full shadow cursor-pointer hover:bg-indigo-50 hover:shadow-none hover:text-indigo-200"

class="flex items-center justify-center w-10 h-10 transition duration-150 ease-in border border-indigo-500 rounded-full shadow cursor-pointer bg-indigo-50 hover:bg-indigo-500 hover:shadow-none hover:text-yellow-400"

{{-- CROWNED
   text-yellow-400 
   bg-indigo-500
   border-indigo-500
   hover:bg-indigo-50 
   hover:text-indigo-200
--}}

{{-- NOT CROWNED
   border-indigo-500
   bg-indigo-50 
   hover:bg-indigo-500  
   hover:text-yellow-400
--}}

These are the classes that differ from the two buttons that we want to refactor to one.

Next step is to apply the @class conditional directive just like the docs showed as how.

@class([
    'flex items-center justify-center w-10 h-10 transition 
     duration-150 ease-in  border rounded-full shadow cursor-pointer hover:shadow-none',

    'text-yellow-400 bg-indigo-500 border-indigo-500 
     hover:bg-indigo-50 hover:text-indigo-200' 
            => $hasCrowned,

     'border-indigo-500 bg-indigo-50 hover:bg-indigo-500 hover:text-yellow-400 ' 
            => ! $hasCrowned
    ])

The final result is,

<button wire:click.prevent="crown"
   @class([
    'flex items-center justify-center w-10 h-10 transition 
     duration-150 ease-in  border rounded-full shadow cursor-pointer hover:shadow-none',

    'text-yellow-400 bg-indigo-500 border-indigo-500 
     hover:bg-indigo-50 hover:text-indigo-200' 
            => $hasCrowned,
     'border-indigo-500 bg-indigo-50 hover:bg-indigo-500 hover:text-yellow-400 ' 
            => ! $hasCrowned
    ])>
    <svg class="w-5 h-auto fill-current" viewBox="0 0 24 24">
        <path fill-rule="evenodd" clip-rule="evenodd"
            d="M2.5 6.09143L7.21997 10.8114L12.0005 6.03088L16.7811 10.8114L21.5 6.09245V14.9691C21.5 16.626 20.1569 17.9691 18.5 17.9691H5.5C3.84314 17.9691 2.5 16.626 2.5 14.9691V6.09143ZM19.5 10.9087V14.9691C19.5 15.5214 19.0523 15.9691 18.5 15.9691H5.5C4.94771 15.9691 4.5 15.5214 4.5 14.9691V10.9077L7.21997 13.6277L12.0005 8.84717L16.7811 13.6277L19.5 10.9087Z" />
    </svg>
</button>

That's uncomperably cleaner, don't you agree? Well if you agree, I can't argue with you... but let's go a step further shall we? We are writing code with a razor sharp blade after all!

Let's slice the ugly SVG code and put it into its own component inside a folder named SVG and let's call the component crown 👑.

<button wire:click.prevent="crown"
   @class([
    'flex items-center justify-center w-10 h-10 transition 
     duration-150 ease-in  border rounded-full shadow cursor-pointer hover:shadow-none',

    'text-yellow-400 bg-indigo-500 border-indigo-500 
     hover:bg-indigo-50 hover:text-indigo-200' 
            => $hasCrowned,
     'border-indigo-500 bg-indigo-50 hover:bg-indigo-500 hover:text-yellow-400 ' 
            => ! $hasCrowned
    ])>
    <x-svg.crown/>
</button>

Now we are talking! Now we have beautifully refactored the code and have successfully implemented the very helpful @class blade directive. Our Job is finished! End of article. Full Stop. Close the curtains, and thank you all for coming to the show 🎻👏!!!
Except if... We could maybe just do some...

More Refactoring!

Yes, yes... some more... This button could really become a reusable component, if we play our cards right.
There is a folder in the Sudorealm codebase for button components, so if we created a new component file named crown under that folder we would be able to call it like,

<x-button.crown />

Just like that, but I wouldn't say that wire:click.prevent="crown"is really reusable, so we will just use the Blade Component Attributes. Our final component code will look like this,

<button
    {{ $attributes }}
    @class([
        'flex items-center justify-center w-10 h-10 transition duration-150 ease-in  border rounded-full shadow cursor-pointer hover:shadow-none',
        'text-yellow-400 bg-indigo-500 border-indigo-500 hover:bg-indigo-50 hover:text-indigo-200' 
            => $hasCrowned,
        'border-indigo-500 bg-indigo-50 hover:bg-indigo-500 hover:text-yellow-400 ' 
            => ! $hasCrowned
    ])
    >
    <x-svg.crown />
</button>

and now we can call our new fully refactored button like this,

<x-button.crown wire:click.prevent="crown" 
                :hasCrowned="$hasCrowned" />

a true sight for sore eyes.

Get Better at Laravel!

Do you know what is the best way to learn Laravel? Laracasts of course! What else?

Why Every Dev is Raving About Laracasts:

🚀 All-Star Cast: Learn Laravel from the best! We're talking the creator of Laravel, Taylor Otwell, the genius behind Livewire, Caleb Porzio, and more Laravel legends.

🛠️ Full Toolkit: It’s not just Laravel! Dive deep into Livewire, Alpine.js, and the entire Laravel ecosystem and its intricacies.

🔄 Always Fresh: With direct insights from industry leaders, you’re getting content that’s not just up-to-date, but ahead of its time.

💡 Real-World Ready: Theory’s cool, but practical knowledge? Priceless. Jump from lesson to project seamlessly.

👩‍💻 Join the Elite: Step into a community that’s all about pushing boundaries and perfecting the craft.

Bottom line: Want to level up fast? Laracasts is your jam. Dive in using my referral link and join the coding revolution. 🔗 Laracasts

Conclusion

This is the first Laravel post of many and many to come, from simple quality changes to entire reusable livewire components. I hope that I will manage to cover a great variety of tips and tricks of the TALL Framework.

🚀 Spread the Love & Support the Realm

Hey there, fellow Realmer! If this guide illuminated a new path in your coder/hacker journey, your support would mean a lot. Every bit of magic helps.

Spread the Love

👑 Crown & Share: If you found value in this post, please give it a crown and share it with your fellow coder/hacker enthusiasts. Spreading knowledge is what Sudorealm is all about! Fun fact the Author with the most crowns inside a realm will be crowned as the Realm King! 🤴

🆇 X Shoutout: Feeling extra grateful or have some cool feedback? Drop me a shoutout on Twitter – I'd love to hear from you! d3adR1nger on X

💬 Join our Discord Server: Join the Sudorealm Discord Server connect with fellow enthusiasts and chat about everything that fascinates you! From new blog post suggestions to seeking support on tricky tutorials. Come, share your ideas, and let's grow together! 🚀🌐

Support the Realm

🛍 Affiliate Treasures Below: Dive into the depths below the post to uncover some affiliate products I've curated just for you. It's a great way to support the realm and discover some nerdy treasures.

☕️ Coffee Driven Development: Love what you're reading? Fuel my passion for coding with a delicious cup of coffee! Every sip powers up another line of code and helps bring more exciting content your way. Support my caffeine-fueled coding adventures and let's brew up something amazing together! ☕👨‍💻 Join the journey and BuyMeACoffee

d3ad R1nger buymeacoffee

Thanks for being a part of our realm. Every bit of support propels our community to new horizons. Until next time, keep exploring!

Affiliate Links

Check out what d3ad R1nger suggests for Refactoring Laravel with conditional classes - @class!