refactor(input): R10 S3b — one input recipe, ring focus everywhere
Unify text inputs onto the form.tsx inputCls look (surface bg, rounded-lg, 2px accent RING focus), replacing the settings-family border-focus style app-wide: - deleted 3 duplicate local `const inputCls` (KeyGate/SettingsPanel/SetupWizard) → import the shared one; a 4th (Welcome, py-2.5) kept local but re-styled to match. - ~30 inline settings inputs: default full-size fields → inputCls; size variants (pager/ table-filter/chat textarea/plex/glass-card) keep their width/radius/padding but swap bg-card→bg-surface and border-focus→ring-focus. glass-card/bg-transparent bgs left as-is. - refreshed the form.tsx doc-comment (the old two-style split is gone). The ring is the more visible / a11y-correct focus cue, matching R9's focus foundation.
This commit is contained in:
@@ -156,7 +156,7 @@ export default function AddToPlaylist({
|
||||
}
|
||||
}}
|
||||
placeholder={t("playlists.newPlaylist")}
|
||||
className="flex-1 min-w-0 bg-card border border-border rounded-md px-2 py-1 text-xs outline-hidden focus:border-accent"
|
||||
className="flex-1 min-w-0 bg-surface border border-border rounded-md px-2 py-1 text-xs focus:outline-hidden focus:ring-2 focus:ring-accent/40"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -347,7 +347,7 @@ function AdminInvites() {
|
||||
value={newEmail}
|
||||
onChange={(e) => setNewEmail(e.target.value)}
|
||||
placeholder={t("settings.invites.addPlaceholder")}
|
||||
className="flex-1 min-w-0 bg-card border border-border rounded-xl px-3 py-2 text-sm outline-hidden focus:border-accent"
|
||||
className="flex-1 min-w-0 bg-surface border border-border rounded-xl px-3 py-2 text-sm focus:outline-hidden focus:ring-2 focus:ring-accent/40"
|
||||
/>
|
||||
<button
|
||||
type="submit"
|
||||
@@ -466,7 +466,7 @@ function AdminDemo() {
|
||||
value={newEmail}
|
||||
onChange={(e) => setNewEmail(e.target.value)}
|
||||
placeholder={t("settings.demo.addPlaceholder")}
|
||||
className="flex-1 min-w-0 bg-card border border-border rounded-xl px-3 py-2 text-sm outline-hidden focus:border-accent"
|
||||
className="flex-1 min-w-0 bg-surface border border-border rounded-xl px-3 py-2 text-sm focus:outline-hidden focus:ring-2 focus:ring-accent/40"
|
||||
/>
|
||||
<button
|
||||
type="submit"
|
||||
|
||||
@@ -111,7 +111,7 @@ function TagEditorMenu({
|
||||
}}
|
||||
maxLength={64}
|
||||
placeholder={t("channels.tags.createPlaceholder")}
|
||||
className="flex-1 min-w-0 bg-transparent text-xs px-2 py-1 rounded-md border border-border focus:border-accent outline-hidden"
|
||||
className="flex-1 min-w-0 bg-transparent text-xs px-2 py-1 rounded-md border border-border focus:outline-hidden focus:ring-2 focus:ring-accent/40"
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
|
||||
@@ -211,7 +211,7 @@ export default function ChatThread({
|
||||
}}
|
||||
rows={1}
|
||||
placeholder={t("messages.writePlaceholder")}
|
||||
className="flex-1 resize-none max-h-28 bg-card border border-border rounded-xl px-3 py-2 text-sm outline-hidden focus:border-accent"
|
||||
className="flex-1 resize-none max-h-28 bg-surface border border-border rounded-xl px-3 py-2 text-sm focus:outline-hidden focus:ring-2 focus:ring-accent/40"
|
||||
/>
|
||||
<button
|
||||
onClick={submit}
|
||||
|
||||
@@ -40,7 +40,7 @@ export default function ColumnSortControl<T>({
|
||||
onChange(key ? { key, dir: active?.key === key ? active.dir : "asc" } : null);
|
||||
}}
|
||||
aria-label={label}
|
||||
className="bg-card border border-border rounded-lg px-2 py-1.5 text-sm outline-hidden focus:border-accent"
|
||||
className="bg-surface border border-border rounded-lg px-2 py-1.5 text-sm focus:outline-hidden focus:ring-2 focus:ring-accent/40"
|
||||
>
|
||||
<option value="">{defaultLabel}</option>
|
||||
{sortable.map((c) => (
|
||||
|
||||
@@ -366,7 +366,7 @@ function Field({
|
||||
max={isNum && item.max != null ? item.max : undefined}
|
||||
placeholder={item.secret ? "••••••••" : String(item.default ?? "")}
|
||||
aria-label={t(`config.fields.${item.key}.label`, item.key)}
|
||||
className="w-52 bg-card border border-border rounded-xl px-3 py-1.5 text-sm outline-hidden focus:border-accent disabled:opacity-50"
|
||||
className="w-52 bg-surface border border-border rounded-xl px-3 py-1.5 text-sm focus:outline-hidden focus:ring-2 focus:ring-accent/40 disabled:opacity-50"
|
||||
/>
|
||||
)}
|
||||
{showReset && !secretDisabled && (
|
||||
|
||||
@@ -410,7 +410,7 @@ export default function Feed({
|
||||
value={ytCount}
|
||||
onChange={(e) => setYtCount(Number(e.target.value))}
|
||||
title={t("feed.yt.showHint")}
|
||||
className="bg-card border border-border rounded-lg px-2 py-1 text-xs outline-hidden focus:border-accent"
|
||||
className="bg-surface border border-border rounded-lg px-2 py-1 text-xs focus:outline-hidden focus:ring-2 focus:ring-accent/40"
|
||||
>
|
||||
{[20, 40, 60, 100].map((n) => (
|
||||
<option key={n} value={n}>
|
||||
@@ -603,7 +603,7 @@ export default function Feed({
|
||||
})
|
||||
}
|
||||
title={t("feed.source.tip")}
|
||||
className="bg-card border border-border rounded-lg px-2 py-1 text-xs outline-hidden focus:border-accent"
|
||||
className="bg-surface border border-border rounded-lg px-2 py-1 text-xs focus:outline-hidden focus:ring-2 focus:ring-accent/40"
|
||||
>
|
||||
<option value="organic">{t("feed.source.organic")}</option>
|
||||
<option value="all">{t("feed.source.all")}</option>
|
||||
@@ -637,7 +637,7 @@ export default function Feed({
|
||||
});
|
||||
}}
|
||||
aria-label={t("feed.sortLabel")}
|
||||
className="bg-card border border-border rounded-lg px-2 py-1.5 text-sm outline-hidden focus:border-accent"
|
||||
className="bg-surface border border-border rounded-lg px-2 py-1.5 text-sm focus:outline-hidden focus:ring-2 focus:ring-accent/40"
|
||||
>
|
||||
{/* Relevance only ranks meaningfully when there's a search term — offer it then. */}
|
||||
{(filters.q.trim() ? (["relevance", ...sortKeys] as SortKey[]) : sortKeys).map((k) => (
|
||||
|
||||
@@ -5,9 +5,7 @@ import { useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
import { AlertTriangle, Lock, ShieldCheck } from "lucide-react";
|
||||
import { api, HttpError } from "../lib/api";
|
||||
import * as e2ee from "../lib/e2ee";
|
||||
|
||||
const inputCls =
|
||||
"w-full bg-card border border-border rounded-xl px-3 py-2 text-sm outline-hidden focus:border-accent";
|
||||
import { inputCls } from "./ui/form";
|
||||
|
||||
// Secure-messaging key gate: first-run setup (choose a passphrase) or unlock on this device.
|
||||
// Self-contained — on success it updates the shared e2ee unlock state, so any observing view
|
||||
|
||||
@@ -13,6 +13,7 @@ import { openChat } from "../lib/chatDock";
|
||||
import * as e2ee from "../lib/e2ee";
|
||||
import Avatar from "./Avatar";
|
||||
import KeyGate from "./KeyGate";
|
||||
import { inputCls } from "./ui/form";
|
||||
import ChatThread from "./ChatThread";
|
||||
|
||||
type View =
|
||||
@@ -224,7 +225,7 @@ function Directory({ onPick, onBack }: { onPick: (u: MessageUser) => void; onBac
|
||||
value={filter}
|
||||
onChange={(e) => setFilter(e.target.value)}
|
||||
placeholder={t("messages.searchPeople")}
|
||||
className="w-full bg-card border border-border rounded-xl px-3 py-2 text-sm outline-hidden focus:border-accent"
|
||||
className={inputCls}
|
||||
/>
|
||||
{q.isError && !q.data ? (
|
||||
<StateMessage tone="error" onRetry={() => q.refetch()}>
|
||||
|
||||
@@ -87,7 +87,7 @@ export default function Pager({
|
||||
onBlur={commitPageInput}
|
||||
onKeyDown={(e) => e.key === "Enter" && commitPageInput()}
|
||||
aria-label={t("datatable.pager.pageLabel")}
|
||||
className="w-14 bg-card border border-border rounded-md px-1.5 py-1 text-xs text-center tabular-nums outline-hidden focus:border-accent"
|
||||
className="w-14 bg-surface border border-border rounded-md px-1.5 py-1 text-xs text-center tabular-nums focus:outline-hidden focus:ring-2 focus:ring-accent/40"
|
||||
/>
|
||||
{t("datatable.pager.ofTotal", { total: totalPages })}
|
||||
</span>
|
||||
@@ -110,7 +110,7 @@ export default function Pager({
|
||||
onSizeChange(Number(e.target.value));
|
||||
scrollListToTop();
|
||||
}}
|
||||
className="bg-card border border-border rounded-md px-1.5 py-1 text-xs outline-hidden focus:border-accent"
|
||||
className="bg-surface border border-border rounded-md px-1.5 py-1 text-xs focus:outline-hidden focus:ring-2 focus:ring-accent/40"
|
||||
>
|
||||
{pageSizeOptions.map((n) => (
|
||||
<option key={n} value={n}>
|
||||
|
||||
@@ -590,7 +590,7 @@ export default function Playlists() {
|
||||
if (e.key === "Escape") setRenaming(false);
|
||||
}}
|
||||
aria-label={t("playlists.rename")}
|
||||
className="text-lg font-semibold bg-card border border-border rounded-md px-2 py-0.5 outline-hidden focus:border-accent"
|
||||
className="text-lg font-semibold bg-surface border border-border rounded-md px-2 py-0.5 focus:outline-hidden focus:ring-2 focus:ring-accent/40"
|
||||
/>
|
||||
) : (
|
||||
<div className="flex items-center gap-2 min-w-0">
|
||||
|
||||
@@ -154,7 +154,7 @@ export default function PlaylistsRail({
|
||||
value={plSort.key}
|
||||
onChange={(e) => setPlSort({ ...plSort, key: e.target.value as PlSort["key"] })}
|
||||
aria-label={t("playlists.sortLabel")}
|
||||
className="flex-1 min-w-0 bg-card border border-border rounded-md px-1.5 py-1 text-2xs outline-hidden focus:border-accent"
|
||||
className="flex-1 min-w-0 bg-surface border border-border rounded-md px-1.5 py-1 text-2xs focus:outline-hidden focus:ring-2 focus:ring-accent/40"
|
||||
>
|
||||
<option value="custom">{t("playlists.railSortCustom")}</option>
|
||||
<option value="name">{t("playlists.railSortName")}</option>
|
||||
@@ -200,7 +200,7 @@ export default function PlaylistsRail({
|
||||
onChange={(e) => setNewName(e.target.value)}
|
||||
placeholder={t("playlists.newPlaylist")}
|
||||
disabled={!canWrite}
|
||||
className="flex-1 min-w-0 bg-card border border-border rounded-md px-2 py-1 text-xs outline-hidden focus:border-accent disabled:opacity-50"
|
||||
className="flex-1 min-w-0 bg-surface border border-border rounded-md px-2 py-1 text-xs focus:outline-hidden focus:ring-2 focus:ring-accent/40 disabled:opacity-50"
|
||||
/>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
@@ -131,7 +131,7 @@ export default function PlexCollectionEditor({
|
||||
onChange={(e) => setNewName(e.target.value)}
|
||||
onKeyDown={(e) => e.key === "Enter" && createNew()}
|
||||
placeholder={t("plex.collEditor.newPlaceholder")}
|
||||
className="glass-card min-w-0 flex-1 rounded-lg px-3 py-2 text-sm outline-hidden focus:border-accent"
|
||||
className="glass-card min-w-0 flex-1 rounded-lg px-3 py-2 text-sm focus:outline-hidden focus:ring-2 focus:ring-accent/40"
|
||||
/>
|
||||
<button
|
||||
onClick={createNew}
|
||||
@@ -149,7 +149,7 @@ export default function PlexCollectionEditor({
|
||||
value={q}
|
||||
onChange={(e) => setQ(e.target.value)}
|
||||
placeholder={t("plex.collEditor.search")}
|
||||
className="glass-card mb-2 w-full rounded-lg px-3 py-1.5 text-sm outline-hidden focus:border-accent"
|
||||
className="glass-card mb-2 w-full rounded-lg px-3 py-1.5 text-sm focus:outline-hidden focus:ring-2 focus:ring-accent/40"
|
||||
/>
|
||||
)}
|
||||
|
||||
|
||||
@@ -100,7 +100,7 @@ export default function PlexPlaylistAdd({
|
||||
onChange={(e) => setNewName(e.target.value)}
|
||||
onKeyDown={(e) => e.key === "Enter" && createNew()}
|
||||
placeholder={t("plex.playlistAdd.newPlaceholder")}
|
||||
className="glass-card min-w-0 flex-1 rounded-lg px-3 py-2 text-sm outline-hidden focus:border-accent"
|
||||
className="glass-card min-w-0 flex-1 rounded-lg px-3 py-2 text-sm focus:outline-hidden focus:ring-2 focus:ring-accent/40"
|
||||
/>
|
||||
<button
|
||||
onClick={createNew}
|
||||
|
||||
@@ -532,7 +532,7 @@ export default function PlexPlaylistView({
|
||||
onChange={(e) => setName(e.target.value)}
|
||||
onKeyDown={(e) => e.key === "Enter" && rename()}
|
||||
onBlur={rename}
|
||||
className="glass-card min-w-0 flex-1 rounded-lg px-3 py-1.5 text-lg font-bold outline-hidden focus:border-accent"
|
||||
className="glass-card min-w-0 flex-1 rounded-lg px-3 py-1.5 text-lg font-bold focus:outline-hidden focus:ring-2 focus:ring-accent/40"
|
||||
/>
|
||||
) : (
|
||||
<h1 className="min-w-0 flex-1 truncate text-2xl font-bold">{q.data?.title ?? " "}</h1>
|
||||
|
||||
@@ -171,7 +171,7 @@ export default function PlexSidebar({ layout, setLayout, collapsed, onToggleColl
|
||||
onChange={(e) => setNewPlaylist(e.target.value)}
|
||||
onKeyDown={(e) => e.key === "Enter" && createPlaylist()}
|
||||
placeholder={t("plex.playlist.newPlaceholder")}
|
||||
className="glass-card min-w-0 flex-1 rounded-lg px-2 py-1 text-xs outline-hidden focus:border-accent"
|
||||
className="glass-card min-w-0 flex-1 rounded-lg px-2 py-1 text-xs focus:outline-hidden focus:ring-2 focus:ring-accent/40"
|
||||
/>
|
||||
<button
|
||||
onClick={createPlaylist}
|
||||
@@ -298,7 +298,7 @@ export default function PlexSidebar({ layout, setLayout, collapsed, onToggleColl
|
||||
value={collSearch}
|
||||
onChange={(e) => setCollSearch(e.target.value)}
|
||||
placeholder={t("plex.filter.collectionSearch")}
|
||||
className="mb-1.5 w-full rounded-lg border border-border bg-card px-2 py-1 text-sm focus:border-accent focus:outline-hidden"
|
||||
className="mb-1.5 w-full rounded-lg border border-border bg-surface px-2 py-1 text-sm focus:outline-hidden focus:ring-2 focus:ring-accent/40"
|
||||
/>
|
||||
<div
|
||||
ref={collFade.ref}
|
||||
@@ -546,7 +546,7 @@ function NumberInput({
|
||||
const n = e.target.value === "" ? null : Number(e.target.value);
|
||||
onChange(n != null && Number.isFinite(n) ? n : null);
|
||||
}}
|
||||
className="w-20 rounded-lg border border-border bg-card px-2 py-1 text-sm tabular-nums focus:border-accent focus:outline-hidden"
|
||||
className="w-20 rounded-lg border border-border bg-surface px-2 py-1 text-sm tabular-nums focus:outline-hidden focus:ring-2 focus:ring-accent/40"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -172,7 +172,7 @@ export default function SavedViewsWidget({
|
||||
}}
|
||||
placeholder={t("views.namePlaceholder")}
|
||||
maxLength={80}
|
||||
className="flex-1 min-w-0 bg-card border border-border rounded-lg px-2 py-1 text-xs outline-hidden focus:border-accent"
|
||||
className="flex-1 min-w-0 bg-surface border border-border rounded-lg px-2 py-1 text-xs focus:outline-hidden focus:ring-2 focus:ring-accent/40"
|
||||
/>
|
||||
<button
|
||||
onClick={() => void save()}
|
||||
@@ -256,7 +256,7 @@ function ViewRow({
|
||||
if (e.key === "Escape") onCancelRename();
|
||||
}}
|
||||
maxLength={80}
|
||||
className="flex-1 min-w-0 bg-card border border-border rounded-lg px-2 py-1 text-xs outline-hidden focus:border-accent"
|
||||
className="flex-1 min-w-0 bg-surface border border-border rounded-lg px-2 py-1 text-xs focus:outline-hidden focus:ring-2 focus:ring-accent/40"
|
||||
/>
|
||||
<button
|
||||
onClick={() => onRename(draft)}
|
||||
|
||||
@@ -172,7 +172,7 @@ function JobRow({
|
||||
if (e.key === "Enter") save();
|
||||
if (e.key === "Escape") setEditing(false);
|
||||
}}
|
||||
className="w-16 bg-card border border-border rounded-sm px-1.5 py-0.5 text-xs outline-hidden focus:border-accent"
|
||||
className="w-16 bg-surface border border-border rounded-sm px-1.5 py-0.5 text-xs focus:outline-hidden focus:ring-2 focus:ring-accent/40"
|
||||
/>
|
||||
<span className="text-muted text-xs">{t("scheduler.minutes")}</span>
|
||||
<button
|
||||
@@ -281,7 +281,7 @@ function MaintenanceSettings({
|
||||
onChange={(e) => setVal(e.target.value)}
|
||||
onKeyDown={(e) => e.key === "Enter" && save()}
|
||||
aria-label={t("scheduler.maintenance.batchLabel")}
|
||||
className="w-28 bg-card border border-border rounded-sm px-2 py-1 text-sm tabular-nums outline-hidden focus:border-accent"
|
||||
className="w-28 bg-surface border border-border rounded-sm px-2 py-1 text-sm tabular-nums focus:outline-hidden focus:ring-2 focus:ring-accent/40"
|
||||
/>
|
||||
<button
|
||||
onClick={save}
|
||||
|
||||
@@ -9,7 +9,7 @@ import { LS, useAccountPersistedState } from "../lib/storage";
|
||||
import Avatar from "./Avatar";
|
||||
import { notify, type NotifSettings } from "../lib/notifications";
|
||||
import Tooltip from "./Tooltip";
|
||||
import { Section, SettingRow, Switch } from "./ui/form";
|
||||
import { inputCls, Section, SettingRow, Switch } from "./ui/form";
|
||||
import { type SaveState } from "./ui/DraftSaveBar";
|
||||
import { useConfirm } from "./ConfirmProvider";
|
||||
import { useWizard } from "./WizardProvider";
|
||||
@@ -402,9 +402,6 @@ function SignInMethods({ me }: { me: Me }) {
|
||||
}
|
||||
};
|
||||
|
||||
const inputCls =
|
||||
"w-full bg-card border border-border rounded-xl px-3 py-2 text-sm outline-hidden focus:border-accent";
|
||||
|
||||
// Offer Google only when this instance has Google OAuth configured (or the account is already
|
||||
// linked — then we still show its "Connected" status even if the instance creds were removed).
|
||||
const showGoogle = me.google_enabled || me.has_google;
|
||||
|
||||
@@ -4,6 +4,7 @@ import { Check, ChevronLeft, ChevronRight, Loader2, Send } from "lucide-react";
|
||||
import { api, HttpError } from "../lib/api";
|
||||
import { setLanguage, type LangCode } from "../i18n";
|
||||
import LanguageSwitcher from "./LanguageSwitcher";
|
||||
import { inputCls } from "./ui/form";
|
||||
|
||||
// First-run install wizard. Shown by App.tsx while the instance is unconfigured. The one-time
|
||||
// setup token comes from the URL the app printed to the container logs (?token=…); every step
|
||||
@@ -129,9 +130,6 @@ export default function SetupWizard() {
|
||||
}
|
||||
}
|
||||
|
||||
const inputCls =
|
||||
"w-full bg-card border border-border rounded-xl px-3 py-2 text-sm outline-hidden focus:border-accent";
|
||||
|
||||
return (
|
||||
<Centered>
|
||||
<div className="w-full max-w-lg">
|
||||
|
||||
@@ -211,7 +211,7 @@ function LinkRow({ link, onChanged }: { link: ShareLink; onChanged: () => void }
|
||||
placeholder={t(
|
||||
link.has_password ? "downloads.share.newPassword" : "downloads.share.setPassword",
|
||||
)}
|
||||
className="flex-1 min-w-0 bg-card border border-border rounded-md px-2 py-1 text-xs outline-hidden focus:border-accent"
|
||||
className="flex-1 min-w-0 bg-surface border border-border rounded-md px-2 py-1 text-xs focus:outline-hidden focus:ring-2 focus:ring-accent/40"
|
||||
/>
|
||||
<button
|
||||
onClick={() => savePass.mutate(newPass.trim())}
|
||||
|
||||
@@ -209,7 +209,7 @@ export default function Sidebar({
|
||||
maxAgeDays: undefined,
|
||||
})
|
||||
}
|
||||
className="bg-card border border-border rounded-md px-2 py-1 text-xs outline-hidden focus:border-accent"
|
||||
className="bg-surface border border-border rounded-md px-2 py-1 text-xs focus:outline-hidden focus:ring-2 focus:ring-accent/40"
|
||||
/>
|
||||
</label>
|
||||
<label className="flex items-center justify-between gap-2 text-xs">
|
||||
@@ -224,7 +224,7 @@ export default function Sidebar({
|
||||
maxAgeDays: undefined,
|
||||
})
|
||||
}
|
||||
className="bg-card border border-border rounded-md px-2 py-1 text-xs outline-hidden focus:border-accent"
|
||||
className="bg-surface border border-border rounded-md px-2 py-1 text-xs focus:outline-hidden focus:ring-2 focus:ring-accent/40"
|
||||
/>
|
||||
</label>
|
||||
{(filters.dateFrom || filters.dateTo) && (
|
||||
|
||||
@@ -51,7 +51,7 @@ function TagRow({
|
||||
onChange={(e) => setName(e.target.value)}
|
||||
onBlur={commit}
|
||||
onKeyDown={(e) => e.key === "Enter" && (e.target as HTMLInputElement).blur()}
|
||||
className="flex-1 min-w-0 bg-card border border-border rounded-md px-2 py-1.5 text-sm outline-hidden focus:border-accent"
|
||||
className="flex-1 min-w-0 bg-surface border border-border rounded-md px-2 py-1.5 text-sm focus:outline-hidden focus:ring-2 focus:ring-accent/40"
|
||||
/>
|
||||
<span
|
||||
ref={pop.refs.setReference}
|
||||
@@ -201,7 +201,7 @@ export default function TagManager({
|
||||
value={newName}
|
||||
onChange={(e) => setNewName(e.target.value)}
|
||||
placeholder={t("tagManager.newPlaceholder")}
|
||||
className="flex-1 min-w-0 bg-card border border-border rounded-md px-2 py-1.5 text-sm outline-hidden focus:border-accent"
|
||||
className="flex-1 min-w-0 bg-surface border border-border rounded-md px-2 py-1.5 text-sm focus:outline-hidden focus:ring-2 focus:ring-accent/40"
|
||||
/>
|
||||
<button
|
||||
type="submit"
|
||||
|
||||
@@ -431,7 +431,7 @@ function AuthCard() {
|
||||
}
|
||||
|
||||
const inputCls =
|
||||
"w-full bg-card border border-border rounded-xl px-3 py-2.5 text-sm outline-hidden focus:border-accent";
|
||||
"w-full bg-surface border border-border rounded-xl px-3 py-2.5 text-sm focus:outline-hidden focus:ring-2 focus:ring-accent/40";
|
||||
const titleKey = TITLE_KEY[mode];
|
||||
|
||||
return (
|
||||
|
||||
@@ -148,7 +148,7 @@ function FilterFunnel<T>({
|
||||
value={(value as string) ?? ""}
|
||||
onChange={(e) => onApply(col.key, e.target.value)}
|
||||
placeholder={col.header}
|
||||
className="w-full bg-card border border-border rounded-md px-2 py-1 text-xs outline-hidden focus:border-accent"
|
||||
className="w-full bg-surface border border-border rounded-md px-2 py-1 text-xs focus:outline-hidden focus:ring-2 focus:ring-accent/40"
|
||||
/>
|
||||
)}
|
||||
{f.kind === "select" && (
|
||||
|
||||
@@ -4,11 +4,12 @@ import Tooltip from "../Tooltip";
|
||||
// Shared form/settings primitives, factored out of the many panels that each re-declared
|
||||
// them (Settings, Config, Stats, admin pages, Setup wizard). Visual contract unchanged.
|
||||
|
||||
/** The standard text/select/number input style (full-width, surface bg, accent focus ring) — the
|
||||
* literal string was copy-pasted into DownloadCenter/DownloadDialog/ProfileEditor/ShareDialog/
|
||||
* VideoEditor. NOTE: the settings-family panels (Settings/Config/Setup/Welcome) use a DIFFERENT
|
||||
* `bg-card rounded-xl focus:border-accent` style; unifying the two is a design call left to the
|
||||
* glass-consistency epic, not this DRY extract. */
|
||||
/** The ONE text/select/number input recipe (full-width, surface bg, rounded-lg, 2px accent focus
|
||||
* RING). R10 S3 unified every input onto this: the old settings-family border-focus style
|
||||
* (`bg-card rounded-xl focus:border-accent`) and its three copy-pasted local `inputCls` constants
|
||||
* (KeyGate/SettingsPanel/SetupWizard) are gone. Size variants (a small pager/table-filter/chat
|
||||
* input) keep their own width/radius/padding but adopt the same bg-surface + ring-focus look
|
||||
* inline — reach for this const for a default full-size field, match its look for a smaller one. */
|
||||
export const inputCls =
|
||||
"w-full rounded-lg bg-surface border border-border px-3 py-2 text-sm focus:outline-hidden focus:ring-2 focus:ring-accent/40";
|
||||
|
||||
|
||||
Reference in New Issue
Block a user