Partials FX extends Rails' partials to make them quack more like components. Each partial, located in app/views/components/, is backed by a Ruby class with the same name. It also supports CSS modules, meaning you define CSS in the component class and it gets “scoped” to that component only. It is powering the self-hosted Community Software, Forge.
bundle add partials_fxbin/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
endIn views:
<%= render AvatarComponent.new user: Current.user %>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
# …
endThen 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);
/* … */This project uses Standard for formatting Ruby code. Please make sure to run be standardrb before submitting pull requests. Run tests via rails test.
The gem is available as open source under the terms of the MIT License.