Skip to content

fix(settings): give the text fields the border and padding Tailwind never supplied (#2464) - #2465

Merged
vybe merged 1 commit into
devfrom
fix/2464-intake-field-styles
Sep 1, 2026
Merged

fix(settings): give the text fields the border and padding Tailwind never supplied (#2464)#2465
vybe merged 1 commit into
devfrom
fix/2464-intake-field-styles

Conversation

@dolho

@dolho dolho commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

What

The operator-intake form (ent#463) rendered its five inputs as unpadded, borderless bars beside the Admin sign-in email field it was meant to match.

Nothing is misspelled, and nothing is missing as Tailwind. The string is only correct if @tailwindcss/forms is loaded, and this repo's tailwind.config.js lists typography alone. Without those base styles:

  • border-gray-300 sets a colour on a border whose width is 0 under preflight — nothing renders,
  • padding does not exist unless px-3 py-2 says so,
  • the light background is the UA default rather than a decision.

Review cannot catch this by reading: the defect is the absence of tokens nobody expects to see.

The fix

SETTINGS_TEXT_INPUT_CLASS in the existing components/settings/fieldStyles.js (AC #2), stating all of it explicitly — and carrying no width.

That omission is load-bearing, not tidiness: Tailwind utilities of the same property do not override by class-attribute order. Appending w-20 after a constant's w-24 is resolved by stylesheet source order, not by the array — so a width baked into a shared shape cannot be overridden at a call site, only fought with. (It is why SETTINGS_NUMBER_INPUT_CLASS, which does carry w-24, could not simply be reused here.) The two layouts — w-full in a grid, flex-1 beside a button — stay at the call sites, which is the one thing they legitimately disagree about.

AC #1 holds by construction, not by comparison: the Admin sign-in email input — the field the issue names as the reference — now imports the same constant, so the two cannot drift apart again.

Scope: wider than the report, on purpose

Writing the guard as a rule over every Settings field rather than over the five that were reported immediately found five more live instances of the identical defect that nobody had filed:

File Field
UserGitHubPatPanel.vue per-user GitHub PAT
views/Settings.vue public URL
views/Settings.vue ×3 creator / operator / user role quotas

Leaving them would have meant an allowlist of known-broken fields inside the guard written to find them — which institutionalises the bug. All ten fields now share one shape.

The three quota fields keep w-20 text-center and their native spinners: only the actual defect is fixed, not their size or alignment, so their visual delta is exactly the missing border, padding and light background.

Two deliberate changes to fields that were not broken, so the shared shape stays one thing: the Admin sign-in email and public-URL inputs gain disabled:opacity-60 disabled:cursor-not-allowed (matching the sibling number constant; visible only while a save is in flight) and an explicit bg-white where they previously inherited the UA default.

Checkboxes and radios are deliberately outside the guard — without the forms plugin they render as UA-native controls whose appearance is never reset, so a border colour on one is inert rather than broken. Including them fired on four correct controls; that is stated in the helper rather than left as a silent filter.

Verified, not asserted

  • npm run build, then grepped the built CSS: .border, .px-3, .py-2, .bg-white, .shadow-sm, .placeholder-gray-400 are all present. The classes survive purge from a .js constant, which they only do because the content globs cover src/**/*.{vue,js,ts,jsx,tsx} — worth checking rather than assuming, since a purged constant would have shipped the same bug through the fix.
  • Raw-color ratchet (AC Feature/vector log retention #3): no file grew; Settings.vue's gray count fell 691 → 671.
  • Frontend unit suite: 1536 passed (69 files).

Guards

src/frontend/tests/unit/settingsFieldStyles.spec.js (9 tests) pins three things in order of durability:

  1. The trap, by discovery — no text-entry field on any Settings surface may name a border colour without the bare border that gives it a width. That is the rule; the reported five are just today's members of it. Includes a non-vacuity test so it cannot pass on a file set that no longer contains inputs.
  2. The shape is shared — both consumers import the constant, and the intake panel keeps no hand-written field class.
  3. The premise@tailwindcss/forms is still absent. If someone adds it, this fails so the constant's docstring gets re-read instead of silently becoming wrong.

Four mutations confirmed red then reverted: drop the bare border; restore the original class string on one intake input; give the constant a width; add the forms plugin.

Learnings entry added for the class (a utility-class string that reads as complete while depending on a plugin the repo never loaded).

Fixes #2464

…ever supplied

The operator-intake form (ent#463) rendered its five inputs as unpadded,
borderless bars beside the Admin sign-in email field it was meant to match.
The class string is not misspelled and nothing is missing *as Tailwind* — it
is only correct if `@tailwindcss/forms` is loaded, and this repo's
tailwind.config.js lists `typography` alone. Without those base styles
`border-gray-300` sets a COLOUR on a border whose width is 0 under preflight,
padding does not exist unless `px-3 py-2` says so, and the light background is
the UA default rather than a decision.

`SETTINGS_TEXT_INPUT_CLASS` in the existing components/settings/fieldStyles.js
states all of it explicitly, and carries NO width. That omission is load-
bearing rather than tidiness: Tailwind utilities of the same property do not
override by class-attribute order — appending `w-20` after a constant's `w-24`
is resolved by stylesheet source order, not by the array — so a width baked
into a shared shape cannot be overridden at a call site, only fought with. The
two layouts (`w-full` in a grid, `flex-1` beside a button) stay at the call
sites, which is the one thing they legitimately disagree about.

Scope, stated because it is wider than the report. Writing the guard as a rule
over every Settings field rather than over the five that were reported
immediately found FIVE MORE live instances of the identical defect that nobody
had filed: the per-user GitHub PAT field, the public-URL field, and the three
role-quota fields. Leaving them would have meant an allowlist of known-broken
fields inside the guard written to find them, which institutionalises the bug.
All ten now share one shape. The three quota fields keep `w-20 text-center`
and their native spinners — i.e. only the actual defect is fixed, not their
size or alignment, so the visual delta is exactly the missing border, padding
and light background.

Two deliberate small changes to fields that were NOT broken, both so the
shared shape stays one thing: the Admin sign-in email and public-URL inputs
gain `disabled:opacity-60 disabled:cursor-not-allowed` (matching the sibling
SETTINGS_NUMBER_INPUT_CLASS, visible only while a save is in flight) and an
explicit `bg-white` in place of the UA default.

Checkboxes and radios are deliberately outside the guard: without the forms
plugin they render as UA-native controls whose `appearance` is never reset, so
a border colour on one is inert rather than broken. Including them would have
fired on four correct controls.

Verified rather than asserted: `npm run build` and the built CSS carries
`.border`, `.px-3`, `.py-2`, `.bg-white`, `.shadow-sm` and
`.placeholder-gray-400` — the classes survive purge from a `.js` constant,
which they only do because the content globs cover `src/**/*.{vue,js,...}`.
Raw-color ratchet: no file grew; Settings.vue's gray count fell 691 -> 671.
Frontend unit suite 1536 passed.

Four mutations confirmed red then reverted: drop the bare `border` from the
constant; restore the original class string on one intake input; give the
constant a width; add the forms plugin to tailwind.config.js (which must fail,
because the constant's whole rationale is that the plugin is absent — that
assertion exists so the docstring gets re-read instead of silently becoming
wrong).

Fixes #2464

@vybe vybe left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Validated via /validate-pr: closing keyword ✅, shared fieldStyles constant with spec, learnings entry, build + e2e green.

@vybe
vybe merged commit 34a8665 into dev Sep 1, 2026
26 checks passed
vybe pushed a commit that referenced this pull request Sep 1, 2026
…end conflict)

Both sides appended 2026-09-01 learnings entries; kept all three
(the two #2454 entries from this branch + the #2464 Tailwind entry
from dev via PR #2465).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015QWiYNh8QMuaMxcrhh2SxF
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants