Skip to content

[PI-82408] 전체 컴포넌트에 VoiceOver 접근성 지원 추가#450

Merged
knine79 merged 4 commits intomainfrom
feature/PI-82408
Mar 20, 2026
Merged

[PI-82408] 전체 컴포넌트에 VoiceOver 접근성 지원 추가#450
knine79 merged 4 commits intomainfrom
feature/PI-82408

Conversation

@knine79
Copy link
Copy Markdown
Contributor

@knine79 knine79 commented Mar 20, 2026

개요

수정사항

  • 인터랙티브 컴포넌트(Button, IconButton, Chip, FilterButton 등)에 .accessibilityElement, .accessibilityLabel, .accessibilityAddTraits(.isButton) 적용
  • Selection 컴포넌트(Control, Checkbox, Radio, Switch 등)에 .accessibilityValue로 선택 상태 전달
  • 입력 컴포넌트(TextField, TextArea, Select)에 heading 기반 접근성 레이블 및 상태 값 적용
  • 네비게이션 컴포넌트(Tab, SegmentedControl)에 .isSelected trait 적용
  • 콘텐츠 컴포넌트(Card, ListCard, ListCell)에 .accessibilityElement(children: .combine) 적용
  • Loading, Skeleton에 "로딩 중" 접근성 레이블 추가
  • Toast에 .accessibilityElement(children: .combine) 적용
  • 고정 한국어 문자열을 String(localized:bundle:.module)로 현지화 준비
  • Localizable.xcstrings 파일 추가 및 Package.swiftdefaultLocalization: "ko" 설정

미리보기

접근성 변경은 시각적 차이 없음 — Accessibility Inspector 또는 VoiceOver로 검증

Summary by CodeRabbit

  • 새로운 기능

    • 다수 UI 컴포넌트에 접근성 메타데이터 추가(레이블, 값, 그룹화, 트레잇, 조정 액션)로 스크린리더·보조기술 지원 향상
    • 슬라이더에 접근성 조정(증감) 동작 및 선택 상태 트레잇 개선
  • 지역화

    • 패키지 기본 로케일을 한국어로 설정
    • 주요 UI 문자열(로딩, 선택 상태, 오류, 뒤로 가기, 아이콘, 프로필/회사/학원 등) 한국어 리소스 추가

@knine79 knine79 requested a review from a team as a code owner March 20, 2026 07:01
@knine79 knine79 requested review from jhseo and szzang-kr and removed request for a team March 20, 2026 07:01
@knine79 knine79 added the in review This issue requires a review. label Mar 20, 2026
@knine79 knine79 self-assigned this Mar 20, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 20, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: e8ee7afe-62c2-4c9d-af02-ce17cb4481bd

📥 Commits

Reviewing files that changed from the base of the PR and between 0f2298c and e561d68.

📒 Files selected for processing (1)
  • Sources/Montage/1 Components/3 Selection And Input/Slider.swift
🚧 Files skipped from review as they are similar to previous changes (1)
  • Sources/Montage/1 Components/3 Selection And Input/Slider.swift

Walkthrough

패키지 기본 로컬라이제이션을 한국어로 설정하고, 다수의 SwiftUI 컴포넌트에 접근성 레이블·값·트레이트 및 접근성 조정 로직을 추가하며 한국어 로컬라이제이션 리소스를 도입합니다.

Changes

Cohort / File(s) Summary
Package Configuration
Package.swift
패키지 매니페스트에 defaultLocalization: "ko" 추가.
Action Components
Sources/Montage/1 Components/2 Actions/Button.swift, Sources/Montage/1 Components/2 Actions/Chip.swift, Sources/Montage/1 Components/2 Actions/IconButton.swift, Sources/Montage/1 Components/2 Actions/FilterButton.swift
버튼형 컴포넌트들에 자식 접근성 무시(children: .ignore) 및 접근성 레이블·트레이트(.isButton)·상태값(로딩/선택 등) 추가; 일부 소소한 공백 제거.
Selection / Input
Sources/Montage/1 Components/3 Selection And Input/Control.swift, .../SegmentedControl.swift, .../Select.swift, .../Slider.swift, .../TextArea.swift, .../TextField.swift
Toggle 라벨 숨김(.labelsHidden) 처리(.switch), 각 컨트롤에 접근성 레이블·값 및 상태 설명(선택/부분선택/오류 등) 추가. Slider에 접근성용 조정 액션 핸들러와 SliderAccessibilityModifier 추가(범위 슬라이더 핸들링 포함).
Content Components
Sources/Montage/1 Components/4 Contents/Avatar.swift, Sources/Montage/1 Components/4 Contents/AvatarGroup.swift, Sources/Montage/1 Components/4 Contents/Card.swift, Sources/Montage/1 Components/4 Contents/ListCard.swift, Sources/Montage/1 Components/4 Contents/ListCell.swift
아바타·카드·리스트 뷰에 접근성 그룹화(children: .ignore / .combine) 적용; Avatar에 variant 기반 접근성 설명 추가 및 탭 가능 시 .isButton 조건 적용 (Variant.accessibilityDescription 추가).
Loading / Skeleton
Sources/Montage/1 Components/5 Loading/Loading.swift, Sources/Montage/1 Components/5 Loading/Skeleton.swift
로딩 관련 뷰들에 accessibilityLabel("로딩 중") 추가.
Navigation
Sources/Montage/1 Components/6 Navigations/Tab.swift, Sources/Montage/1 Components/6 Navigations/TopNavigation.swift
탭 항목에 버튼 트레이트 및 선택 상태 반영(.isSelected 추가/제거); 뒤로 가기 버튼에 접근성 레이블("뒤로 가기") 추가.
Feedback / Presentation
Sources/Montage/1 Components/7 Feedback/Toast.swift, Sources/Montage/1 Components/8 Presentation/Tooltip.swift
Toast 콘텐츠를 결합하여 접근성 노출(accessibilityElement(children: .combine)) 및 정적 텍스트 트레이트 추가; Tooltip에 메시지 접근성 레이블 추가.
Localization Resources
Sources/Montage/Asset/Localizable.xcstrings
한국어(sourceLanguage: "ko") 로컬라이제이션 파일 추가 — 키: "로딩 중", "부분 선택됨", "선택 안 됨", "선택됨", "슬라이더", "아이콘", "오류", "뒤로 가기", "프로필 이미지", "회사 로고", "학원 로고".

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 60.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed PR 제목은 전체 변경 사항의 주요 목적인 VoiceOver 접근성 지원 추가를 명확하게 반영하며, 모든 컴포넌트에 대한 접근성 개선이라는 핵심 변경 사항을 정확하게 설명합니다.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/PI-82408
📝 Coding Plan
  • Generate coding plan for human review comments

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
Sources/Montage/1 Components/3 Selection And Input/Control.swift (1)

94-110: ⚠️ Potential issue | 🟠 Major

.switch variant에 접근성 레이블과 값이 누락되었습니다.

.labelsHidden()은 시각적 레이블만 숨기고 VoiceOver 레이블을 제공하지 않습니다. 다른 variant(checkbox, radio, checkmark)와 달리 .switch에는 accessibilityLabel, accessibilityValue 등이 적용되지 않아 VoiceOver 사용자가 스위치의 용도와 상태를 알 수 없습니다.

♿ 접근성 수정 제안
 Toggle(
     "",
     isOn: Binding(
         get: {
             !state.isUnchecked
         },
         set: {
             onSelect?($0 ? .checked : .unchecked)
         })
 )
 .labelsHidden()
 .disabled(disable)
 .tint(backgroundColor)
 .frame(width: switchSize.width, height: switchSize.height)
 // ... 기존 코드 ...
 .frame(width: boxSize.width, height: boxSize.height)
+.accessibilityLabel(label)
+.accessibilityValue(accessibilityStateDescription)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Sources/Montage/1` Components/3 Selection And Input/Control.swift around
lines 94 - 110, The .switch variant currently only uses .labelsHidden() in the
modifier chain (after
.tint(...).frame(...).offset(...).transformEffect(...).frame(...).offset(...))
and lacks VoiceOver metadata; update the switch view’s modifier chain to add
appropriate accessibility modifiers (e.g., .accessibilityLabel(...) using the
switch’s descriptive label text and .accessibilityValue(...) or
.accessibilityState(...) reflecting its on/off state, and
.accessibilityHint(...) if needed) so screen readers announce both purpose and
current state—apply these to the same view that calls .labelsHidden() to mirror
how checkbox/radio variants expose accessibility information.
🧹 Nitpick comments (2)
Sources/Montage/1 Components/6 Navigations/Tab.swift (1)

118-120: 탭 접근성 구현이 올바릅니다.

.isButton.isSelected trait 적용이 적절합니다. 다만, 선택 상태 처리 로직을 더 간결하게 작성할 수 있습니다.

♻️ 선택적 리팩토링 제안
 .accessibilityAddTraits(.isButton)
-.accessibilityAddTraits(index == selectedIndex ? .isSelected : [])
-.accessibilityRemoveTraits(index == selectedIndex ? [] : .isSelected)
+.accessibilityAddTraits(index == selectedIndex ? [.isButton, .isSelected] : .isButton)

또는 현재 방식을 유지해도 동작에는 문제가 없습니다.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Sources/Montage/1` Components/6 Navigations/Tab.swift around lines 118 - 120,
The accessibility trait handling for the tab is correct but verbose; simplify by
computing the combined trait set from index and selectedIndex and applying it
once instead of adding then removing traits. Replace the three calls
(.accessibilityAddTraits(.isButton), .accessibilityAddTraits(index ==
selectedIndex ? .isSelected : []), .accessibilityRemoveTraits(index ==
selectedIndex ? [] : .isSelected)) with a single application that composes
.isButton plus .isSelected when index == selectedIndex (use the local index and
selectedIndex variables or build an AccessibilityTraits value) and call the
appropriate accessibility modifier once.
Sources/Montage/1 Components/3 Selection And Input/TextArea.swift (1)

432-433: heading이 없을 때 접근성 레이블이 비어있음

headingnil인 경우 접근성 레이블이 빈 문자열이 됩니다. placeholder를 fallback으로 사용하면 VoiceOver 사용자에게 텍스트 영역의 목적을 전달할 수 있습니다.

♿ Placeholder를 fallback 레이블로 사용
-        .accessibilityLabel(heading ?? "")
+        .accessibilityLabel(heading ?? placeholder ?? "")
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Sources/Montage/1` Components/3 Selection And Input/TextArea.swift around
lines 432 - 433, The accessibilityLabel currently uses heading ?? "" which
yields an empty label when heading is nil; update the TextArea's
accessibilityLabel call to fallback to the placeholder (e.g., use heading ??
placeholder ?? "") so VoiceOver receives a meaningful label; locate the
accessibilityLabel(...) invocation in TextArea.swift and replace the fallback
logic so it uses placeholder when heading is nil.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@Sources/Montage/1` Components/2 Actions/Button.swift:
- Around line 368-371: The Button's accessibilityLabel currently uses
accessibilityLabel(text ?? "") which yields an empty string for icon-only
buttons; update the Button initializers and view modifiers (in Button.swift, the
Button component/initializer used for icon-only buttons and the view body
applying .accessibilityLabel) to accept an optional accessibilityLabel parameter
(or compute a fallback) and change the modifier to use
accessibilityLabel(customAccessibilityLabel ?? text ?? iconAccessibilityName ??
String(localized: "Button", bundle: .module)) so icon-only buttons expose a
descriptive label (use the icon's name or a provided custom label as fallback)
and ensure .accessibilityValue(loading ? ...) remains unchanged.

In `@Sources/Montage/1` Components/2 Actions/IconButton.swift:
- Around line 225-227: Add an optional accessibilityLabel parameter to the
IconButton initializer (e.g., public init(..., icon: Icon, accessibilityLabel:
String? = nil, handler: (() -> Void)? = nil)) and change the view’s modifier
from .accessibilityLabel(icon.rawValue) to
.accessibilityLabel(accessibilityLabel ?? icon.rawValue) so callers can supply
meaningful, localized labels (or continue to fall back to the enum rawValue);
alternatively implement a localized label property on the Icon enum and use that
when accessibilityLabel is nil.

In `@Sources/Montage/1` Components/3 Selection And Input/Slider.swift:
- Around line 235-236: The slider's accessibility currently only sets
accessibilityValue(headingLabel) and ignores adjustable behavior; update the
Slider view to add .accessibilityAddTraits(.adjustable), provide a descriptive
.accessibilityLabel (e.g., use headingLabel or a dedicated label string) and
implement .accessibilityAdjustableAction to call the same increment/decrement
logic used by the slider (use the Slider struct's value-binding/step handlers or
the existing increase/decrease methods) so VoiceOver users can perform
.increment and .decrement actions while preserving .accessibilityValue updates.

In `@Sources/Montage/1` Components/4 Contents/Avatar.swift:
- Around line 178-180: The Avatar view currently hides children with
.accessibilityElement(children: .ignore) and conditionally adds
.accessibilityAddTraits(.isButton) but lacks an accessibility label; update
Avatar to accept an optional accessibilityLabel parameter (or provide a
modifier) and in the view body apply .accessibilityLabel(accessibilityLabel ??
defaultLabel(for: variant)) so VoiceOver gets either the passed label or a
sensible default derived from the variant (use the existing variant enum and
onTap presence to build defaults like "User avatar", "Company logo", etc.);
ensure the .accessibilityAddTraits(.isButton) remains when onTap != nil and that
the new accessibilityLabel is applied alongside the existing
.accessibilityElement call.

In `@Sources/Montage/1` Components/4 Contents/AvatarGroup.swift:
- Line 143: The AvatarGroup view currently uses .accessibilityElement(children:
.ignore) without providing a label, so VoiceOver users can't know the group or
its count; update the AvatarGroup SwiftUI view to supply an accessibility
label/value (e.g., use .accessibilityLabel and/or .accessibilityValue on the
view that calls .accessibilityElement(children: .ignore)) that summarizes the
group (for example "3 avatars" or a descriptive string built from the view's
avatars/participants property), and ensure the label updates when the underlying
avatars/participants collection or count changes (reference the AvatarGroup
struct and the place where .accessibilityElement(children: .ignore) is applied).

---

Outside diff comments:
In `@Sources/Montage/1` Components/3 Selection And Input/Control.swift:
- Around line 94-110: The .switch variant currently only uses .labelsHidden() in
the modifier chain (after
.tint(...).frame(...).offset(...).transformEffect(...).frame(...).offset(...))
and lacks VoiceOver metadata; update the switch view’s modifier chain to add
appropriate accessibility modifiers (e.g., .accessibilityLabel(...) using the
switch’s descriptive label text and .accessibilityValue(...) or
.accessibilityState(...) reflecting its on/off state, and
.accessibilityHint(...) if needed) so screen readers announce both purpose and
current state—apply these to the same view that calls .labelsHidden() to mirror
how checkbox/radio variants expose accessibility information.

---

Nitpick comments:
In `@Sources/Montage/1` Components/3 Selection And Input/TextArea.swift:
- Around line 432-433: The accessibilityLabel currently uses heading ?? "" which
yields an empty label when heading is nil; update the TextArea's
accessibilityLabel call to fallback to the placeholder (e.g., use heading ??
placeholder ?? "") so VoiceOver receives a meaningful label; locate the
accessibilityLabel(...) invocation in TextArea.swift and replace the fallback
logic so it uses placeholder when heading is nil.

In `@Sources/Montage/1` Components/6 Navigations/Tab.swift:
- Around line 118-120: The accessibility trait handling for the tab is correct
but verbose; simplify by computing the combined trait set from index and
selectedIndex and applying it once instead of adding then removing traits.
Replace the three calls (.accessibilityAddTraits(.isButton),
.accessibilityAddTraits(index == selectedIndex ? .isSelected : []),
.accessibilityRemoveTraits(index == selectedIndex ? [] : .isSelected)) with a
single application that composes .isButton plus .isSelected when index ==
selectedIndex (use the local index and selectedIndex variables or build an
AccessibilityTraits value) and call the appropriate accessibility modifier once.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: f257ce34-69e5-43b5-ad0a-ff529c91ec30

📥 Commits

Reviewing files that changed from the base of the PR and between e385702 and 9c7e97c.

📒 Files selected for processing (23)
  • Package.swift
  • Sources/Montage/1 Components/2 Actions/Button.swift
  • Sources/Montage/1 Components/2 Actions/Chip.swift
  • Sources/Montage/1 Components/2 Actions/IconButton.swift
  • Sources/Montage/1 Components/3 Selection And Input/Control.swift
  • Sources/Montage/1 Components/3 Selection And Input/FilterButton.swift
  • Sources/Montage/1 Components/3 Selection And Input/SegmentedControl.swift
  • Sources/Montage/1 Components/3 Selection And Input/Select.swift
  • Sources/Montage/1 Components/3 Selection And Input/Slider.swift
  • Sources/Montage/1 Components/3 Selection And Input/TextArea.swift
  • Sources/Montage/1 Components/3 Selection And Input/TextField.swift
  • Sources/Montage/1 Components/4 Contents/Avatar.swift
  • Sources/Montage/1 Components/4 Contents/AvatarGroup.swift
  • Sources/Montage/1 Components/4 Contents/Card.swift
  • Sources/Montage/1 Components/4 Contents/ListCard.swift
  • Sources/Montage/1 Components/4 Contents/ListCell.swift
  • Sources/Montage/1 Components/5 Loading/Loading.swift
  • Sources/Montage/1 Components/5 Loading/Skeleton.swift
  • Sources/Montage/1 Components/6 Navigations/Tab.swift
  • Sources/Montage/1 Components/6 Navigations/TopNavigation.swift
  • Sources/Montage/1 Components/7 Feedback/Toast.swift
  • Sources/Montage/1 Components/8 Presentation/Tooltip.swift
  • Sources/Montage/Asset/Localizable.xcstrings

@knine79 knine79 added this to the 3.4.2 milestone Mar 20, 2026
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

♻️ Duplicate comments (1)
Sources/Montage/1 Components/3 Selection And Input/Slider.swift (1)

235-251: ⚠️ Potential issue | 🟠 Major

.accessibilityLabel 누락으로 슬라이더 용도 설명 불가

accessibilityValue만 설정되어 있어 VoiceOver 사용자가 이 컨트롤이 무엇인지 알 수 없습니다. 슬라이더의 목적을 설명하는 레이블을 추가해야 합니다.

또한 disable 상태일 때 VoiceOver에 비활성화 상태가 전달되지 않습니다.

♿ 접근성 개선 제안
 .allowsHitTesting(!disable)
 .accessibilityElement(children: .ignore)
+.accessibilityLabel(String(localized: "슬라이더", bundle: .module))
 .accessibilityValue(headingLabel)
+.accessibilityAddTraits(disable ? .isNotEnabled : [])
 .accessibilityAdjustableAction { direction in
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Sources/Montage/1` Components/3 Selection And Input/Slider.swift around lines
235 - 251, Accessibility is missing a label and disabled state reporting: add
.accessibilityLabel(...) alongside the existing .accessibilityValue(...) (e.g.,
use the control's descriptive text instead of only the value) and when the
slider is not interactive add the disabled trait via
.accessibilityAddTraits(.isDisabled) (or .accessibilityAddTraits(.notEnabled) if
your API variant uses that); update the chain around
.accessibilityElement(children: .ignore), .accessibilityValue(headingLabel), and
.accessibilityAdjustableAction to include .accessibilityLabel(headingLabel) and
a conditional .accessibilityAddTraits(.isDisabled) tied to your slider’s
enabled/disabled flag (e.g., isEnabled or isDisabled).
🧹 Nitpick comments (1)
Sources/Montage/1 Components/3 Selection And Input/Slider.swift (1)

237-250: 범위 슬라이더의 VoiceOver 조작이 제한적입니다

현재 구현에서:

  • Increment: thumbRatio2(상한값)만 증가
  • Decrement: thumbRatio1(하한값)만 감소

이 로직은 범위를 확장만 할 수 있고 축소는 불가능합니다. VoiceOver 사용자가 양쪽 thumb을 자유롭게 조절할 수 없어 시각 사용자와 동등한 제어가 어렵습니다.

💡 대안 제안: 마지막으로 선택한 thumb 기준 조절
 .accessibilityAdjustableAction { direction in
     let step = 0.05
     switch direction {
     case .increment:
-        thumbRatio2 = min(1.0, thumbRatio2 + step)
+        if isRangeSlider {
+            if focusedThumb == 1 {
+                thumbRatio1 = min(thumbRatio2, thumbRatio1 + step)
+            } else {
+                thumbRatio2 = min(1.0, thumbRatio2 + step)
+            }
+        } else {
+            thumbRatio2 = min(1.0, thumbRatio2 + step)
+        }
     case .decrement:
         if isRangeSlider {
-            thumbRatio1 = max(0.0, thumbRatio1 - step)
+            if focusedThumb == 1 {
+                thumbRatio1 = max(0.0, thumbRatio1 - step)
+            } else {
+                thumbRatio2 = max(thumbRatio1, thumbRatio2 - step)
+            }
         } else {
             thumbRatio2 = max(0.0, thumbRatio2 - step)
         }
     `@unknown` default:
         break
     }
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Sources/Montage/1` Components/3 Selection And Input/Slider.swift around lines
237 - 250, The VoiceOver adjustable action currently only moves thumbRatio2 on
increment and moves thumbRatio1 (if range) or thumbRatio2 on decrement,
preventing shrinking a range; update the accessibilityAdjustableAction handler
to operate on whichever thumb was last selected (introduce or use a
lastSelectedThumb or selectedThumb enum/flag alongside thumbRatio1 and
thumbRatio2) so both increment and decrement apply to that active thumb, clamp
values within 0.0...1.0 and preserve range ordering (swap or constrain the other
thumb if needed) when isRangeSlider is true; ensure the same lastSelectedThumb
is set when a thumb is tapped/driven so VoiceOver adjusts the intended thumb.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@Sources/Montage/1` Components/3 Selection And Input/Slider.swift:
- Around line 235-251: Accessibility is missing a label and disabled state
reporting: add .accessibilityLabel(...) alongside the existing
.accessibilityValue(...) (e.g., use the control's descriptive text instead of
only the value) and when the slider is not interactive add the disabled trait
via .accessibilityAddTraits(.isDisabled) (or
.accessibilityAddTraits(.notEnabled) if your API variant uses that); update the
chain around .accessibilityElement(children: .ignore),
.accessibilityValue(headingLabel), and .accessibilityAdjustableAction to include
.accessibilityLabel(headingLabel) and a conditional
.accessibilityAddTraits(.isDisabled) tied to your slider’s enabled/disabled flag
(e.g., isEnabled or isDisabled).

---

Nitpick comments:
In `@Sources/Montage/1` Components/3 Selection And Input/Slider.swift:
- Around line 237-250: The VoiceOver adjustable action currently only moves
thumbRatio2 on increment and moves thumbRatio1 (if range) or thumbRatio2 on
decrement, preventing shrinking a range; update the
accessibilityAdjustableAction handler to operate on whichever thumb was last
selected (introduce or use a lastSelectedThumb or selectedThumb enum/flag
alongside thumbRatio1 and thumbRatio2) so both increment and decrement apply to
that active thumb, clamp values within 0.0...1.0 and preserve range ordering
(swap or constrain the other thumb if needed) when isRangeSlider is true; ensure
the same lastSelectedThumb is set when a thumb is tapped/driven so VoiceOver
adjusts the intended thumb.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 53a6521c-3e2e-4b73-a523-8c9a4f047b09

📥 Commits

Reviewing files that changed from the base of the PR and between 9c7e97c and 3cb63b7.

📒 Files selected for processing (6)
  • Sources/Montage/1 Components/2 Actions/Button.swift
  • Sources/Montage/1 Components/2 Actions/IconButton.swift
  • Sources/Montage/1 Components/3 Selection And Input/Slider.swift
  • Sources/Montage/1 Components/4 Contents/Avatar.swift
  • Sources/Montage/1 Components/4 Contents/AvatarGroup.swift
  • Sources/Montage/Asset/Localizable.xcstrings
🚧 Files skipped from review as they are similar to previous changes (4)
  • Sources/Montage/1 Components/4 Contents/AvatarGroup.swift
  • Sources/Montage/1 Components/2 Actions/Button.swift
  • Sources/Montage/1 Components/2 Actions/IconButton.swift
  • Sources/Montage/Asset/Localizable.xcstrings

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@Sources/Montage/1` Components/3 Selection And Input/Slider.swift:
- Around line 279-299: handleAccessibilityAdjust currently lets VoiceOver adjust
the slider even when disable == true; add an early check at the start of
handleAccessibilityAdjust to return immediately if disable is true (or otherwise
prevents adjustments when disabled), so no changes are made to
thumbRatio1/thumbRatio2 when disabled; keep the existing logic for
isRangeSlider, focusedThumb, and the increment/decrement branches intact but
gated by the disable check (also remove reliance on .allowsHitTesting(!disable)
for accessibility).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 73dcc376-01f6-4a03-8dac-9e1d07e82af4

📥 Commits

Reviewing files that changed from the base of the PR and between 3cb63b7 and 0f2298c.

📒 Files selected for processing (2)
  • Sources/Montage/1 Components/3 Selection And Input/Slider.swift
  • Sources/Montage/Asset/Localizable.xcstrings
🚧 Files skipped from review as they are similar to previous changes (1)
  • Sources/Montage/Asset/Localizable.xcstrings

@szzang-kr szzang-kr added accepted This issue has been reviewed. and removed in review This issue requires a review. labels Mar 20, 2026
@knine79 knine79 merged commit 0b3bf32 into main Mar 20, 2026
6 checks passed
@knine79 knine79 deleted the feature/PI-82408 branch March 20, 2026 08:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

accepted This issue has been reviewed. AI Review Completed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants