Bonus Module — Phlex::UI as a Standalone Gem
6 lessons · Open Source · Phlex::UI
We extract thePhlex::UIcomponent library from KanbanFlow into a properly structured, versioned, published Ruby gem — complete with Lookbook previews, a configuration API, and a Rails generator.
Before we start
This module is optional. KanbanFlow is complete. You don’t need to do this to have a working application or a useful component library.
But if you want Phlex::UI to travel with you across projects — if you
want to bundle add phlex_ui in a new Rails app and have 20+ components
available immediately, with Lookbook previews, typed props, and Tailwind
token theming — this module shows you how.
The process of extraction is also instructive. Moving components from an app into a gem surfaces assumptions you didn’t know you’d made. By the end you will understand what makes a component library genuinely reusable, as opposed to merely portable.
Lesson 1 — Gem structure
Creating the gem skeleton
|
|
The --no-ext flag skips native extensions (we don’t need them).
--no-test skips the test framework setup — we’ll add Minitest manually.
The generator creates:
phlex_ui/
lib/
phlex_ui/
version.rb
phlex_ui.rb
spec/ (or test/)
phlex_ui.gemspec
Gemfile
Rakefile
README.md
CHANGELOG.md
LICENSE.txt
.gitignoreThe gemspec
|
|
Version numbering with SemVer
|
|
For a component library, SemVer means:
Patch (0.1.0 → 0.1.1) — bug fixes, typo corrections in
class strings, accessibility improvements that don’t change the API.
Minor (0.1.0 → 0.2.0) — new components, new props with defaults,
new variants. Anything additive that doesn’t break existing usage.
Major (0.1.0 → 1.0.0) — removed components, renamed props,
changed slot method names, breaking Tailwind class changes that require
host app changes. Anything that breaks existing phlex_ui usage.
Start at 0.1.0 — the 0.x range signals that the API is not yet
stable and minor versions may contain breaking changes.
Directory structure
The gem will follow Rails Engine conventions for its app directory:
phlex_ui/
app/
components/
phlex_ui/
base.rb
button.rb
badge.rb
avatar.rb
alert.rb
card.rb
table.rb
panel.rb
link.rb
heading.rb
empty_state.rb
modal.rb
dropdown.rb
toast.rb
toast_container.rb
accordion.rb
tabs.rb
breadcrumb.rb
search_bar.rb
form_group.rb
label.rb
text_input.rb
textarea.rb
select.rb
checkbox.rb
radio.rb
stimulus_component.rb
test/
components/
button_test.rb
# ... etc
lib/
phlex_ui/
version.rb
railtie.rb
configuration.rb
phlex_ui.rbNote the phlex_ui/ subdirectory under app/components/. This namespacing
prevents conflicts with the host app’s own Components:: namespace.
The gem’s components live under PhlexUI::Components:: rather than
Components:: — this is an important design decision covered in Lesson 2.
Lesson 2 — Moving components into the gem
The namespace question
In KanbanFlow, components live under Components::Button,
Components::Card etc. When extracted to a gem, we have two options:
Option A — Keep Components:: namespace
Host apps use Components::Button — but the gem and the host app
share the same namespace. Conflicts are possible if the host app defines
its own Components::Button.
Option B — Use PhlexUI::Components:: namespace
The gem owns PhlexUI::Components::. The host app’s Components::
is completely separate. Kits are set up independently.
We use Option B. It’s more explicit and avoids conflicts:
|
|
The host app can include PhlexUI::Components in its own
Components::Base to get short-form Kit syntax if desired.
PhlexUI::Components::Base
|
|
Notice what’s missing: no include Phlex::Rails::Helpers::Routes. The
gem’s base class has no Rails dependencies beyond Phlex itself. Routes
and other Rails helpers are opt-in — the host app includes them as
needed:
|
|
This separation means PhlexUI::Components::Base can be used in a
non-Rails Ruby app — a Sinatra app, a Roda app, even a plain Ruby script.
Moving the components
Copy each component from KanbanFlow’s app/components/ into the gem’s
app/components/phlex_ui/, updating:
- The class name:
Components::Button→PhlexUI::Components::Button - The parent class:
Components::Base→PhlexUI::Components::Base - Remove
include Phlex::Rails::Helpers::Routesfrom any component that has it — this moves to opt-in - Update any internal Kit calls to use the
PhlexUI::Components::prefix
|
|
Repeat for every component. This is mechanical but important — take care to remove any KanbanFlow-specific assumptions (route helpers, model references, app-specific constants).
The entry point
|
|
Lesson 3 — Railtie for Rails integration
What a Railtie does
A Railtie is the mechanism Rails gems use to hook into the Rails initialisation process. It lets the gem add autoload paths, run initializers, add routes, and configure Rails — all automatically when the gem is loaded.
For phlex_ui, the Railtie needs to:
- Tell Zeitwerk where the gem’s components live
- Add Lookbook’s preview path (in development)
- Optionally run a generator to set up the host app
The Railtie
|
|
Testing the Railtie
With the Railtie in place, add the gem to a fresh Rails app and confirm:
|
|
Visit any page — if the Railtie is working, PhlexUI::Components::Button
will be autoloaded. Visit /lookbook — if Lookbook is installed,
PhlexUI components will appear in the sidebar.
The generator
A generator that sets up the host app with the necessary configuration:
|
|
Run with:
|
|
Bundling Tailwind tokens
The gem’s Tailwind tokens need to be importable in the host app’s CSS. Add the token file to the gem:
|
|
Host apps can override any token after the import:
|
|
Lesson 4 — Lookbook previews in the gem
Bundling previews
Lookbook previews packaged inside the gem give host apps a working component browser immediately — no preview files to write, no setup beyond installing the gem.
The gem’s previews live in test/components/previews/phlex_ui/:
|
|
The PhlexUI:: namespace on preview classes prevents conflicts with
the host app’s own previews.
Documentation pages
Lookbook supports Markdown documentation pages alongside previews.
Bundle them in test/components/docs/phlex_ui/:
|
|
Variants
<%= render_preview :primary %> <%= render_preview :secondary %> <%= render_preview :danger %> <%= render_preview :ghost %>
Props
| Prop | Type | Default | Description |
|---|---|---|---|
label |
String | required | Button text |
variant |
Symbol | :primary |
Visual style |
size |
Symbol | :md |
Size — :sm, :md, :lg |
disabled |
Boolean | false |
Disables the button |
type |
String | "button" |
HTML button type |
Accessibility
aria-disabled is always set — even when disabled: false — ensuring
screen readers correctly announce the button’s state.
### The Lookbook preview layout
The gem needs its own preview layout so components render with the
correct Tailwind stylesheet — even if the host app has different assets:
```ruby
# app/views/layouts/phlex_ui/preview.html.erb
<!DOCTYPE html>
<html>
<head>
<%= stylesheet_link_tag "tailwind", "data-turbo-track": "reload" %>
<%= stylesheet_link_tag "application", "data-turbo-track": "reload" %>
</head>
<body class="p-8 bg-white">
<%= yield %>
</body>
</html>Set as the default layout for all PhlexUI previews in the Railtie:
|
|
Lesson 5 — Configuration
ActiveSupport::Configurable
Rails provides ActiveSupport::Configurable for implementing a
configuration API. It’s what Rails itself uses for config.x options:
|
|
Host apps configure the library in an initializer:
|
|
Using configuration in components
|
|
The -> { PhlexUI.config.default_button_variant } Proc form ensures
the config is read at instantiation time, not at class load time — so
changing the config after load still takes effect.
Allowing component subclassing
Host apps can extend any PhlexUI component:
|
|
This is the extension pattern preferred over monkey-patching — the host app owns its subclass, the gem owns the base class. Both can evolve independently.
Theme customisation
For Tailwind token overrides, the gem provides a documented list of every token it uses — so host app designers know exactly which CSS variables to override:
|
|
Lesson 6 — Publishing and maintenance
Pre-publish checklist
Before publishing to RubyGems for the first time:
□ All components have Lookbook previews
□ All components have documentation pages
□ README explains installation and basic usage
□ CHANGELOG.md has a 0.1.0 entry
□ gemspec metadata is complete (homepage, source_code_uri)
□ No KanbanFlow-specific code remains in components
□ Tests pass: bundle exec rake test
□ The gem installs cleanly in a fresh Rails app
□ /lookbook shows all components correctly
□ Dark mode works in the preview layoutCutting a release
|
|
rake release builds the gem, pushes the tag to GitHub, and publishes
to RubyGems in one step.
Writing a good CHANGELOG.md
Follow the Keep a Changelog format:
|
|
CONTRIBUTING.md
|
|
Consuming the gem in a fresh Rails app
The final test — install the published gem in a brand new Rails app and confirm everything works:
|
|
In config/routes.rb:
|
|
In app/assets/tailwind/application.css:
|
|
Start the server and visit /lookbook. The full PhlexUI component
library should appear — all previews, all documentation, all variants.
In a view:
|
|
If that renders correctly, the gem is working end to end.
Bonus Module summary
bundle gem phlex_uicreates the skeleton — gemspec, version file, Rakefile, README, CHANGELOG- Components move to
app/components/phlex_ui/under thePhlexUI::Components::namespace — this prevents conflicts with host app components PhlexUI::Components::Basehas no Rails dependencies — routes and other Rails helpers are opt-in at the host app level- The Railtie hooks into Rails initialisation to add autoload paths and Lookbook preview paths automatically — no manual setup in the host app
- Lookbook previews and documentation pages bundled in the gem give every host app a working component browser out of the box
ActiveSupport::Configurableprovides thePhlexUI.configureblock — host apps set defaults for variants, sizes, and durations- Component subclassing is the preferred extension pattern — host apps inherit from PhlexUI components and override what they need
- Tailwind tokens are published as an importable CSS file —
@import "phlex_ui/tokens"then override individual tokens rake releasebuilds, tags, and publishes to RubyGems in one step- The preview-first development workflow — write the Lookbook preview before writing the component — is the recommended contribution pattern
What you’ve built
phlex_ui — a published, versioned Ruby gem containing:
- 20+ Phlex components with typed props and Tailwind token theming
- Lookbook previews and Markdown documentation for every component
- Dark mode and multi-theme support via CSS custom properties
- A Rails generator for installation
- A configuration API for host-app customisation
- A
CHANGELOG.mdandCONTRIBUTING.md
Any Rails app can now use bundle add phlex_ui and get a complete,
documented, themeable component library. That’s the promise from
Module 1, fully delivered.