-
-
Notifications
You must be signed in to change notification settings - Fork 4
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Proposal for changes #9
Comments
Hey @javierav Awesome suggestions! Thanks for taking the time to fill this in.
For 2. I think we should explore a new API. Maybe something that resembles VC slots? modal_classes = ClassVariants.build(
base: "inline-flex items-center rounded",
variants: {
color: {
red: "bg-red-600",
blue: "bg-blue-600",
},
border: "border"
}
) do |classes|
classes.title "text-sm" # I'm not sure this is possible
# If not, maybe this?
classes.title do
"text-sm"
end
end I'm open to suggestions for this. How about we start with PRs for the small ones like 1. and 3. and open new issues for the next ones to track and start working after? |
Thanks Adrian for the quick response! Regarding your proposal in point 2, the way to specify the conditions on which these classes should apply, whether they are variants or composite variants, is missing, although it gives me the opportunity to comment on a point that I had left out of the previous list but that I am using right now in my application: using a code block to define the configuration instead of a hash. ClassVariants.build do
base <<~BASE
inline-flex items-center justify-center gap-1 rounded-md font-semibold disabled:cursor-not-allowed disabled:opacity-30
BASE
variants do
color do
white "bg-white text-gray-900"
end
size do
xs "text-xs px-2 py-1"
sm "text-sm px-2 py-1"
md "text-base px-2.5 py-1.5"
end
solid "shadow-sm"
uppercase "uppercase"
end
defaults do
border true
color :white
size :md
solid true
end
end If we adapt it to use the new slots, it could look something like this: ClassVariants.build do
base do
title "..."
body "..."
end
variants do
color do
white do
title "..."
body "..."
end
black do
title "..."
body "..."
end
end
end
end Although this leads me to reintroduce a possibility that I had not initially contemplated in the suggested changes: unifying the variants and the compound variants into a single configuration: ClassVariants.build do
base do
container "rounded py-3 px-5 mb-4",
title "font-bold mb-1"
end
variant type: :outlined do
container "border"
end
variant type: :outlined, severity: :error do
container "border-red-700 dark:border-red-500"
title "text-red-700 dark:text-red-500"
message "text-red-600 dark:text-red-500"
end
variant type: :outlined, severity: :success do
container "border-green-700 dark:border-green-500"
title "text-green-700 dark:text-green-500"
message "text-green-600 dark:text-green-500"
end
variant type: :filled, severity: :error do
container "border-red-700 dark:border-red-500"
title "text-red-700 dark:text-red-500"
message "text-red-600 dark:text-red-500"
end
variant type: :filled, severity: :success do
container "bg-green-100 dark:bg-green-800"
title "text-green-900 dark:text-green-50"
message "text-green-700 dark:text-green-200"
end
end Thus, for the options And if we don't need to work with slots because it is a simple component, it could look like this: ClassVariants.build do
base "inline-block w-2.5 h-2.5 rounded-md"
variant color: :green, class: "bg-green-300 border-green-400"
variant color: :yellow, class: "bg-amber-300 border-amber-400"
variant color: :red, class: "bg-red-300 border-red-400"
end Or ClassVariants.build do
base "inline-block w-2.5 h-2.5 rounded-md"
variant color: :green do
"bg-green-300 border-green-400"
end
variant color: :yellow do
"bg-amber-300 border-amber-400"
end
variant color: :red do
"bg-red-300 border-red-400"
end
end Or implement for these simple cases the ClassVariants.build do
base "inline-block w-2.5 h-2.5 rounded-md"
variants do
color do
green "bg-green-300 border-green-400"
yellow "bg-amber-300 border-amber-400"
red "bg-red-300 border-red-400"
end
end
end And leave the
On this subject, in my humble opinion, I have the feeling that the way in which we define the structure of point 2 will condition the implementation in code of everything else, especially because of the issue of slots, so I would say that until we have a structure with which we feel comfortable we should not start working on the rest. |
Thanks for thinking this through. Regarding simple, non-slot scenarios, I presume we'll still keep the current (hash) configuration. ClassVariants.build do
base "inline-block w-2.5 h-2.5 rounded-md"
variant color: :green, class: "bg-green-300 border-green-400"
variant color: :yellow, class: "bg-amber-300 border-amber-400"
variant color: :red, class: "bg-red-300 border-red-400"
end I wouldn't open up block just to pass strings inside if we could do it using a kw argument. Now, if we use # instead of
variant type: :outlined, severity: :error do
container "border-red-700 dark:border-red-500"
title "text-red-700 dark:text-red-500"
message "text-red-600 dark:text-red-500"
end
# we add `class:`
variant type: :outlined, severity: :error do
container class: "border-red-700 dark:border-red-500"
title class: "text-red-700 dark:text-red-500"
message class: "text-red-600 dark:text-red-500"
end Regarding composed variants ( # this should work
variant type: :outlined, severity: :error do
container class: "border border-red-700 dark:border-red-500"
title class: "border text-red-700 dark:text-red-500"
message class: "border text-red-600 dark:text-red-500"
end
# this should work and give the same result, right?
variant type: :outlined do
container class: "border"
title class: "border"
message class: "border"
end
variant severity: :error do
container class: "border-red-700 dark:border-red-500"
title class: "text-red-700 dark:text-red-500"
message class: "text-red-600 dark:text-red-500"
end |
I think the same too.
In this case I see it as redundant to have to pass the class as a kw argument when it is the only parameter you are going to have to pass. Would it make sense to do something like this? variant type: :outlined, severity: :error do
slot :container, class: "border-red-700 dark:border-red-500"
slot :title, class: "text-red-700 dark:text-red-500"
slot :message, class: "text-red-600 dark:text-red-500"
end By the way, we are making it clear that what we are defining is a slot and the class it should have.
In this example you give, if several variant definitions meet the criteria, their classes must be mixed in the final result. |
Wow! variant type: :outlined, severity: :error do
slot :container, class: "border-red-700 dark:border-red-500"
slot :title, class: "text-red-700 dark:text-red-500"
slot :message, class: "text-red-600 dark:text-red-500"
end It's because:
ClassVariants.build do
base "inline-block w-2.5 h-2.5 rounded-md"
variant color: :green, class: "bg-green-300 border-green-400"
variant color: :yellow, class: "bg-amber-300 border-amber-400"
variant color: :red, class: "bg-red-300 border-red-400"
end |
Awesome work @javierav! |
@adrianthedev this is how the configuration possibilities would look after what we have been talking about so far: ClassVariants.build(
"...", # positional arg, print deprecation warning
base: "...",
variants: {
color: {
red: "...",
black: "..."
},
type: {
button: "...",
link: "..."
}
},
compoundVariants: [], # supported? print deprecation warning?
defaults: {
color: :red,
type: :button
}
) do
# base without slots
base "..." # override hash definition? merge with it?
# base with slots
base do
slot :head, class: "..."
slot :body, class: "..."
end
# variant without slots
variant color: :red, class: "..."
# variant with slots
variant color: :red do
slot :head, class: "..."
slot :body, class: "..."
end
# compound variant without slots
variant type: :button, color: :red, class: "..."
# compound variant with slots
variant type: :button, color: :red do
slot :head, class: "..."
slot :body, class: "..."
end
# option 1 (my favorite)
defaults color: :red, type: :button
# option 2
defaults do
color :red
type :button
end
end Decisions must be made based on this structure:
I don't know if it makes sense to leave the hash option or go directly to the code block option that allows you to do more things in a more elegant way. |
We also have to make decisions when calling the alert_classes = ClassVariants.build ...
# render default slot with default variants
# but whats happen if slots are used in definition?
# at this point we don't know which slot render...
# throw error? render empty string?
alert_classes.render
# render default slot with custom variants
alert_classes.render(color: :red)
# render slot with defaults variants
alert_classes.render(:body)
# render slot with custom variants
alert_classes.render(:body, color: :red)
# if slot not exist, throw error? return empty classes?
alert_classes.render(:non_existent_slot, color: :red)
# render default slot with custom class (will be merged)
alert_classes.render(class: "...")
# render slot with custom class (will be merged)
alert_classes.render(:body, class: "...") What is clear to me is that all the previous calculations must be done at the time of definition with We also need to define how to configure Tailwind Merge. My proposal: ClassVariants.configure do |config|
config.tw_merge = true # by default is false
config.tw_merge_config = { ... } # by default is nil
end If the option to use Tailwind Merge is not enabled, the library will not be initialized, so we avoid directly depending on that gem. |
Thanks for this summarization @javierav!
# declaring a default option
variant color: :red, class: "...", default: true # memoization somewhere in the `render` method
def render
return @result if @result.present?
# do things to figure out the classes
result[:default] = "bg-red-500..."
result[:some_slot] = "border..."
@result = result
end |
@adrianthedev Yeah! I'm going to start working on it! |
@adrianthedev I think all the tasks have been completed. The PRs are in draft form because they will have to be updated as they are merged, as many of them are dependent on each other.
|
@javierav. You are a machine 🔥 Thanks for working on these! |
@adrianthedev The next steps are:
|
Hey @javierav. |
All that's left to do now is update the readme to explain how all these changes work and release version 1.0.0. My English level isn't that good, how is yours @adrianthedev? 😆 |
Yup. I'll take care of it @javierav. |
Continuing the conversation here |
After using this gem for some time to create components in the project I am working on, I have detected a series of improvements that could be interesting to apply to this library, so I am sharing them with you to decide if they are interesting to implement.
1. Configuration structure
The current structure is:
In order to be able to undertake the following proposals, the chain containing the base classes should be part of the configuration hash:
2. Slot support
This is about implementing the slot concept as defined in Tailwind Variants. This great idea allows you to apply different styles to the different html tags that a more complex component can be divided into. It is especially useful when used with variants / compound variants as seen in the example of an alert message.
The implementation is simple, since you have to detect if the object passed to
class
is a text string or a hash.If it is a string as until now, it will go to the
default
slot:If instead of a string it is a hash, the keys of that hash will define the slots:
type: :outlined, severity: :error
will return the classes:container
slot:rounded py-3 px-5 mb-4 border border-red-700 dark:border-red-500
title
slot:font-bold mb-1 text-red-700 dark:text-red-500
container
slot:text-red-600 dark:text-red-500
When executing the
render
method, you can optionally pass it the slot from which you want to get the classes:Although we will have to discuss implementation details later, because I have the feeling that something better needs to be done.
3. Override classes
When rendering classes, we need to be able to override them. Following the example above:
4. Class mixing strategies
At various points in this library we find ourselves in the need to mix CSS classes from different sources, so different mixing strategies should be implemented.
The most basic one can be a simple concatenation as it happens now.
For Tailwind, GitHub - gjtorikian/tailwind_merge: Utility function to efficiently merge Tailwind CSS classes without style conflicts could be used, always as an optional feature.
5. Helper to include in other classes
It is convenient to provide a helper that can be included in other classes to add the use of this library in a simple way:
What do you think @adrianthedev?
The text was updated successfully, but these errors were encountered: