The Problem
When using Tailwind CSS, I didn't like the fact that classes for different states and different breakpoints (such as hover, focus, sm, etc) were grouped together in one class attribute in a HTML element. It made it hard to figure out which classes would be active on different states.
The better way, I felt, would be to have a focus
, hover
, sm
, md
, etc attributes for each element so that it was visually easy to figure out what tailwind classes would activate on different states.
The Solution
Since HAML is a Ruby library, I solved the above with a bit of monkey patching. I added the following after my ApplicationHelper
module code in app/helpers/application_helper.rb
:
module Haml
# Haml::AttriubuteParser parses Hash literal to { String (key name) => String (value literal) }.
module AttributeParser
class << self
# @param [String] exp - Old attributes literal or Hash literal generated from new attributes.
# @return [Hash<String, String>,nil] - Return parsed attribute Hash whose values are Ruby literals, or return nil if argument is not a single Hash literal.
def parse(exp)
return nil unless hash_literal?(exp)
hash = Hash.new{"\"\""}
each_attribute(exp) do |key, value|
if key.in?(['sm', 'md', 'lg', 'xl', 'focus', 'hover'])
# Build up string to insert into class attribute
tw_classes = value[1..-2] # Remove the esacped quotes from the original string
.split # Split into array
.map{ |clas| "#{key}:#{clas}" }
.join(" ")
# Insert tw classes string before last escape quote
hash['class'] = hash['class'].insert(-2, " #{tw_classes}")
elsif key == 'class'
hash['class'] = hash['class'].insert(-2, " #{value[1..-2]}")
else
hash[key] = value
end
end
hash
rescue UnexpectedTokenError, UnexpectedKeyError
nil
end
end
end
end
Now, in my views I'm able to style my elements with Tailwind CSS like so:
%input{class: "bg-gray-900 text-white pl-10 pr-4 py-2 rounded-lg w-full",
sm: "bg-red-400 pl-2"
focus: "outline-none bg-white text-gray-900",
hover: "outline-none bg-white text-gray-900",
placeholder: "Search by keywords"}
The Solution Process
I figured this was a parsing problem so after a look at the HAML documentation, I figured I would have to monkey patch HAML::AttributeParser
. Investigating the code further, there was only one method that made sense to patch: the parse
method.
I copied and pasted the code over to my application_helper.rb
and then inserted a raise
to see if my code was affecting the parser and it was.
After a few puts
, each followed by a raise
, I found that attribute values were saved in the hash
with double quotes escaped. Thus, an element with attributes of
{class: "bg-white text-black"}
was saved in the local hash
variable with a key of class
and value of
"\"bg-white text-black\""
Thus, all we have to do was handle the double quotes properly.
When inserting into the key of a hash, we have to insert before the last double quote using .insert(-2, <string>)
.
When handling the value string of an attribute, we have to remove the escaped double quotes using [1..-2]
.
I'm sure there's a more efficient way of achieving the above. If you have a better solution, please let me know by creating a PR / Discussion here.
Top comments (0)