diff --git a/docs/BUG_HISTORY.md b/docs/BUG_HISTORY.md index d9a4caf2..ee272ca8 100644 --- a/docs/BUG_HISTORY.md +++ b/docs/BUG_HISTORY.md @@ -7617,3 +7617,19 @@ - 复发自:无 - 修复版本:待发布 +## BUG-495 | 选择题选中项外圈粗描边溢出气泡 + +- 状态:resolved +- 首次发现:2026-09-02 +- 最近更新:2026-09-02 +- 影响面:`.birth-time-choice-option:focus-visible`、嵌入 `RectificationChoiceCard`、`.message-bubble` +- 用户现象:点选 A 后(或新题自动聚焦第一项时),选项外侧出现一圈粗的红棕色描边,圆角对不齐,看起来像边框溢出。已有选中底色和边框已经够用。 +- 触发条件:校正聊天里嵌入选择题;选项在 `.message-bubble { overflow: hidden }` 内。新题会 `focus()` 第一项,点击后按钮仍保持 `:focus-visible`。 +- 根因:`:focus-visible` 使用 `outline-offset: 2px`(会话页还有 `3px`),描边画在按钮外面,被气泡裁切。选中态 `border-color: var(--color-action)` 与焦点色相同,叠成双层粗边。 +- 修复:焦点环改为 `outline-offset: -2px` 画在按钮内。已选中项不再叠加 outline(高对比模式除外)。嵌入卡不再自动聚焦第一项。 +- 验证:`rectification-varga-style-copy` 锁 inset offset、选中无 outline、卡片不 `.focus()`。 +- 防复发:选择题焦点环不得使用正 `outline-offset`。选中态只保留 `data-selected` 的边框和底色。 +- 相关记录:BUG-490 +- 复发自:无 +- 修复版本:待发布 + diff --git a/frontend/src/app/birth-time-choice.css b/frontend/src/app/birth-time-choice.css index 298ce126..1c3998e7 100644 --- a/frontend/src/app/birth-time-choice.css +++ b/frontend/src/app/birth-time-choice.css @@ -11,7 +11,11 @@ .birth-time-choice-option:hover:not(:disabled) { border-color: var(--color-border-strong); background: var(--color-canvas-soft); } .birth-time-choice-option:active:not(:disabled) { transform: scale(.96); } .birth-time-choice-option[data-selected="true"] { border-color: var(--color-action); background: var(--color-action-soft); color: var(--color-ink); } -.birth-time-choice-option:focus-visible { outline: 2px solid var(--color-focus); outline-offset: 2px; } +.birth-time-choice-option:focus-visible { outline: 2px solid var(--color-focus); outline-offset: -2px; } +.birth-time-choice-option[data-selected="true"]:focus-visible { outline: none; } +@media (forced-colors: active) { + .birth-time-choice-option[data-selected="true"]:focus-visible { outline: 2px solid Highlight; outline-offset: -2px; } +} .birth-time-choice-question:disabled .birth-time-choice-option { cursor: wait; opacity: .62; } .birth-time-choice-pending { margin: 0; color: var(--color-action); font-size: var(--type-caption); } .birth-time-unmatched-note { display: grid; gap: var(--space-2); color: var(--color-ink-secondary); font-size: var(--type-caption); font-weight: 600; } diff --git a/frontend/src/app/globals.css b/frontend/src/app/globals.css index c279a957..f01ca91a 100644 --- a/frontend/src/app/globals.css +++ b/frontend/src/app/globals.css @@ -930,6 +930,8 @@ button:disabled { cursor: default; opacity: .45; } .conversational-rectification > *, .conversational-rectification form, .conversational-rectification fieldset { min-width: 0; max-width: 100%; } .conversational-rectification button { min-height: 44px; max-width: 100%; overflow-wrap: anywhere; } .conversational-rectification :where(button, textarea):focus-visible { outline: 3px solid color-mix(in srgb, var(--color-focus) 56%, transparent); outline-offset: 3px; } +.conversational-rectification .birth-time-choice-option:focus-visible { outline: 2px solid var(--color-focus); outline-offset: -2px; } +.conversational-rectification .birth-time-choice-option[data-selected="true"]:focus-visible { outline: none; } .conversational-narrative { overflow: hidden; border: 1px solid var(--color-border); border-radius: var(--radius-lg); background: var(--color-canvas); box-shadow: var(--shadow-soft); } .conversational-narrative-body { padding: var(--space-4) var(--space-5); } .conversational-narrative .message-markdown { overflow-wrap: anywhere; color: var(--color-ink-secondary); font-size: var(--type-body-sm); line-height: 1.8; } diff --git a/frontend/src/components/rectification-choice-card.tsx b/frontend/src/components/rectification-choice-card.tsx index 7c88ce97..976198a8 100644 --- a/frontend/src/components/rectification-choice-card.tsx +++ b/frontend/src/components/rectification-choice-card.tsx @@ -1,6 +1,6 @@ "use client"; -import { useEffect, useRef, useState } from "react"; +import { useState } from "react"; import type { ChoiceKey, RectificationChoiceCard as ChoiceCard, @@ -20,14 +20,8 @@ type RectificationChoiceCardProps = Readonly<{ export function RectificationChoiceCard(props: RectificationChoiceCardProps) { const [localSelected, setLocalSelected] = useState(""); const selectedKey = props.selectedKey || localSelected; - const firstChoiceRef = useRef(null); const answered = Boolean(selectedKey); - useEffect(() => { - if (props.pending || answered) return; - firstChoiceRef.current?.focus(); - }, [answered, props.card.question_id, props.pending]); - function select(key: ChoiceKey) { if (props.pending || props.disabled || selectedKey) return; setLocalSelected(key); @@ -58,10 +52,9 @@ export function RectificationChoiceCard(props: RectificationChoiceCardProps) { : {props.card.prompt}} {!hidePrompt && props.card.why ?

{props.card.why}

: null}
- {props.card.options.map((option, index) => ( + {props.card.options.map((option) => (