Partials FX
Partials FX extends Rails' partials to make them quack more like components.
Installation
bundle add partials_fx
Usage
bin/rails generate component Avatar
This creates two files:
app/views/components/_avatar_component.html.erb
;app/views/components/avatar_component.rb
.
They could look like this:
<span id="user-avatar" class="<%= component_class %> <%= avatar_size %>">
<% if avatar.attached? %>
<%= image_tag avatar.variant(avatar_variant), alt: alt_text %>
<% else %>
<%= tag.span name.first.upcase, class: "initial" %>
<% end %>
</span>
# app/views/components/avatar_component.rb
class AvatarComponent < PartialsFx::Component
attribute :user, required: true
attribute :size, :string, default: "md", inquiry: true
attribute :variant, :string, default: "thumb", inquiry: true
def avatar_size
return "avatar-profile" if variant.profile?
class_names(
"avatar-xs": size.xs?,
"avatar-sm": size.sm?,
"avatar-md": size.md?,
"avatar-lg": size.lg?,
"avatar-xl": size.xl?
)
end
def name
@name ||= user.decorate.name
end
def avatar = profile.avatar
def alt_text = "Avatar for #{name}"
def avatar_variant
variant.thumb? ? :thumb : :profile
end
private
def profile
@profile ||= user.profile
end
end
In views:
<%= render AvatarComponent.new user: Current.user %>
CSS modules
A powerful feature of Partials FX is support for “CSS modules”. Meaning that styles are locally scoped by default, preventing style conflicts across components. This allows the use of the same class names in different components without worrying about collisions, as Partials FX automatically generates unique class names, for the parent element, during compilation. CSS modules improve maintainability by creating a clear connection between components and their styles, making it easier to manage complex UI systems.
It works like this. First define a styles
block in the class:
class AvatarComponent < PartialsFx::Component
# …
styles do
<<~CSS
.component {
}
CSS
end
# …
end
Then add the component_class
method to the the component partial:
<span id="user-avatar" class="<%= component_class %> <%= avatar_size %>">
</span>
You can now add any CSS selector in the .component
selector, which itself will compile to something like:
/* app/assets/stylesheets/components.partialsfx.css */
.c-9af2b949 {
--avatar-font-size-fallback:
clamp(.75rem, calc(var(--avatar-height) * .8), 2rem);
display: inline flex;
align-items: center;
justify-content: center;
height: var(--avatar-height, 1rem);
/* … */
.initial {
font-size: var(--avatar-font-size, var(--avatar-font-size-fallback));
font-weight: 600;
color: var(--avatar-color, var(--base-60));
}
&.avatar-profile {}
/* … */
}
Inside the component partial, component_class
will be replaced with the unique selector .c-9af2b949
. Because nested selectors are available in CSS this will work smoothly. Keeping your selectors scoped to the one component. Making your app’s CSS way more maintainable.
Make sure to import the automatically-created CSS file from Partials FX into your application.css
, e.g.
@layer reset, components, utilities;
/* … */
@import url("./components.partialsfx.css") layer(components);
/* … */