Simplify the view layer with a component library built on Phlex and styled with Bulma CSS framework. The code is simple and the UI is clean.
Let's take a look at an example:
The first row has three fields, in columns, with labels:
form.columns do
form.collection_select :customer_id, @customers, :id, :name, {}, { autofocus: true }
form.date_field :invoice_date
form.text_field :number
endThere's a nested form, with delete buttons and an add button. Those are built in.
ff.nested_form_delete_button(row_selector: "tr",
icon: "fas fa-trash",
color: "danger",
rounded: true,
outlined: true)You do not need a template for the new row, just add a button:
form.nested_form_add_button(:lines,
label: "Add Line",
container: "#invoice-lines tbody",
color: "success",
rounded: true,
outlined: true,
icon: "fas fa-plus")If you like clean UI, if you like clean code, you are in the right place. Take the power of Bulma CSS and Phlex for a spin today!
Add this line to your application's Gemfile:
gem "bulma-phlex-rails"And then execute:
bundle installOr install it yourself as:
gem install bulma-phlex-railsThis gem requires:
- Ruby 3.2.10 or higher
- Rails 7.2 or higher
- Phlex Rails 2.4 or higher
- Bulma CSS (which you'll need to include in your application)
-
Include Bulma CSS in your application. You can add it via npm/yarn, CDN, or the bulma-rails gem.
-
Require the gem in your code:
require "bulma-phlex-rails"The custom form builder that simplifies the process of creating forms with Bulma styles. It overrides the form helpers to generate Bulma-compatible HTML.
To use the form builder application-wide, set it as the default in an initializer:
# config/initializers/bulma_phlex.rb
ActionView::Base.default_form_builder = BulmaPhlex::Rails::FormBuilderThen use form_with as normal and the builder is applied automatically:
form_with(model: @project) do |f|
f.text_field :name
endRails also lets you set the default form builder per controller, which is useful if you only want Bulma styling on a subset of your application:
class ProjectsController < ApplicationController
default_form_builder BulmaPhlex::Rails::FormBuilder
endThis applies to all views rendered by that controller and any subclasses. To enable it for your entire application at the controller level, set it on ApplicationController instead.
Alternatively, specify the builder explicitly on a per-form basis:
form_with(model: @project, builder: BulmaPhlex::Rails::FormBuilder) do |f|
f.text_field :name
endFor example, use the standard text_field form helper:
form.text_field :nameIt generates the following HTML (assuming a form object for a Project model):
<div class="field">
<label class="label" for="project_name">Name</label>
<div class="control">
<input type="text" id="project_name" name="project[name]" class="input" />
</div>
</div>All of the standard Rails form helpers are supported. Under the hood the form builder still uses Rails form helpers, so all standard options are supported as well and you get the full power of Rails forms.
In addition to the standard options, the following options are supported for inputs:
suppress_label: Do not render the label taghelp: Render a help text below the fieldicon_left: Add an icon to the left of the input fieldicon_right: Add an icon to the right of the input fieldcolumn: Specify a Bulma column size when inside acolumnsblockgrid: Specify a Bulma grid cell size when inside agridblock
For example, want your name field to include a user icon?
form.text_field :name, icon_left: "fas fa-user"The checkbox method on the Form Builder generates a Bulma-styled checkbox input. If the label needs to be HTML instead of plain text, pass a block to the method.
form.checkbox :terms_of_service do
span { "I agree to the " }
a(href: '/terms') { 'the Terms' }
endBoth the collection_radio_buttons and collection_checkboxes methods wrap the inputss with the Bulma structure and styles including a label. Add option stacked: true to stack the inputs vertically.
form.collection_radio_buttons(:author_id, Author.all, :id, :name_with_initial)The file_field method supports the following additional Bulma-specific options:
color: Bulma color modifier (e.g.,"primary","info","success","danger")size: Bulma size modifier ("small","normal","medium","large")align: Set to"right"to right-align the file input labelfullwidth: (Boolean) Makes the file input span the full width of its containerboxed: (Boolean) Uses Bulma's boxed file upload styleshow_selections: (Boolean) Displays the selected filename next to the button
form.file_field :avatar, color: "info", boxed: true, show_selections: trueThe submit method accepts the following Bulma button styling options:
color: (String) Bulma color modifier such as"primary","link","info","success","warning", or"danger"size: (String)"small","normal","medium", or"large"mode: (String)"light"or"dark"responsive: (Boolean) Makes the button size responsivefullwidth: (Boolean) Makes the button full-widthoutlined: (Boolean) Renders the button as outlinedinverted: (Boolean) Renders the button as invertedrounded: (Boolean) Renders the button with rounded corners
form.submit "Save", color: "primary", rounded: trueThe button method renders a Bulma-styled button and accepts the same Bulma styling options as submit, plus icon options:
icon_left: Add an icon to the left of the button labelicon_right: Add an icon to the right of the button label
form.button "Preview", color: "info", icon_left: "fas fa-eye"Add and remove rows from nested forms with form builder methods nested_form_add_button and nested_form_delete_button. These work with Rails' fields_for helper to create dynamic nested forms.
There's no need to define anything else. Just use the methods in your form:
form.fields_for :tasks do |task_form|
task_form.text_field :name
task_form.nested_form_delete_button icon: "fas fa-trash", row_selector: "tr"
end
form.nested_form_add_button :tasks, label: "Add Task", container: "#tasks-list"The delete button will either hide the row and mark it for deletion (for existing records) or remove it from the DOM (for new records).
Both methods accept the following options:
nested_form_delete_button:
row_selector: (String, required) CSS selector passed toclosest()to identify the row to deletelabel: (String, optional) Button label texticon: (String, optional) Icon class displayed on the left (shorthand foricon_left)icon_left: (String, optional) Icon class displayed on the left of the buttonicon_right: (String, optional) Icon class displayed on the right of the button
nested_form_add_button:
record_name: (Symbol, required) The name of the nested association (e.g.,:tasks)container: (String, required) CSS selector for the container where new rows are appendedlabel: (String, optional) Button label texticon: (String, optional) Icon class displayed on the left (shorthand foricon_left)icon_left: (String, optional) Icon class displayed on the left of the buttonicon_right: (String, optional) Icon class displayed on the right of the button
Both methods also accept Bulma button styling options: color, size, mode, responsive, fullwidth, outlined, inverted, and rounded.
Bulma provides simple ways to build responsive layouts using columns and grids. The form builder includes helpers to leverage those features.
Need three columns on your form?
form.columns do
form.text_field :city
form.text_field :state
form.text_field :zip
endYou can use the following options for the Bulma columns:
minimum_breakpoint: (Symbol, optional) Sets the minimum breakpoint for the columns; default is:tablet.multiline: (Boolean, optional) If true, allows the columns to wrap onto multiple lines.gap: (optional) Use an integer (0-8) to set the gap size between columns; use a hash keyed by breakpoints to set responsive gap sizes.centered: (Boolean, optional) If true, centers the columns.vcentered: (Boolean, optional) If true, vertically centers the columns.
You can also size the individual columns, using either the names like "half", "two-thirds", etc., or the numeric values 1-12.
form.columns do
form.text_field :city, column: "half"
form.text_field :state, column: 2
form.text_field :zip, column: "narrow"
endBulma allows columns to be assigned different widths at different viewport sizes. Pass in a hash to the column option to specify those:
form.columns do
form.text_field :city, column: { mobile: "full", tablet: "half", desktop: "one-third" }
form.text_field :state, column: { mobile: "full", tablet: "one-quarter", desktop: "one-sixth" }
form.text_field :zip, column: { mobile: "full", tablet: "one-quarter", desktop: "one-sixth" }
endGrids let your form be extremely responsive to the viewport size:
form.grid do
form.telephone_field :phone
form.email_field :email
form.text_field :city
form.text_field :state
form.text_field :zip
endYou can use the following options for the Bulma grids:
fixed_columns: (Integer, optional) Specifies a fixed number of columns for the grid.auto_count: (Boolean, optional) If true, the grid will automatically adjust the number of columns based on the content.minimum_column_width: (Integer 1-32, optional) Sets a minimum width for the columns in the grid.gap: (optional) Sets the gap size between grid items from 1-8 with 0.5 increments.column_gap: (optional) Sets the column gap size between grid items from 1-8 with 0.5 increments.row_gap: (optional) Sets the row gap size between grid items from 1-8 with 0.5 increments.
You can still provide guidance on the individualgrid cell sizes:
form.grid do
form.telephone_field :phone, column: "col-span-2"
form.email_field :email, column: "col-span-3"
form.text_field :city, column: "col-span-2"
form.text_field :state
form.text_field :zip
endAll of the power of Bulma columns and grids are at your fingertips with an easy-to-use, Rails-friendly API.
Important
This feature has not yet been implemented.
Bulma can attach inputs, buttons, and dropdowns or group controls together. Helper methods make those easy, too.
form.addon do
form.text_field :username, placeholder: "Username"
form.button "Check Availability", class: "button is-info"
endform.group do
form.text_field :first_name, placeholder: "First Name"
form.text_field :last_name, placeholder: "Last Name"
endThe utilities and clean look of forms is also available for displaying data. The Phlex mixin BulmaPhlex::Rails::DisplayableFormFields makes it easy to show and organize fields.
with_options model: invoice do
in_columns do
show_text :customer_name
show_date :invoice_date
show_text :number
show_currency :amount
end
in_columns do
show_text :notes, column: "three-quarters"
show_text :payment_status, &:titleize
end
endMethod show_text renders a label and read-only input field for displaying text data. An optional block can be provided to format the value.
model: ActiveRecord Model - The model containing the text attribute. This can also be passed via theoptions(helpful when usingwith_options).method: Symbol or String - The attribute method name for the text field.options: Hash - Additional Bulma form field options can be passed, such as:help,:icon_left,:icon_right,:column, and:grid.
Method show_date renders a label and read-only date field. An optional format key can be provided with the options to specify the date format. This is passed to the Rails to_fs method.
model: ActiveRecord Model - The model containing the date attribute. This can also be passed via theoptions(helpful when usingwith_options).method: Symbol or String - The attribute method name for the date field.options: Hash - Additional options for the display field. This can include theformatkey, which gets to the Railsto_fsmethod.
Method show_currency renders a label and read-only currency field. An optional currency_options key can be provided with the options. It is passed to the Rails number_to_currency helper method.
model: ActiveRecord Model - The model containing the currency attribute. This can also be passed via theoptions(helpful when usingwith_options).method: Symbol or String - The attribute method name for the currency field.options: Hash - Additional options for the display field. This can include thecurrency_optionskey, which should be a hash of options passed tonumber_to_currency.
Method in_columns creates a Bulma columns container for organizing display fields. Individual fields can optionally specify their column sizes via the column option.
Method in_grid creates a Bulma grid container for organizing display fields. Individual fields can optionally specify their grid cell sizes via the grid option.
Bulma provides a wide variety of components to make you applications look great. This extends the bulma-phlex gem, bringing in the all those components to make it easy to add a Navigation Bar, Table, Cards, and more to your Rails application. See the bulma-phlex gem for the full list of components.
In some cases, the components are extended here with Rails features.
For example, the BulmaPhlex::Card component provides the standard card. With the Rails extension, your card can now use Turbo Frames for dynamic content loading.
render BulmaPhlex::Card.new do |card|
card.head("Product Info")
card.turbo_frame_content("product", src: product_path(@product), pending_message: "Loading product...")
endRails provides great support for rendering currency amounts with the number_to_currency helper. The BulmaPhlex::Table component is extended with an amount_column method to make it easy to show amounts in your tables.
render BulmaPhlex::Table.new(invoices) do |table|
table.column "Invoice No", &:invoice_number
table.column "Customer", &:customer_name
table.amount_column "Amount Due", &:total_amount
table.date_colum "Due Date", &:due_date
endAfter checking out the repo, run bundle install to install dependencies. Then, run rake test to run the tests.
To test across different versions of Rails, use the appraisal gem. Run bundle exec appraisal install to set up the different test environments, then bundle exec appraisal rake test to run the tests across all versions.
Bug reports and pull requests are welcome on GitHub at https://github.com/RockSolt/bulma-phlex-rails.
The gem is available as open source under the terms of the MIT License.
This leverages the Bulma CSS library and Phlex but is not endorsed or certified by either. We are fans of the both and this makes using them together easier.