mirror of
https://github.com/freeCodeCamp/freeCodeCamp.git
synced 2026-03-01 17:03:30 -05:00
fix(curriculum,a11y): improve accessibility of Drum Machine demo (#63050)
Co-authored-by: Huyen Nguyen <25715018+huyenltnguyen@users.noreply.github.com>
This commit is contained in:
@@ -69,6 +69,15 @@ Inside your `#pad-bank` element you should have nine clickable drum pad elements
|
||||
assert.lengthOf(document.querySelectorAll('#pad-bank .drum-pad'), 9);
|
||||
```
|
||||
|
||||
Each `.drum-pad` should be a `button` element.
|
||||
|
||||
```js
|
||||
const pads = document.querySelectorAll('.drum-pad');
|
||||
pads.forEach(pad => {
|
||||
assert.equal(pad.tagName, 'BUTTON');
|
||||
});
|
||||
```
|
||||
|
||||
Each `.drum-pad` should have one of the following letters as `innerText`, in order: `Q`, `W`, `E`, `A`, `S`, `D`, `Z`, `X`, `C`.
|
||||
|
||||
```js
|
||||
@@ -178,24 +187,31 @@ assert.lengthOf(collection, 9);
|
||||
|
||||
<div class="controls-container">
|
||||
<div class="control power-control">
|
||||
<p>Power: <span id="power-status">On</span></p>
|
||||
<div class="select">
|
||||
<div class="inner"></div>
|
||||
</div>
|
||||
<label class="select">
|
||||
<span>Power: <span id="power-status">On</span></span>
|
||||
<span class="toggle-container">
|
||||
<input type="checkbox" id="power-toggle" checked />
|
||||
<span class="slider"></span>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<!-- Display for Drum Pad Name and Volume -->
|
||||
<p id="display"></p>
|
||||
|
||||
|
||||
<div class="volume-slider">
|
||||
<input type="range" min="0" max="1" step="0.01" value="0.3" />
|
||||
<label for="volume-control">Volume</label>
|
||||
<input type="range" id="volume-control" min="0" max="1" step="0.01" value="0.3" />
|
||||
</div>
|
||||
|
||||
<div class="control bank-control">
|
||||
<p>Bank: <span id="current-bank">Heater Kit</span></p>
|
||||
<div class="select">
|
||||
<div class="inner"></div>
|
||||
</div>
|
||||
<label class="select">
|
||||
<span>Bank: <span id="current-bank">Heater Kit</span></span>
|
||||
<span class="toggle-container">
|
||||
<input type="checkbox" id="bank-toggle" checked />
|
||||
<span class="slider"></span>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -247,14 +263,27 @@ body {
|
||||
justify-content: center;
|
||||
width: 70px;
|
||||
height: 70px;
|
||||
background: #444;
|
||||
background: #666;
|
||||
border: none;
|
||||
border-radius: 5px;
|
||||
font-size: 1.2em;
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
font-family: inherit;
|
||||
}
|
||||
|
||||
.drum-pad:hover {
|
||||
background: #777;
|
||||
}
|
||||
|
||||
.drum-pad:focus-visible {
|
||||
outline: 2px solid #0d6efd;
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
.drum-pad.active {
|
||||
background: #ffaa33;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.controls-container {
|
||||
@@ -272,22 +301,77 @@ body {
|
||||
}
|
||||
|
||||
.select {
|
||||
width: 50px;
|
||||
height: 20px;
|
||||
background: #555;
|
||||
border-radius: 10px;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.select .inner {
|
||||
.toggle-container {
|
||||
position: relative;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
width: 50px;
|
||||
height: 24px;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.toggle-container input[type="checkbox"] {
|
||||
opacity: 0;
|
||||
width: 0;
|
||||
height: 0;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.toggle-container .slider {
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: #6c757d; /* default gray for inactive state */
|
||||
border-radius: 12px;
|
||||
transition: background 0.25s ease, box-shadow 0.25s ease;
|
||||
}
|
||||
|
||||
.toggle-container .slider::before {
|
||||
position: absolute;
|
||||
content: "";
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
background: #ffaa33;
|
||||
left: 1px;
|
||||
bottom: 3px;
|
||||
background: #fff;
|
||||
border-radius: 50%;
|
||||
top: 1px;
|
||||
left: calc(100% - 18px);
|
||||
transition: transform 0.25s ease, background 0.25s ease;
|
||||
box-shadow: 0 1px 2px rgba(0,0,0,0.2);
|
||||
}
|
||||
|
||||
/* move knob and set green background */
|
||||
.toggle-container input:checked + .slider {
|
||||
background: #28a745;
|
||||
}
|
||||
|
||||
.toggle-container input:checked + .slider::before {
|
||||
transform: translateX(26px);
|
||||
}
|
||||
|
||||
/* add glow when active */
|
||||
.toggle-container input#power-toggle:checked + .slider {
|
||||
box-shadow: 0 0 0 4px rgba(40,167,69,0.12);
|
||||
}
|
||||
|
||||
/* Bank toggle: always show green and glow in both states */
|
||||
.toggle-container input#bank-toggle + .slider {
|
||||
background: #28a745;
|
||||
box-shadow: 0 0 0 4px rgba(40,167,69,0.12);
|
||||
}
|
||||
|
||||
.toggle-container input:focus-visible + .slider {
|
||||
outline: 2px solid #0d6efd;
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
#display {
|
||||
@@ -300,6 +384,16 @@ body {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.volume-slider {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.volume-slider label {
|
||||
display: block;
|
||||
margin-bottom: 5px;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
.volume-slider input[type='range'] {
|
||||
width: 100%;
|
||||
-webkit-appearance: none;
|
||||
@@ -318,6 +412,11 @@ body {
|
||||
border-radius: 50%;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.volume-slider input[type='range']:focus-visible {
|
||||
outline: 2px solid #0d6efd;
|
||||
outline-offset: 2px;
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
@@ -483,7 +582,7 @@ const renderPadBank = () => {
|
||||
padBank.innerHTML = '';
|
||||
|
||||
state.currentPadBank.forEach(drum => {
|
||||
const pad = document.createElement('div');
|
||||
const pad = document.createElement('button');
|
||||
pad.classList.add('drum-pad');
|
||||
pad.id = drum.id;
|
||||
pad.innerText = drum.keyTrigger;
|
||||
@@ -513,9 +612,6 @@ const powerControl = () => {
|
||||
: 'Off';
|
||||
|
||||
setDisplay('');
|
||||
|
||||
const powerSlider = document.querySelector('.power-control .select .inner');
|
||||
powerSlider.style.left = state.power ? 'calc(100% - 18px)' : '0';
|
||||
};
|
||||
|
||||
const selectBank = () => {
|
||||
@@ -529,17 +625,16 @@ const selectBank = () => {
|
||||
renderPadBank();
|
||||
document.getElementById('current-bank').textContent =
|
||||
state.currentPadBankId === 'Heater Kit' ? 'Heater Kit' : 'Smooth Piano Kit';
|
||||
const bankControl = document.querySelector('.bank-control .select .inner');
|
||||
bankControl.style.left =
|
||||
state.currentPadBankId === 'Heater Kit' ? 'calc(100% - 18px)' : '0';
|
||||
};
|
||||
|
||||
document
|
||||
.querySelector('.power-control')
|
||||
.addEventListener('click', powerControl);
|
||||
document.querySelector('.bank-control').addEventListener('click', selectBank);
|
||||
.getElementById('power-toggle')
|
||||
.addEventListener('change', powerControl);
|
||||
document
|
||||
.querySelector('.volume-slider input')
|
||||
.getElementById('bank-toggle')
|
||||
.addEventListener('change', selectBank);
|
||||
document
|
||||
.getElementById('volume-control')
|
||||
.addEventListener('input', adjustVolume);
|
||||
|
||||
renderPadBank();
|
||||
|
||||
Reference in New Issue
Block a user