Unify filter/search bars onto shared CSS classes + partials - #2220
Conversation
e441e7d to
4acfabd
Compare
4acfabd to
2d89d65
Compare
Filter fields, labels, placeholders, and the clear/search buttons had drifted into ~30 near-identical-but-inconsistent class strings across the index pages. Centralize the look in .search-field / .search-label / .search-select-placeholder / .search-submit (one CSS file) plus shared/_search_clear and shared/_search_submit partials, so every filter bar reads the same and restyling later is a one-place change. Placeholders now render small + light grey and revert to normal text when a value is entered; "All"-prompt selects grey their prompt like a placeholder and match the height of sibling text inputs. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2d89d65 to
a546814
Compare
| /* Shared filter/search-bar styling — restyle every filter bar here. In the | ||
| components layer so markup utility overrides (w-64, bg-white, pr-10) win. */ | ||
| @layer components { | ||
| .search-field { | ||
| @apply w-full rounded-lg border border-gray-300 px-3 py-2 text-gray-800 shadow-sm; | ||
| @apply focus:border-blue-500 focus:ring focus:ring-blue-200 focus:outline-none; | ||
| } | ||
|
|
||
| .search-field::placeholder { | ||
| @apply text-sm text-gray-400; | ||
| } | ||
|
|
||
| .search-label { | ||
| @apply block text-xs font-semibold uppercase text-gray-500 tracking-wide mb-1; | ||
| } | ||
|
|
||
| /* Grey the "All" prompt like a placeholder; text-sm/6 keeps the input's | ||
| line-height so the select stays the same height. */ | ||
| .search-select-placeholder:has(option:first-child:checked) { | ||
| @apply text-sm/6 text-gray-400; | ||
| } | ||
|
|
||
| /* Magnifier submit; pair with a `.search-field pr-10` input in a relative wrapper. */ | ||
| .search-submit { | ||
| @apply absolute inset-y-0 right-0 flex items-center pr-3 text-gray-500 hover:text-blue-600; | ||
| } | ||
| } | ||
|
|
There was a problem hiding this comment.
| /* Shared filter/search-bar styling — restyle every filter bar here. In the | |
| components layer so markup utility overrides (w-64, bg-white, pr-10) win. */ | |
| @layer components { | |
| .search-field { | |
| @apply w-full rounded-lg border border-gray-300 px-3 py-2 text-gray-800 shadow-sm; | |
| @apply focus:border-blue-500 focus:ring focus:ring-blue-200 focus:outline-none; | |
| } | |
| .search-field::placeholder { | |
| @apply text-sm text-gray-400; | |
| } | |
| .search-label { | |
| @apply block text-xs font-semibold uppercase text-gray-500 tracking-wide mb-1; | |
| } | |
| /* Grey the "All" prompt like a placeholder; text-sm/6 keeps the input's | |
| line-height so the select stays the same height. */ | |
| .search-select-placeholder:has(option:first-child:checked) { | |
| @apply text-sm/6 text-gray-400; | |
| } | |
| /* Magnifier submit; pair with a `.search-field pr-10` input in a relative wrapper. */ | |
| .search-submit { | |
| @apply absolute inset-y-0 right-0 flex items-center pr-3 text-gray-500 hover:text-blue-600; | |
| } | |
| } | |
| .search-select-placeholder:has(option:first-child:checked) { | |
| color: rgb(156 163 175); /* gray-400 */ | |
| font-size: 0.875rem; /* text-sm */ | |
| line-height: 1.5; /* /6 */ | |
| } |
Using @apply is frowned up with the guidance we just added about tailwind and we've done a pretty good job about not using it thus far.
Could we create shared partials instead and use the default tailwind classes in them? It would remove a lot of duplicate code and also remove the need for @apply.
I think only that placeholder code might need to live in this file and that shouldn't need @apply.
There was a problem hiding this comment.
🤖 From Claude: Done — all @apply removed. .search-field/.search-label/.search-submit are gone; field/label styling moved to SearchFormHelper#search_field_class/#search_label_class (a helper rather than a partial because these classes decorate ~100 heterogeneous controls — selects, remote/multi-selects, date/number/text inputs — that can't share one field partial; Tailwind v4 still generates the literals since it scans app/helpers via @source). _search_submit/_search_clear stay as shared partials with inline default Tailwind. Only this :has() placeholder rule remains, as raw CSS.
Two notes: I applied it via a normal commit (GitHub's "Apply suggestion" is a UI-only button, no API to commit it), and I corrected the line-height in your snippet from 1.5 to 1.5rem — text-sm/6 means a 1.5rem line-height, and the unitless 1.5 would have shrunk the select and undone the "keep it the same height as a text input" purpose this rule exists for.
Per review: @apply trades away Tailwind's benefits and the project avoids it. The field/label classes decorate ~100 heterogeneous filter controls that can't share one partial, so they move to SearchFormHelper#search_field_class / #search_label_class (Tailwind v4 scans app/helpers via @source); submit/clear stay as shared partials with inline default Tailwind. Only the :has() select placeholder rule remains in CSS, as raw CSS rather than @apply. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
| /* Filter/search-bar field and label styling lives in shared partials + | ||
| SearchFormHelper#search_field_class (default Tailwind, no @apply). Only this | ||
| placeholder rule stays here — its :has() selector isn't expressible as a utility. */ |
There was a problem hiding this comment.
This comment only makes sense if you are looking at the PR.
There was a problem hiding this comment.
🤖 From Claude: Good catch — reworded it to describe what the rule does (greying the select's blank first option like a placeholder, raw CSS because :has() has no utility equivalent) rather than narrating the change. Pushed in 2b60e6f.
The prior comment narrated the PR ("styling now lives in ... no @apply, only
this stays here") and mislabeled the helper as a partial. Describe what the rule
does instead, so it reads correctly from the file alone.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The /features filter bar was the lone search box the #2220 unification missed, so its labels rendered in the old normal style while every sibling index now shows the shared eyebrow style. Route its labels and fields through search_label_class/search_field_class so it matches. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adopt SearchFormHelper field/label classes + shared/_search_clear (introduced on main in #2220) so the form filter matches every other index. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…missions index (#2237) * Add slug + published to Form; extract FormAnswerPersistence for public forms Foundation for a public pretty-URL endpoint for standalone (event-less) forms. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Public pretty-URL endpoint for standalone forms + form-filterable submissions index Publish an event-less Form at /f/:slug for account-free public filling; view those submissions via the form-filterable /form_submissions index. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Refresh forms_controller brakeman ignore for :slug/:published permit Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Trim comments to non-obvious whys per CLAUDE.md Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Seed standalone public forms + designate public (event-less) submissions in index Adds a dev seed (two published event-less forms with submissions/answers) and marks event-less public submissions with a 'Public form' pill in the index so a blank Event column reads as intentional, not missing. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Fix View link escaping the results frame; consolidate answer persistence onto FormSubmission; forms UX polish - Bug: View link inside the submissions results Turbo frame navigated the frame to a frameless show page (Oopsie); break it out with turbo_frame _top. - Move persist_answer + hardened file-upload handling onto FormSubmission; registration, public forms, and bulk payment now share it (mixin removed). - Forms index: event-connected unpublished forms read 'Event form' not 'Not published'; delete link removed; submissions count links back with a Forms eyebrow anchored to the row. - Editor: clarify publish only controls the public link; swap Save/Delete placement and show a reason when a form can't be deleted. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Add FormSubmission#persist_answer model spec (text, multi-value, upsert, forged upload) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Email confirmation + admin FYI on public form submission Mirror event registration: new form_submission_confirmation(_fyi) notification kinds, NotificationMailer methods + views, job dispatch, and previews. Sent from PublicFormSubmission to the submitter and the AWBW team. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Forms index: show public-link and event-form chips independently A form can be both; render a chip for each that applies (rename column to Availability) rather than one-or-the-other. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Align submissions filter with the shared search-bar helpers/partials Adopt SearchFormHelper field/label classes + shared/_search_clear (introduced on main in #2220) so the form filter matches every other index. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Capture "Other" answers and keep the form filter on public-form submissions Public forms were the only submission path that skipped OtherResponses::CaptureFromSubmission, so a sector question answered "Other: …" never reached the curation queue — it only lived in the form answer, invisible to promotion. The submissions View link carried person_id but not form_id, so arriving from the forms index and clicking back landed on that person's submissions rather than the form-filtered list you came from. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * Reject a slug that parameterizes away instead of blanking it parameterize strips "!!!" (or any input with no url-safe characters) down to "", which the format validation waves through as blank and the unique index then collides on for the second such form — surfacing to the admin as the baffling "Slug has already been taken". Keep the typed value intact so the format error fires instead, and the admin sees what they entered. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * Keep the Forms eyebrow across the submission detail round trip The View link told the detail page it came from the submissions index, but not where that index was itself reached from — so returning from a submission opened via Forms landed on the filtered index with the "← Forms" back link gone. Carry the origin forward on the link and hand it back on the way home. The filter form carries it too, so changing the filter doesn't strip it out of the frame's params and orphan the View links. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * Refresh the drifted controller and service counts in AGENTS.md This branch adds a controller and a service, and both directory counts were already well behind the tree — the numbers are only useful if they're close. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
🤖 suggested review level: 5 Inspect 🔬 wide-reaching filter-bar restyle across ~30 index pages + shared helper/partials and select-height behavior
What is the goal of this PR and why is this important?
btn-utilityvs-outlinevs-secondaryclear buttons, ad-hoc search-icon buttons). This unifies them so every filter bar reads the same and future restyling is a one-place change.How did you approach the change?
@apply: field/label classes live inSearchFormHelper#search_field_class/#search_label_class(Tailwind v4 scansapp/helpersvia@source). A helper rather than a partial because these classes decorate ~100 heterogeneous controls (selects, remote/multi-selects, date/number/text inputs) that can't share one field partial.shared/_search_clear(standard "Clear filters" button) andshared/_search_submit(magnifier submit), both inline default Tailwind.:has()rule, written as raw CSS (line-height: 1.5rem), since its selector isn't expressible as a utility.Anything else to add?
ai/test_extra, Vite build + all system specs). Every migrated ERB template compiles; nosearch-field/search-labelrefs remain anywhere.