How We Write Code
Pre Reading
Html First - An overview of the principles we want to follow when building software.
Base Styles - Our lightweight library for applying styles
Github | Docs Site | Demo Site | Raw CSS file
Mini Js - a lightweight javascript library for adding basic interactivity to web pages
Patterns
Quick Reference
Links
• Use <a href="<%= my_path %>">Click Me</a>
. Avoid using link_to
• HTMX will be applied by default, so no need to add any hx- attributes by default.
• To remove/reset htmx on an individual link, do <a hx-boost="false" ...></a>
• Use the url helpers current_url_with
, current_params_with
if needed. Read more
• To make a link in an h1expo app open in a modal, add ?presentation=modal
• HTMX will not be applied if the request is inside a webview (user agent is h1expo).
• HTMX will not be applied by default if the link is inside active admin.
• For styling, use the .ui-button
class to make a link a button, or .ui-link
class for plain underline
• For links styled as buttons, add hx-indicator='this'
to the <a>
tag and add a loading spinner inside with <%= inline_svg_tag("misc/spinner.svg",class:"shown-while-loading") %>
Forms
• Use the form_for or form_with helper to open a form. Always add the .ui-form
class. For example: <%= form_for @user, url: demo_user_form_path(@user), html: {class:"ui-form"} do |f| %>
• For form fields, use the normal field helpers. For example: <%= f.text_field :first_name %>
• Unless otherwise specified, use floating input labels for form elements. For example:
<%= f.text_field :first_name %><%= f.label :first_name, class:"--label" %>
• By default, all forms submit asynchronously (because we have hx-boost
on the body. To disable this behaviour, add hx-boost='false'
to the form.
• For nested forms, use accepts_nested_attributes_for
in the model and fields_for
in the view. To only display a subset of records, pass them in explicitly. For example: <%= user_form.fields_for :categories, @editable_categories do |nested_form| %>
• Unless otherwise specified, include the form errors partial to show errors. For example: <%= render partial: "shared/form_errors", locals: { record: f.object} %>
• Unless otherwise specified, add loading spinner icons to form submit buttons, with the .shown-while-loading
class. For example: <%= f.button class: "ui-button --solid" do |button| %>Continue<%= inline_svg_tag("misc/spinner.svg", class:"shown-while-loading") %><% end %>
• Use the same controller action for both get and post actions. For multistep forms, create an action for each step and redirect the user to the next step if saved successfully. For example: def get_order; if request.post?; redirect_to order_step2_path if @order.update(order_params); end; end
• For multistep forms apply conditional validations by using an attr_accessor
called validation_set
. Set it in the controller just before a save, and add the validations in the model with a proc. For example: validates_presence_of :first_name, if: proc { |order| order.validation_set == "step1" }
• For forms with nested records, use a new button to add records, and add hx-preserve to the existing record divs so that they don’t get overwritten.
• For forms that require frontend notification messages, set toasts in the controller. For example: flash.now[:toasts] = [{ title: 'Post Created', message: 'Your post has been created.' }]
General
• All buttons should have loading spinners unless otherwise specified.
Last updated