Skip to content

Commit

Permalink
Apply default form styling (maybe-finance#272)
Browse files Browse the repository at this point in the history
* Add and organise component stylesheets

* Revert CSS folder and file structure

* Add FormsHelper and FormBuilder to apply component classes

* Refactor label args

Co-authored-by: Jose Farias <[email protected]>
Signed-off-by: Josh Brown <[email protected]>

* Update form field styles

* Apply form builder to all fields

* Remove redundant style rules

Some of these were either duplicative or had no effect.

* Apply default submit button styles

* Set default form class

* Fix opacity of input when focused

---------

Signed-off-by: Josh Brown <[email protected]>
Co-authored-by: Jose Farias <[email protected]>
Co-authored-by: Josh Pigford <[email protected]>
  • Loading branch information
3 people authored Feb 9, 2024
1 parent f817499 commit df3e14a
Show file tree
Hide file tree
Showing 11 changed files with 131 additions and 120 deletions.
70 changes: 40 additions & 30 deletions app/assets/stylesheets/application.tailwind.css
Original file line number Diff line number Diff line change
Expand Up @@ -2,34 +2,44 @@
@tailwind components;
@tailwind utilities;

.prose table {
@apply divide-y divide-gray-300;
}

.prose tr {
@apply divide-x divide-gray-100;
}

.prose th {
@apply whitespace-nowrap px-2 py-3.5 text-left text-sm font-semibold text-gray-900;
}

.prose tbody {
@apply divide-y divide-gray-200;
}

.prose td {
@apply px-2 py-2 text-sm text-gray-500 whitespace-nowrap;
}

.input-wrapper {
@apply relative p-4 bg-gray-100 border border-gray-200 rounded-2xl focus-within:bg-white focus-within:drop-shadow-form focus-within:opacity-100;
}

.input-label {
@apply block text-sm font-medium text-gray-500;
}

.input-field {
@apply p-0 mt-1 bg-transparent border-none opacity-50 focus:outline-none focus:ring-0 focus-within:opacity-100;
@layer components {
.prose {
table {
@apply divide-y divide-gray-300;
}

tr {
@apply divide-x divide-gray-100;
}

th {
@apply whitespace-nowrap px-2 py-3.5 text-left text-sm font-semibold text-gray-900;
}

tbody {
@apply divide-y divide-gray-200;
}

td {
@apply px-2 py-2 text-sm text-gray-500 whitespace-nowrap;
}
}

.form-field {
@apply relative border bg-white rounded-xl shadow-sm;
@apply focus-within:shadow-none focus-within:border-gray-900 focus-within:ring-4 focus-within:ring-gray-100;
}

.form-field__label {
@apply p-3 pb-0 block text-sm font-medium opacity-50;
}

.form-field__input {
@apply p-3 pt-1 w-full bg-transparent border-none opacity-50;
@apply focus:outline-none focus:ring-0 focus:opacity-100;
}

.form-field__submit {
@apply w-full p-3 text-center text-white bg-black rounded-lg hover:bg-gray-700;
}
}
2 changes: 2 additions & 0 deletions app/controllers/application_controller.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
class ApplicationController < ActionController::Base
include Authentication

default_form_builder ApplicationFormBuilder

# Only allow modern browsers supporting webp images, web push, badges, import maps, CSS nesting, and CSS :has.
allow_browser versions: :modern

Expand Down
58 changes: 58 additions & 0 deletions app/helpers/application_form_builder.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
class ApplicationFormBuilder < ActionView::Helpers::FormBuilder
def initialize(object_name, object, template, options)
options[:html] ||= {}
options[:html][:class] ||= "space-y-4"

super(object_name, object, template, options)
end

(field_helpers - [ :label, :check_box, :radio_button, :fields_for, :fields, :hidden_field, :file_field ]).each do |selector|
class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
def #{selector}(method, options)
default_options = { class: "form-field__input" }
merged_options = default_options.merge(options)
return super(method, merged_options) unless options[:label]
@template.form_field_tag do
label(method, *label_args(options)) +
super(method, merged_options.except(:label))
end
end
RUBY_EVAL
end

def select(method, choices, options = {}, html_options = {})
default_options = { class: "form-field__input" }
merged_options = default_options.merge(html_options)

return super(method, choices, options, merged_options) unless options[:label]

@template.form_field_tag do
label(method, *label_args(options)) +
super(method, choices, options, merged_options.except(:label))
end
end

def submit(value = nil, options = {})
value, options = nil, value if value.is_a?(Hash)
default_options = { class: "form-field__submit" }
merged_options = default_options.merge(options)
super(value, merged_options)
end

private

def label_args(options)
case options[:label]
when Array
options[:label]
when String
[ options[:label], { class: "form-field__label" } ]
when Hash
[ nil, options[:label] ]
else
[ nil, { class: "form-field__label" } ]
end
end
end
5 changes: 5 additions & 0 deletions app/helpers/forms_helper.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module FormsHelper
def form_field_tag(&)
tag.div class: "form-field", &
end
end
5 changes: 1 addition & 4 deletions app/views/accounts/account/_depository.html.erb
Original file line number Diff line number Diff line change
@@ -1,4 +1 @@
<div class="relative p-3 border border-[#141414]/8 bg-white rounded-xl focus-within:bg-white focus-within:shadow-none focus-within:border-gray-900 focus-within:ring-4 focus-within:ring-gray-100 focus-within:opacity-100 shadow-sm">
<label for="account_name" class="block text-sm font-medium opacity-50 focus-within:opacity-100">Type</label>
<%= f.select :subtype, options_for_select([["Checking", "checking"], ["Savings", "savings"]], selected: ""), {}, class: "block w-full p-0 mt-1 bg-transparent border-none focus:outline-none focus:ring-0" %>
</div>
<%= f.select :subtype, options_for_select([["Checking", "checking"], ["Savings", "savings"]], selected: ""), { label: "Type" } %>
5 changes: 1 addition & 4 deletions app/views/accounts/account/_investment.html.erb
Original file line number Diff line number Diff line change
@@ -1,4 +1 @@
<div class="relative p-3 border border-[#141414]/8 bg-white rounded-xl focus-within:bg-white focus-within:shadow-none focus-within:border-gray-900 focus-within:ring-4 focus-within:ring-gray-100 focus-within:opacity-100 shadow-sm">
<label for="account_name" class="block text-sm font-medium opacity-50 focus-within:opacity-100">Type</label>
<%= f.select :subtype, options_for_select(Account::Investment::SUBTYPES, selected: ""), {}, class: "block w-full p-0 mt-1 bg-transparent border-none focus:outline-none focus:ring-0" %>
</div>
<%= f.select :subtype, options_for_select(Account::Investment::SUBTYPES, selected: ""), { label: true } %>
18 changes: 3 additions & 15 deletions app/views/accounts/new.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -70,25 +70,13 @@
<%= form_with model: @account, url: accounts_path, scope: :account, html: { class: "space-y-4 m-5 mt-1", data: { turbo: false } } do |f| %>
<%= f.hidden_field :accountable_type %>

<div class="relative p-3 border border-[#141414]/8 bg-white rounded-xl focus-within:bg-white focus-within:shadow-none focus-within:border-gray-900 focus-within:ring-4 focus-within:ring-gray-100 focus-within:opacity-100 shadow-sm">
<%= f.label :name, 'Account name', class: 'block text-sm font-medium opacity-50 focus-within:opacity-100' %>
<%= f.text_field :name, placeholder: 'Example account name', required: 'required', class: "p-0 mt-1 bg-transparent border-none opacity-50 focus:outline-none focus:ring-0 focus-within:opacity-100" %>
</div>
<%= f.text_field :name, placeholder: 'Example account name', required: 'required', label: 'Account name' %>

<%= render "accounts/#{permitted_accountable_partial(@account.accountable_type)}", f: f %>

<div class="relative p-3 border border-[#141414]/8 bg-white rounded-xl focus-within:bg-white focus-within:shadow-none focus-within:border-gray-900 focus-within:ring-4 focus-within:ring-gray-100 focus-within:opacity-100 shadow-sm">
<%= f.label :balance, class: 'block text-sm font-medium opacity-50 focus-within:opacity-100' %>
<div class="flex">
<%= f.number_field :balance, placeholder: number_to_currency(0), in: 0.00..100000000.00, required: 'required', class: "p-0 mt-1 bg-transparent border-none opacity-50 focus:outline-none focus:ring-0 focus-within:opacity-100 w-full" %>
</div>
</div>
<%= f.number_field :balance, placeholder: number_to_currency(0), in: 0.00..100000000.00, required: 'required', label: true %>

<div class="">
<button type="submit" class="w-full p-3 text-center text-white bg-black rounded-lg hover:bg-gray-700" title="Submit">
Add <%= @account.accountable.model_name.human.downcase %>
</button>
</div>
<%= f.submit "Add #{@account.accountable.model_name.human.downcase}" %>
<% end %>
<% end %>
<% end %>
11 changes: 3 additions & 8 deletions app/views/password_resets/new.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,10 @@
header_title t('.title')
%>

<%= form_with url: password_reset_path, html: {class: 'space-y-6'} do |form| %>
<%= form_with url: password_reset_path do |form| %>
<%= auth_messages form %>

<div class="relative border border-gray-100 bg-offwhite rounded-xl focus-within:bg-white focus-within:shadow focus-within:opacity-100">
<%= form.label :email, class: 'p-4 pb-0 block text-sm font-medium text-gray-700' %>
<%= form.email_field :email, autofocus: false, autocomplete: "email", required: 'required', placeholder: '[email protected]', class: 'p-4 pt-1 bg-transparent border-none opacity-50 focus:outline-none focus:ring-0 focus-within:opacity-100 w-full' %>
</div>
<%= form.email_field :email, label: true, autofocus: false, autocomplete: "email", required: 'required', placeholder: '[email protected]' %>

<div>
<%= form.submit t('.submit'), class: 'flex justify-center w-full px-4 py-3 text-sm font-medium text-white bg-black rounded-xl hover:bg-black focus:outline-none focus:ring-2 focus:ring-gray-200 shadow' %>
</div>
<%= form.submit t('.submit') %>
<% end %>
26 changes: 6 additions & 20 deletions app/views/registrations/new.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -2,32 +2,18 @@
header_title t('.title')
%>

<%= form_with model: @user, url: registration_path, html: {class: 'space-y-6'} do |form| %>
<%= form_with model: @user, url: registration_path do |form| %>
<%= auth_messages form %>

<div class="relative border border-gray-100 bg-offwhite rounded-xl focus-within:bg-white focus-within:shadow focus-within:opacity-100">
<%= form.label :email, class: 'p-4 pb-0 block text-sm font-medium text-gray-700' %>
<%= form.email_field :email, autofocus: false, autocomplete: "email", required: 'required', placeholder: '[email protected]', class: 'p-4 pt-1 bg-transparent border-none opacity-50 focus:outline-none focus:ring-0 focus-within:opacity-100 w-full' %>
</div>
<%= form.email_field :email, autofocus: false, autocomplete: "email", required: 'required', placeholder: '[email protected]', label: true %>

<div class="relative border border-gray-100 bg-offwhite rounded-xl focus-within:bg-white focus-within:shadow focus-within:opacity-100">
<%= form.label :password, class: 'p-4 pb-0 block text-sm font-medium text-gray-700' %>
<%= form.password_field :password, autocomplete: "new-password", required: 'required', class: 'p-4 pt-1 bg-transparent border-none opacity-50 focus:outline-none focus:ring-0 focus-within:opacity-100 w-full' %>
</div>
<%= form.password_field :password, autocomplete: "new-password", required: 'required', label: true %>

<div class="relative border border-gray-100 bg-offwhite rounded-xl focus-within:bg-white focus-within:shadow focus-within:opacity-100">
<%= form.label :password_confirmation, class: 'p-4 pb-0 block text-sm font-medium text-gray-700' %>
<%= form.password_field :password_confirmation, autocomplete: "new-password", required: 'required', class: 'p-4 pt-1 bg-transparent border-none opacity-50 focus:outline-none focus:ring-0 focus-within:opacity-100 w-full' %>
</div>
<%= form.password_field :password_confirmation, autocomplete: "new-password", required: 'required', label: true %>

<% if hosted_app? %>
<div class="bg-offwhite rounded-xl focus-within:bg-white focus-within:shadow focus-within:opacity-100 relative border border-gray-100">
<%= form.label :invite_code, class: 'p-4 pb-0 block text-sm font-medium text-gray-700' %>
<%= form.password_field :invite_code, required: 'required', class: 'p-4 pt-1 bg-transparent border-none opacity-50 focus:outline-none focus:ring-0 focus-within:opacity-100 w-full' %>
</div>
<%= form.password_field :invite_code, required: 'required', label: true %>
<% end %>

<div>
<%= form.submit class: 'cursor-pointer flex justify-center w-full px-4 py-3 text-sm font-medium text-white bg-black rounded-xl hover:bg-black focus:outline-none focus:ring-2 focus:ring-gray-200 shadow' %>
</div>
<%= form.submit %>
<% end %>
16 changes: 4 additions & 12 deletions app/views/sessions/new.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,14 @@
header_title t('.title')
%>

<%= form_with url: session_path, html: {class: 'space-y-6'} do |form| %>
<%= form_with url: session_path do |form| %>
<%= auth_messages form %>

<div class="relative border border-gray-100 bg-offwhite rounded-xl focus-within:bg-white focus-within:shadow focus-within:opacity-100">
<%= form.label :email, t('.email'), class: 'p-4 pb-0 block text-sm font-medium text-gray-700' %>
<%= form.email_field :email, autofocus: false, autocomplete: "email", required: 'required', placeholder: t('.email_placeholder'), class: 'p-4 pt-1 bg-transparent border-none opacity-50 focus:outline-none focus:ring-0 focus-within:opacity-100 w-full' %>
</div>
<%= form.email_field :email, label: t('.email'), autofocus: false, autocomplete: "email", required: 'required', placeholder: t('.email_placeholder') %>

<div class="relative border border-gray-100 bg-offwhite rounded-xl focus-within:bg-white focus-within:shadow focus-within:opacity-100">
<%= form.label :password, class: 'p-4 pb-0 block text-sm font-medium text-gray-700' %>
<%= form.password_field :password, required: 'required', class: 'p-4 pt-1 bg-transparent border-none opacity-50 focus:outline-none focus:ring-0 focus-within:opacity-100 w-full' %>
</div>
<%= form.password_field :password, label: true, required: 'required' %>

<div>
<%= form.submit t('.submit'), class: 'cursor-pointer flex justify-center w-full px-4 py-3 text-sm font-medium text-white bg-black rounded-xl hover:bg-black focus:outline-none focus:ring-2 focus:ring-gray-200 shadow' %>
</div>
<%= form.submit t('.submit') %>
<% end %>

<div class="mt-6 text-center">
Expand Down
35 changes: 8 additions & 27 deletions app/views/settings/edit.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -2,39 +2,20 @@

<%= form_with model: Current.user, url: settings_path, html: { class: "space-y-4" } do |form| %>
<%= form.fields_for :family_attributes do |family_fields| %>
<div class="relative p-4 border border-gray-100 bg-offwhite rounded-xl focus-within:bg-white focus-within:shadow focus-within:opacity-100">
<%= family_fields.label :name, "Family name", class: "block text-sm font-medium opacity-75 focus-within:opacity-100" %>
<%= family_fields.text_field :name, placeholder: "Family name", value: Current.family.name, class: "p-0 mt-1 bg-transparent border-none opacity-50 focus:outline-none focus:ring-0 focus-within:opacity-100" %>
</div>
<%= family_fields.text_field :name, placeholder: "Family name", value: Current.family.name, label: "Family name" %>
<% end %>

<%= form.text_field :first_name, placeholder: "First name", value: Current.user.first_name, label: true %>

<div class="relative p-4 border border-gray-100 bg-offwhite rounded-xl focus-within:bg-white focus-within:shadow focus-within:opacity-100">
<%= form.label :first_name, class: "block text-sm font-medium opacity-75 focus-within:opacity-100" %>
<%= form.text_field :first_name, placeholder: "First name", value: Current.user.first_name, class: "w-full p-0 mt-1 bg-transparent border-none opacity-50 focus:outline-none focus:ring-0 focus-within:opacity-100" %>
</div>

<div class="relative p-4 border border-gray-100 bg-offwhite rounded-xl focus-within:bg-white focus-within:shadow focus-within:opacity-100">
<%= form.label :last_name, class: "block text-sm font-medium opacity-75 focus-within:opacity-100" %>
<%= form.text_field :last_name, placeholder: "Last name", value: Current.user.last_name, class: "w-full p-0 mt-1 bg-transparent border-none opacity-50 focus:outline-none focus:ring-0 focus-within:opacity-100" %>
</div>
<%= form.text_field :last_name, placeholder: "Last name", value: Current.user.last_name, label: true %>

<%= form.email_field :email, placeholder: "Email", value: Current.user.email, label: true %>

<div class="relative p-4 border border-gray-100 bg-offwhite rounded-xl focus-within:bg-white focus-within:shadow focus-within:opacity-100">
<%= form.label :email, class: "block text-sm font-medium opacity-75 focus-within:opacity-100" %>
<%= form.email_field :email, placeholder: "Email", value: Current.user.email, class: "w-full p-0 mt-1 bg-transparent border-none opacity-50 focus:outline-none focus:ring-0 focus-within:opacity-100" %>
</div>
<%= form.password_field :password, label: true %>

<div class="relative p-4 border border-gray-100 bg-offwhite rounded-xl focus-within:bg-white focus-within:shadow focus-within:opacity-100">
<%= form.label :password, class: "block text-sm font-medium opacity-75 focus-within:opacity-100" %>
<%= form.password_field :password, class: "w-full p-0 mt-1 bg-transparent border-none opacity-50 focus:outline-none focus:ring-0 focus-within:opacity-100" %>
</div>

<div class="relative p-4 border border-gray-100 bg-offwhite rounded-xl focus-within:bg-white focus-within:shadow focus-within:opacity-100">
<%= form.label :password_confirmation, class: "block text-sm font-medium opacity-75 focus-within:opacity-100" %>
<%= form.password_field :password_confirmation, class: "w-full p-0 mt-1 bg-transparent border-none opacity-50 focus:outline-none focus:ring-0 focus-within:opacity-100" %>
</div>
<%= form.password_field :password_confirmation, label: true %>

<div class="absolute right-5 bottom-5">
<div class="fixed right-5 bottom-5">
<button type="submit" class="flex items-center justify-center w-12 h-12 mb-2 bg-black rounded-full shrink-0 grow-0 hover:bg-gray-600">
<%= inline_svg_tag('icn-check.svg', class: 'text-white fill-current') %>
</button>
Expand Down

0 comments on commit df3e14a

Please sign in to comment.