Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
d51b010
feat: add a11y for course details & scores
Ahmed-Naguib93 Dec 3, 2025
99e8e25
Merge branch 'master' into feature/career-accessibility-course-detail…
Ahmed-Naguib93 Dec 3, 2025
a88fd26
fix: review comment
Ahmed-Naguib93 Dec 3, 2025
bf023aa
address code review comment
Ahmed-Naguib93 Dec 3, 2025
028f7a7
feat(Accessibility): Add accessibility to IntroBlock
Ahmed-Naguib93 Dec 3, 2025
df7696d
Add a11y for module item sequence
Ahmed-Naguib93 Dec 4, 2025
046ae55
address code review
Ahmed-Naguib93 Dec 4, 2025
7ed0105
Add a11y for assignments
Ahmed-Naguib93 Dec 8, 2025
249fa32
fix: code review comments
Ahmed-Naguib93 Dec 8, 2025
e6b4f4c
Fix: notebook a11y
Ahmed-Naguib93 Dec 8, 2025
3608b54
fix typos
Ahmed-Naguib93 Dec 8, 2025
7fc4043
Merge branch 'master' into feature/fix-accessibility-notebook
Ahmed-Naguib93 Dec 10, 2025
54dffff
add missed localization
Ahmed-Naguib93 Dec 10, 2025
05a200b
Merge branch 'feature/fix-accessibility-notebook' into feature/career…
Ahmed-Naguib93 Dec 13, 2025
7dbf3b7
feat: add programs a11y
Ahmed-Naguib93 Dec 15, 2025
1236855
Merge branch 'master' into feature/fix-accessibility-notebook
Ahmed-Naguib93 Dec 15, 2025
c9801e7
Merge branch 'feature/fix-accessibility-notebook' into feature/career…
Ahmed-Naguib93 Dec 15, 2025
eb271cd
fix: a11y
Ahmed-Naguib93 Dec 15, 2025
a6500b3
address code review comments
Ahmed-Naguib93 Dec 15, 2025
d0fff57
Fix: typos
Ahmed-Naguib93 Dec 15, 2025
62733c9
Merge branch 'master' into feature/career-program-a11y-CLX-2888
Ahmed-Naguib93 Dec 16, 2025
ef80497
fix: conflicts
Ahmed-Naguib93 Dec 16, 2025
ff73613
add missed localization
Ahmed-Naguib93 Dec 16, 2025
2e79b17
address code review comments
Ahmed-Naguib93 Dec 18, 2025
ed291cf
fix: selected tabs
Ahmed-Naguib93 Dec 18, 2025
bd80a69
Merge branch 'master' into feature/career-program-a11y-CLX-2888
Ahmed-Naguib93 Dec 19, 2025
841d0a5
Fix: program a11y
Ahmed-Naguib93 Dec 19, 2025
be4074c
make enroll course to have its own focus indicator
Ahmed-Naguib93 Dec 19, 2025
3957767
Add ordinalString
Ahmed-Naguib93 Dec 26, 2025
b555409
feat: add a11y at Ai chatboat
Ahmed-Naguib93 Dec 26, 2025
f419bbc
Merge branch 'master' into feature/career-add-a11y-chatbot-CLX-3720
Ahmed-Naguib93 Jan 6, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions Horizon/Horizon/Resources/Localizable.xcstrings
Original file line number Diff line number Diff line change
Expand Up @@ -24175,6 +24175,10 @@
}
}
},
"chat Bot" : {
"comment" : "Title for the chat bot tab in the horizon tab bar.",
"isCommentAutoGenerated" : true
},
"Chatham Is. (+12:45/+13:45)" : {
"localizations" : {
"ar" : {
Expand Down Expand Up @@ -29842,6 +29846,10 @@
}
}
},
"Correct answer" : {
"comment" : "Text read out loud when a quiz answer is correct.",
"isCommentAutoGenerated" : true
},
"Count of items is %d" : {
"comment" : "The number of items in a module.",
"isCommentAutoGenerated" : true
Expand Down Expand Up @@ -35560,6 +35568,10 @@
"comment" : "Text that describes an action to be taken to expand a collapsed view.",
"isCommentAutoGenerated" : true
},
"Double tap to flip" : {
"comment" : "A hint that appears when a user taps on a flashcard to instruct them to flip it.",
"isCommentAutoGenerated" : true
},
"Double tap to load more courses" : {
"comment" : "An accessibility hint for the \"Show more\" button in the course list view.",
"isCommentAutoGenerated" : true,
Expand Down Expand Up @@ -37368,6 +37380,10 @@
"comment" : "A description of the action to take to post a comment.",
"isCommentAutoGenerated" : true
},
"Double tap to start typing." : {
"comment" : "A hint text that appears when the user taps on the text input field in the Assist Chat view.",
"isCommentAutoGenerated" : true
},
"Double tap to view details" : {
"localizations" : {
"ar" : {
Expand Down Expand Up @@ -48170,6 +48186,10 @@
}
}
},
"Go to next card" : {
"comment" : "A button label that translates to \"Go to next card\".",
"isCommentAutoGenerated" : true
},
"Go to next comments page" : {
"comment" : "A label describing a button that navigates to the next page of comments.",
"isCommentAutoGenerated" : true
Expand All @@ -48182,6 +48202,10 @@
"comment" : "Label for a button that navigates to the next module in a course.",
"isCommentAutoGenerated" : true
},
"Go to previous card" : {
"comment" : "A label describing an action to go to the previous card in a list.",
"isCommentAutoGenerated" : true
},
"Go to previous comments page" : {
"comment" : "A label for a button that navigates to the previous page of comments.",
"isCommentAutoGenerated" : true
Expand Down Expand Up @@ -59476,6 +59500,10 @@
}
}
},
"Like" : {
"comment" : "A label describing an action that likes something.",
"isCommentAutoGenerated" : true
},
"Lima (-05:00/-05:00)" : {
"localizations" : {
"ar" : {
Expand Down Expand Up @@ -73868,6 +73896,10 @@
}
}
},
"Not correct" : {
"comment" : "Text displayed as a hint when a user selects an answer that is not correct.",
"isCommentAutoGenerated" : true
},
"Not Enrolled" : {
"comment" : "Name of the program card status when the user is not enrolled in the program.",
"isCommentAutoGenerated" : true
Expand Down Expand Up @@ -113786,6 +113818,10 @@
}
}
},
"Unlike" : {
"comment" : "A button label that translates to \"Unlike\".",
"isCommentAutoGenerated" : true
},
"Unlimited" : {
"localizations" : {
"ar" : {
Expand Down Expand Up @@ -122775,6 +122811,14 @@
}
}
},
"Your answer is correct. " : {
"comment" : "Voiceover text announced when they answer a question correctly.",
"isCommentAutoGenerated" : true
},
"Your answer wrong. " : {
"comment" : "Voiceover text announced when the user selects an incorrect answer.",
"isCommentAutoGenerated" : true
},
"Your draft was deleted." : {
"localizations" : {
"ar" : {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ import SwiftUI
import Core

struct AssistChatView: View {
// MARK: - Propertites A11y

@AccessibilityFocusState private var messageFocusID: String?

// MARK: - Properties

@Bindable var viewModel: AssistChatViewModel
Expand All @@ -40,7 +44,9 @@ struct AssistChatView: View {
}
.scrollIndicators(.hidden)
.onReceive(viewModel.shouldOpenKeyboardPublisher) { value in
isFocused = value
if !UIAccessibility.isVoiceOverRunning {
isFocused = value
}
}
.onFirstAppear { viewModel.viewController = viewController }
.padding(.horizontal, .huiSpaces.space16)
Expand Down Expand Up @@ -70,6 +76,7 @@ struct AssistChatView: View {
ForEach(viewModel.messages, id: \.id) { message in
AssistChatMessageView(message: message)
.id(message.id)
.accessibilityFocused($messageFocusID, equals: message.id)
}
.animation(.smooth, value: viewModel.isRetryButtonVisible)
.animation(.smooth, value: viewModel.messages)
Expand All @@ -89,6 +96,7 @@ struct AssistChatView: View {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.35) {
withAnimation {
scrollViewProxy.scrollTo(id, anchor: .top)
messageFocusID = id
}
}
}
Expand All @@ -106,6 +114,7 @@ struct AssistChatView: View {
.resizable()
.frame(width: 20, height: 20)
.foregroundStyle(Color.huiColors.icon.surfaceColored)
.accessibilityHidden(true)
}
}

Expand Down Expand Up @@ -139,6 +148,9 @@ struct AssistChatView: View {
.opacity(viewModel.message.isEmpty && !isFocused ? 1 : 0)
}
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
.accessibilityAction {
isFocused = true
}
)
.frame(minHeight: 36)
.frame(maxHeight: 100)
Expand All @@ -149,6 +161,12 @@ struct AssistChatView: View {
.padding(.top, .huiSpaces.space12)
.foregroundColor(Color.huiColors.text.timestamp)
.scrollContentBackground(.hidden)
.accessibilityLabel(
viewModel.message.isEmpty
? String(localized: "Ask a question")
: viewModel.message
)
.accessibilityHint(String(localized: "Double tap to start typing."))
}

private var textInputSendButton: some View {
Expand All @@ -157,6 +175,7 @@ struct AssistChatView: View {
}
.padding(.huiSpaces.space12)
.disabled(viewModel.isDisableSendButton)
.accessibilityLabel(String(localized: "Send"))
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,88 +21,66 @@ import HorizonUI
import SwiftUI

struct AssistFeedbackView: View {
private var onChange: AssistChatMessageViewModel.OnFeedbackChange
private var onChange: (Bool?) -> Void
@State private var selected: Bool?
@State private var thumbsOpacity: Double = 1.0
@State private var thanksOpacity: Double = 0.0
@State private var viewHeight: CGFloat = 0 // Dynamically measured height
@State private var isFeedbackVisiable = true
@State private var opacity: Double = 0.0

init(onChange: @escaping AssistChatMessageViewModel.OnFeedbackChange) {
init(onChange: @escaping (Bool?) -> Void) {
self.onChange = onChange
}

private var thumbsUpOpacity: Double {
selected == true ? 1 : 0
}
private var thumbsDownOpacity: Double {
selected == false ? 1 : 0
}

var body: some View {
ZStack(alignment: .leading) {
HStack(spacing: HorizonUI.spaces.space8) {
thumbUpIcon
thumbDownIcon
VStack {
if selected == nil {
HStack(spacing: HorizonUI.spaces.space8) {
thumbUpIcon
thumbDownIcon
}
.padding(.top, .huiSpaces.space8)
}
.opacity(thumbsOpacity)
.animation(.easeInOut(duration: 0.2), value: thumbsOpacity)
.padding(.top, .huiSpaces.space8)

Text(String(localized: "Thank you for your feedback!", bundle: .horizon))
.huiTypography(.p1)
.foregroundColor(HorizonUI.colors.text.surfaceColored)
.opacity(thanksOpacity)
.animation(.easeInOut(duration: 0.2), value: thanksOpacity)
}
.background(
GeometryReader { proxy in
Color.clear
.onAppear {
viewHeight = proxy.size.height
}
if selected != nil, isFeedbackVisiable {
Text(String(localized: "Thank you for your feedback!", bundle: .horizon))
.huiTypography(.p1)
.foregroundColor(HorizonUI.colors.text.surfaceColored)
.opacity(opacity)
.animation(.easeInOut(duration: 0.2), value: opacity)
.padding(.top, .huiSpaces.space10)
}
)
.frame(height: viewHeight)
.animation(.easeInOut(duration: 0.2), value: viewHeight)
}
.animation(.smooth, value: selected)
}

private var thumbUpIcon: some View {
ZStack {
HorizonUI.icons.thumbUp
.foregroundStyle(HorizonUI.colors.text.surfaceColored)
.onTapGesture { onTap(true) }
HorizonUI.icons.thumbUp
.foregroundStyle(HorizonUI.colors.text.surfaceColored)
.onTapGesture { onTap(true) }
.accessibilityElement(children: .ignore)
.accessibilityLabel(String(localized: "Like"))
.accessibilityAddTraits(.isButton)

HorizonUI.icons.thumbUp
.foregroundStyle(HorizonUI.colors.text.surfaceColored)
.onTapGesture { onTap(true) }
.opacity(thumbsUpOpacity)
.animation(.easeInOut(duration: 0.2), value: thumbsUpOpacity)
}
}

private var thumbDownIcon: some View {
ZStack {
HorizonUI.icons.thumbDown
.foregroundStyle(HorizonUI.colors.text.surfaceColored)
.onTapGesture { onTap(false) }

HorizonUI.icons.thumbDown
.foregroundStyle(HorizonUI.colors.text.surfaceColored)
.onTapGesture { onTap(false) }
.opacity(thumbsUpOpacity)
.animation(.easeInOut(duration: 0.2), value: thumbsUpOpacity)
}
HorizonUI.icons.thumbDown
.foregroundStyle(HorizonUI.colors.text.surfaceColored)
.onTapGesture { onTap(false) }
.accessibilityElement(children: .ignore)
.accessibilityLabel(String(localized: "Unlike"))
.accessibilityAddTraits(.isButton)
}

private func onTap(_ value: Bool) {
thumbsOpacity = 0.0
thanksOpacity = 1.0
selected = selected == value ? nil : value
opacity = 1
selected = value
onChange(selected)

DispatchQueue.main.asyncAfter(deadline: .now() + 3.0) {
thanksOpacity = 0.0
viewHeight = 0
withAnimation {
opacity = 0.0
} completion: {
isFeedbackVisiable = false
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ struct AssistTitle: View {
)
.opacity(backOpacity)
.animation(.easeInOut(duration: 0.2), value: backOpacity)
.accessibilityLabel(String(localized: "Back"))
}

private var close: some View {
Expand All @@ -78,9 +79,10 @@ struct AssistTitle: View {
private var title: some View {
HStack {
HorizonUI.icons.aiFilled
.accessibilityHidden(true)
Text(String(localized: "IgniteAI", bundle: .horizon))
.huiTypography(.h4)

.accessibilityAddTraits(.isHeader)
}
.foregroundStyle(Color.textLightest)
.foregroundStyle(Color.huiColors.text.surfaceColored)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,12 @@ struct AssistFlashCardModel: Identifiable, Hashable {
mutating func makeItFlipped() {
isFlipped.toggle()
}

var accessibilityDescription: String {
var text = title
text += currentContent
return text
}
}

extension AssistFlashCardModel {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ extension AssistFlashCardStepIndicatorView {
) {
viewModel.goToPreviousCard()
}
.accessibilityAddTraits(.isButton)
.accessibilityLabel(String(localized: "Go to previous card"))
}

private var nextButton: some View {
Expand All @@ -57,6 +59,8 @@ extension AssistFlashCardStepIndicatorView {
) {
viewModel.goToNextCard()
}
.accessibilityAddTraits(.isButton)
.accessibilityLabel(String(localized: "Go to next card"))
}

private func stepButton(image: Image, disabled: Bool, action: @escaping () -> Void) -> some View {
Expand Down
Loading