diff --git a/frontend/eslint.config.js b/frontend/eslint.config.js index f134ed21..a8678c72 100644 --- a/frontend/eslint.config.js +++ b/frontend/eslint.config.js @@ -221,6 +221,18 @@ export default tseslint.config( // The canonical "FE control" row (CLAUDE.md): a short fixed set is a SegmentedToggle, a // longer one or a user's own list a FilterSelect. Six native selects had drifted in; the // 2026-09 debt burn replaced them, and this keeps the next one out. + // `enabled:` sees only the native attribute; a FocusSafeButton that holds the focus is + // disabled by aria-disabled, so a bare `enabled:hover:` lit it up on hover as if it worked. + { + selector: "Literal[value=/(^|\\s)enabled:hover:/]", + message: + "`enabled:hover:` misses a focused FocusSafeButton (aria-disabled): use `enabled:not-aria-disabled:hover:`.", + }, + { + selector: "TemplateElement[value.raw=/(^|\\s)enabled:hover:/]", + message: + "`enabled:hover:` misses a focused FocusSafeButton (aria-disabled): use `enabled:not-aria-disabled:hover:`.", + }, { selector: "JSXOpeningElement[name.name='select']", message: diff --git a/frontend/src/components/PersonalViewsModal.tsx b/frontend/src/components/PersonalViewsModal.tsx index e87d6b1b..e0520207 100644 --- a/frontend/src/components/PersonalViewsModal.tsx +++ b/frontend/src/components/PersonalViewsModal.tsx @@ -240,7 +240,7 @@ export default function PersonalViewsModal() { disabled={!nameChanged} title={t("views.rename")} aria-label={t("views.rename")} - className="inline-flex shrink-0 items-center rounded-lg border border-border p-2 text-muted transition enabled:hover:border-accent enabled:hover:text-accent disabled:opacity-30" + className="inline-flex shrink-0 items-center rounded-lg border border-border p-2 text-muted transition enabled:not-aria-disabled:hover:border-accent enabled:not-aria-disabled:hover:text-accent disabled:opacity-30" > @@ -302,7 +302,7 @@ export default function PersonalViewsModal() { title={ saveCurrentDisabled ? t("views.saveCurrentUpToDate") : t("views.saveCurrentHint") } - className="inline-flex items-center gap-1.5 rounded-lg border border-border px-3 py-1.5 text-sm text-muted transition enabled:hover:border-accent enabled:hover:text-accent disabled:opacity-40" + className="inline-flex items-center gap-1.5 rounded-lg border border-border px-3 py-1.5 text-sm text-muted transition enabled:not-aria-disabled:hover:border-accent enabled:not-aria-disabled:hover:text-accent disabled:opacity-40" > {t("views.saveCurrent")} diff --git a/frontend/src/components/PlaylistLimitsHint.tsx b/frontend/src/components/PlaylistLimitsHint.tsx index e9682ce3..64ca4cfb 100644 --- a/frontend/src/components/PlaylistLimitsHint.tsx +++ b/frontend/src/components/PlaylistLimitsHint.tsx @@ -21,7 +21,12 @@ export default function PlaylistLimitsHint() { offset: 8, }); return ( -
+ // The panel is placed off the whole row, not the "Why?" button: off the button it opened + // over the right third of the rail and covered the lists (measured on localdev). +