Fix the broken input focus outline, unify input styling, tokenize scales
The root cause of 'some inputs show a focus outline, most don't': the
global input:focus/textarea:focus/select:focus rule (outline:none,
border-color: accent) has specificity (0,0,1,1). Every per-component
rule shaped '.wrapper input { border: 1px solid var(--border) }' (.form
input, .field-row input, .kanban__new input, .sidebar__select select,
.modal__field select) ties it exactly, and -- being unconditional --
silently won that tie by simply appearing later in the file, so the
input's border stayed var(--border) forever regardless of focus. Only
two controls escaped: .project-card__name-input (a class applied
directly to the element, (0,0,1,0), too low to ever win the tie) and
.card-row__text (has its own .card-row__text:focus, (0,0,2,0),
genuinely higher). Confirmed with a live computed-style test against
the real stylesheet before and after.
Fix: one canonical .field class (plus .field--compact for inline 'add'
rows and the sidebar, .field--autosize for the dashboard's JS-grown
textarea), applied directly to the <input>/<textarea>/<select> itself
in every view -- structurally immune to the same tie, since a bare
class can never out-specificity input:focus. This also collapses five
near-duplicate rules (each with its own padding/radius/font-size) into
one definition, and fixes .kanban__new input's stray
background: var(--surface) (every other field uses var(--bg); this one
nearly matched its own var(--surface) sidebar panel).
Also: .btn-danger hardcoded #b3261e/#fff instead of the already-
existing var(--error)/var(--accent-text) tokens, so danger buttons
didn't adapt in dark mode like every other error-coloured element;
now they do. Added .field:disabled styling (opacity/cursor), matching
buttons -- latent until now since no input bound :disabled yet.
Second pass, promoting repeated-but-consistent raw values to tokens:
- border-radius: 6/7/8/10/12/999px -- the stray 7px (.sidebar__link)
folded into the 6px tier -- become --radius-sm/md/lg/xl/pill.
- disabled/ghost opacity: 0.6 was used for every disabled button
except .status-row__delete's 0.4 (now unified) and drag-ghost states'
0.5 (semantically different, kept separate) -- --opacity-disabled/
--opacity-ghost.
Font-size and spacing values also repeat but don't reduce to a clean
scale without arbitrary judgement calls either way -- left alone.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -85,7 +85,7 @@ async function onCreate() {
|
||||
<textarea
|
||||
ref="titleInput"
|
||||
v-model="title"
|
||||
class="project-card__name-input"
|
||||
class="field field--autosize"
|
||||
rows="1"
|
||||
maxlength="255"
|
||||
placeholder="Name"
|
||||
|
||||
@@ -70,7 +70,7 @@ async function onPasskeyLogin() {
|
||||
<form v-else class="form" @submit.prevent="onSubmit">
|
||||
<label>
|
||||
<span>Email</span>
|
||||
<input v-model="email" type="email" autocomplete="email" required />
|
||||
<input v-model="email" class="field" type="email" autocomplete="email" required />
|
||||
<small v-if="error?.fieldError('email')" class="field-error">
|
||||
{{ error.fieldError('email') }}
|
||||
</small>
|
||||
|
||||
@@ -141,7 +141,7 @@ async function onRemovePasskey(passkey: Passkey) {
|
||||
<form class="form" @submit.prevent="onChangeEmail">
|
||||
<label>
|
||||
<span>New email</span>
|
||||
<input v-model="newEmail" type="email" maxlength="255" required />
|
||||
<input v-model="newEmail" class="field" type="email" maxlength="255" required />
|
||||
<small v-if="changeError?.fieldError('email')" class="field-error">
|
||||
{{ changeError.fieldError('email') }}
|
||||
</small>
|
||||
@@ -197,7 +197,7 @@ async function onRemovePasskey(passkey: Passkey) {
|
||||
<form class="form form--new-card" @submit.prevent="onAddPasskey">
|
||||
<label>
|
||||
<span>Label</span>
|
||||
<input v-model="newLabel" type="text" maxlength="100" />
|
||||
<input v-model="newLabel" class="field" type="text" maxlength="100" />
|
||||
</label>
|
||||
<p v-if="addError" class="form-error">{{ addError }}</p>
|
||||
<button type="submit" :disabled="adding">
|
||||
|
||||
@@ -201,6 +201,7 @@ onBeforeUnmount(() => window.removeEventListener('keydown', onKeydown))
|
||||
<form class="field-row" @submit.prevent="onRenameProject">
|
||||
<input
|
||||
v-model="nameDraft"
|
||||
class="field field--compact"
|
||||
type="text"
|
||||
maxlength="255"
|
||||
required
|
||||
@@ -251,6 +252,7 @@ onBeforeUnmount(() => window.removeEventListener('keydown', onKeydown))
|
||||
<form class="field-row" @submit.prevent="onAddStatus">
|
||||
<input
|
||||
v-model="newStatusName"
|
||||
class="field field--compact"
|
||||
type="text"
|
||||
maxlength="100"
|
||||
required
|
||||
@@ -282,7 +284,7 @@ onBeforeUnmount(() => window.removeEventListener('keydown', onKeydown))
|
||||
|
||||
<label class="modal__field">
|
||||
<span>Move cards to</span>
|
||||
<select v-model="reassignTo">
|
||||
<select v-model="reassignTo" class="field">
|
||||
<option value="" disabled>Choose a status…</option>
|
||||
<option v-for="s in reassignOptions" :key="s.id" :value="s.id">{{ s.name }}</option>
|
||||
</select>
|
||||
|
||||
@@ -194,7 +194,7 @@ async function onCreate() {
|
||||
<form class="form form--new-card" @submit.prevent="onCreate">
|
||||
<label>
|
||||
<span>New card</span>
|
||||
<input v-model="newText" type="text" maxlength="1000" required />
|
||||
<input v-model="newText" class="field" type="text" maxlength="1000" required />
|
||||
</label>
|
||||
<button type="submit" :disabled="submitting">
|
||||
{{ submitting ? 'Adding…' : 'Add card' }}
|
||||
|
||||
Reference in New Issue
Block a user