Skip to content

Commit

Permalink
Add keyboard and screen reader accessibility
Browse files Browse the repository at this point in the history
  • Loading branch information
SimonRothUCF committed Feb 23, 2024
1 parent 0cdca1e commit 2a20a38
Show file tree
Hide file tree
Showing 3 changed files with 98 additions and 4 deletions.
85 changes: 85 additions & 0 deletions src/player.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,11 @@ HangmanEngine.controller 'HangmanEngineCtrl', ['$scope', '$timeout', 'Parse', 'R
$scope.anvilStage = 0
$scope.keyboard = null # Bound to onscreen keyboard, hit prop fades out key when 1

$scope.focusTitleMessage = ''
$scope.focusAnswerMessage = ''
$scope.focusQuestionMessage = ''
$scope.focusKeyboardMessage = ''

_updateAnvil = ->
# Get number of entered attempts
for i in [0...$scope.max.length]
Expand Down Expand Up @@ -219,14 +224,21 @@ HangmanEngine.controller 'HangmanEngineCtrl', ['$scope', '$timeout', 'Parse', 'R
$scope.startGame = ->
return if $scope.inGame

liveRegionUpdate("The game has begun! Your topic is " + document.getElementsByClassName('title')[0].innerHTML + " with " + $scope.total + " questions.")
$scope.focusTitleMessage = document.getElementsByClassName('title')[0].innerHTML + " with " + $scope.total + " questions."

$scope.curItem++
$scope.anvilStage = 1
$scope.inGame = true
$scope.inQues = true
$scope.readyForInput = true
$scope.ques = _qset.items[0].items[$scope.curItem].questions[0].text
$scope.answer = Parse.forBoard _qset.items[0].items[$scope.curItem].answers[0].text
$scope.focusQuestionMessage = "Question " + ($scope.curItem + 1) + ": " + $scope.ques
$scope.focusAnswerMessage = "Current answer: " + condenseBlanks($scope.answer.guessed)
$scope.focusKeyboardMessage = "You have " + ($scope.max.length - $scope.anvilStage + 1) + " guesses. Press or type a letter."
$timeout ->
liveRegionUpdate("Question 1: " + $scope.ques + condenseBlanks($scope.answer.guessed))
Hangman.Draw.playAnimation 'torso', 'pull-card'
, 800

Expand Down Expand Up @@ -256,6 +268,66 @@ HangmanEngine.controller 'HangmanEngineCtrl', ['$scope', '$timeout', 'Parse', 'R
else
$scope.toggleGame()

liveRegionUpdate = (message) ->
document.getElementById('ariaLive').innerHTML = message

addBlanksForLiveRegion = (guessed) ->
# Takes the user's current answer and adds "blank" where there are blanks so the screen reader will read them out loud
# Used to tell screen reader users what their current board looks like
message = ''
words = 0
maxWords = 0
for word in guessed
maxWords += 1
for word in guessed
words += 1
for letter in word
if letter == ''
message = message.concat('blank, ')
else
message = message.concat(letter, ', ')
if words < maxWords
message = message.concat('new word, ')

return message

guessedToString = (guessed) ->
# Converts the user's current answer to a string to be read by the screen reader
# Used to read the final answer in full words so the player knows what they got
message = ''
for word in guessed
for letter in word
message = message.concat(letter)
message = message.concat(' ')
return message

usedKeysToString = () ->
# Converts the user's previously guessed letters to a string to be read by the screen reader
# Used to tell the player which letters they've already guessed, both correct and incorrect
message = ''
for index, letter of $scope.keyboard
if letter.hit == 1
message += index + ', '
return message

condenseBlanks = (guessed) ->
# Counts the words in the answer and the letters in each word and turns them into a string
# Used to tell the player how many words and letters are in the answer at the beginning of a question
message = ''
words = 0
for word in guessed
words += 1
message = message.concat(words + " words. ")
words = 0
for word in guessed
letters = 0
words += 1
message = message.concat("Word " + words + ": ")
for letter in word
letters += 1
message = message.concat(letters + " letters. ")
return message

$scope.getUserInput = (input) ->
# Keyboard appears slightly before question transition is complete, so ignore early inputs
if $scope.inTransition then return
Expand All @@ -272,20 +344,28 @@ HangmanEngine.controller 'HangmanEngineCtrl', ['$scope', '$timeout', 'Parse', 'R
if matches.length is 0
$scope.max = Input.incorrect $scope.max
_updateAnvil()
liveRegionUpdate(input + " is incorrect. " + ($scope.max.length - $scope.anvilStage + 1) + " guesses remaining.")
$scope.focusKeyboardMessage = ($scope.max.length - $scope.anvilStage + 1) + " guesses remaining. Letters guessed: " + usedKeysToString()

# User entered a correct guess
else
$scope.answer.guessed = Input.correct matches, input, $scope.answer.guessed
liveRegionUpdate(input + " is correct! Current answer: " + addBlanksForLiveRegion($scope.answer.guessed))
$scope.focusAnswerMessage = "Current answer: " + addBlanksForLiveRegion($scope.answer.guessed)
$scope.focusKeyboardMessage = ($scope.max.length - $scope.anvilStage + 1) + " guesses remaining. Letters guessed: " + usedKeysToString()


# Find out if the user can continue to submit guesses
result = Input.cannotContinue $scope.max, $scope.answer.guessed
if result
liveRegionUpdate("Out of guesses. Press Enter to go to the next question.")
$scope.endQuestion()

# The user can't continue because they won and are awesomesauce
if result is 2
Hangman.Draw.playAnimation 'torso', 'pander'
$scope.anvilStage = 1
liveRegionUpdate(guessedToString($scope.answer.guessed) + " is correct! Press Enter to go to the next question.")

$scope.startQuestion = ->

Expand All @@ -298,6 +378,11 @@ HangmanEngine.controller 'HangmanEngineCtrl', ['$scope', '$timeout', 'Parse', 'R

$scope.readyForInput = true

liveRegionUpdate("Question " + ($scope.curItem + 1) + ": " + $scope.ques + condenseBlanks($scope.answer.guessed))
$scope.focusQuestionMessage = "Question " + ($scope.curItem + 1) + ": " + $scope.ques
$scope.focusAnswerMessage = "Current answer: " + condenseBlanks($scope.answer.guessed)
$scope.focusKeyboardMessage = "You have " + ($scope.max.length - $scope.anvilStage + 1) + " guesses. Press or type a letter."

Hangman.Draw.playAnimation 'torso', 'pull-card'

$scope.endQuestion = ->
Expand Down
11 changes: 7 additions & 4 deletions src/player.html
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
<script src="player.js"></script>
</head>
<body ng-cloak>
<div id="ariaLive" aria-live="polite" aria-atomic="true"></div>
<div class="browserwarning">We recommend you upgrade your browser for a better experience</div>
<div id="browserfailure">
<p>We're sorry</p>
Expand Down Expand Up @@ -52,10 +53,10 @@ <h1>Guess the Phrase</h1>
<img src="assets/img/anvil.png" aria-hidden="true" class="anvil">
</div>
<div aria-hidden="true" class="podium"></div>
<div class="title"></div>
<div class="title" tabindex="0" aria-label="{{focusTitleMessage}}"></div>
<div class="question-num">{{ curItem+1 }}</div>
<div class="total-questions"></div>
<div ng-class="{transition: inQues}" class="answer">
<div ng-class="{transition: inQues}" class="answer" tabindex="0" aria-label="{{focusAnswerMessage}}">
<div ng-repeat="word in answer.string" class="row">
<span ng-repeat="letter in word" ng-class="{quirks: cssQuirks}" class="box">
<span class="letter"
Expand All @@ -72,7 +73,9 @@ <h1>Guess the Phrase</h1>
</div>
<div ng-class="{transition: inQues}"
ng-show="inGame"
class="question">
class="question"
tabindex="0"
aria-label="{{focusQuestionMessage}}">
{{ ques }}
<div class="tail"></div>
</div>
Expand All @@ -82,7 +85,7 @@ <h1>Guess the Phrase</h1>
class="icon-close">
</span>
</div>
<div aria-hidden="true" class="keyboard-bg"></div>
<div aria-hidden="true" class="keyboard-bg" tabindex="0" aria-label="{{focusKeyboardMessage}}"></div>
<div aria-hidden="true"
ng-show="inQues"
ng-init="keys=[
Expand Down
6 changes: 6 additions & 0 deletions src/player.scss
Original file line number Diff line number Diff line change
Expand Up @@ -540,3 +540,9 @@ div.browserwarning {
padding: 20px;
display: none;
}

#ariaLive {
width: 1px;
height: 1px;
overflow: hidden;
}

0 comments on commit 2a20a38

Please sign in to comment.