Skip to content

Commit 3c7140d

Browse files
authored
Merge pull request #1 from harshMortal/claude/review-project-01BM8eEJarbKn6fc9k9CkXQS
Improve all blocks with accessibility and code quality enhancements
2 parents 098fefd + 9485229 commit 3c7140d

File tree

20 files changed

+757
-284
lines changed

20 files changed

+757
-284
lines changed

.eslintignore

Lines changed: 0 additions & 1 deletion
This file was deleted.

.eslintrc.js

Lines changed: 0 additions & 18 deletions
This file was deleted.

blocks/accordion/accordion.css

Lines changed: 39 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,15 @@
11
/* blocks/accordion/accordion.css */
22
.accordion {
3-
background: #fff;
3+
--accordion-bg: #fff;
4+
--accordion-header-bg: #f8f8f8;
5+
--accordion-header-hover-bg: #efefef;
6+
--accordion-border-color: #e0e0e0;
7+
--accordion-accent-color: #ff6b35;
8+
--accordion-text-color: #333;
9+
--accordion-content-text: #666;
10+
--accordion-focus-ring: #2c5f7c;
11+
12+
background: var(--accordion-bg);
413
padding: 60px 20px;
514
}
615

@@ -9,90 +18,96 @@
918
margin: 0 auto;
1019
}
1120

12-
.accordion > div > div:nth-child(odd) {
21+
.accordion .accordion-header {
1322
display: flex;
1423
align-items: center;
1524
gap: 20px;
1625
padding: 24px;
17-
background: #f8f8f8;
18-
border: 2px solid #e0e0e0;
26+
background: var(--accordion-header-bg);
27+
border: 2px solid var(--accordion-border-color);
1928
border-radius: 8px 8px 0 0;
2029
cursor: pointer;
2130
position: relative;
2231
margin-top: 16px;
23-
transition: background 0.2s;
32+
transition: background 0.2s, box-shadow 0.2s;
33+
}
34+
35+
.accordion .accordion-header:hover {
36+
background: var(--accordion-header-hover-bg);
2437
}
2538

26-
.accordion > div > div:nth-child(odd):hover {
27-
background: #efefef;
39+
.accordion .accordion-header:focus-visible {
40+
outline: none;
41+
box-shadow: 0 0 0 3px var(--accordion-focus-ring);
42+
z-index: 1;
2843
}
2944

30-
.accordion > div > div:nth-child(odd):first-child {
45+
.accordion .accordion-header:first-child {
3146
margin-top: 0;
3247
}
3348

34-
.accordion > div > div:nth-child(odd)::after {
49+
.accordion .accordion-header::after {
3550
content: '+';
3651
position: absolute;
3752
right: 24px;
3853
font-size: 36px;
3954
font-weight: bold;
40-
color: #ff6b35;
55+
color: var(--accordion-accent-color);
4156
line-height: 1;
4257
transition: transform 0.3s;
4358
}
4459

45-
.accordion > div > div:nth-child(odd).active::after {
46-
content: '';
60+
.accordion .accordion-header.active::after {
61+
content: '-';
4762
transform: rotate(180deg);
4863
}
4964

50-
.accordion > div > div:nth-child(odd) img {
65+
.accordion .accordion-header img {
5166
width: 60px;
5267
height: 60px;
5368
object-fit: contain;
5469
}
5570

56-
.accordion > div > div:nth-child(odd) strong {
71+
.accordion .accordion-header strong {
5772
font-size: 20px;
58-
color: #333;
73+
color: var(--accordion-text-color);
5974
flex: 1;
6075
}
6176

62-
.accordion > div > div:nth-child(even) {
77+
.accordion .accordion-content {
6378
max-height: 0;
6479
overflow: hidden;
6580
padding: 0 24px;
66-
border: 2px solid #e0e0e0;
81+
border: 2px solid var(--accordion-border-color);
6782
border-top: none;
6883
border-radius: 0 0 8px 8px;
69-
background: white;
84+
background: var(--accordion-bg);
7085
transition: max-height 0.4s ease, padding 0.4s ease;
7186
}
7287

73-
.accordion > div > div:nth-child(even).active {
88+
.accordion .accordion-content.active {
7489
max-height: 800px;
7590
padding: 24px;
7691
}
7792

78-
.accordion > div > div:nth-child(even) p {
93+
.accordion .accordion-content p {
7994
margin: 0;
8095
line-height: 1.6;
81-
color: #666;
96+
color: var(--accordion-content-text);
8297
}
8398

8499
@media (width < 768px) {
85-
.accordion > div > div:nth-child(odd) {
100+
.accordion .accordion-header {
86101
padding: 16px;
87102
gap: 12px;
88103
}
89104

90-
.accordion > div > div:nth-child(odd) img {
105+
.accordion .accordion-header img {
91106
width: 40px;
92107
height: 40px;
93108
}
94109

95-
.accordion > div > div:nth-child(odd) strong {
110+
.accordion .accordion-header strong {
96111
font-size: 16px;
97112
}
98113
}

blocks/accordion/accordion.js

Lines changed: 95 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,115 @@
1+
/**
2+
* Accordion Block
3+
* Accessible accordion component with keyboard navigation
4+
*/
5+
6+
let accordionIdCounter = 0;
7+
8+
function generateId() {
9+
accordionIdCounter += 1;
10+
return `accordion-panel-${accordionIdCounter}`;
11+
}
12+
13+
function toggleAccordion(header, content, children, forceState = null) {
14+
const isActive = forceState !== null ? !forceState : header.classList.contains('active');
15+
16+
// Close all panels
17+
children.forEach((child) => {
18+
child.classList.remove('active');
19+
if (child.hasAttribute('aria-expanded')) {
20+
child.setAttribute('aria-expanded', 'false');
21+
}
22+
});
23+
24+
// Open clicked panel if it wasn't active
25+
if (!isActive) {
26+
header.classList.add('active');
27+
header.setAttribute('aria-expanded', 'true');
28+
content.classList.add('active');
29+
}
30+
}
31+
32+
function handleKeydown(e, header, content, children, headers) {
33+
const currentIndex = headers.indexOf(header);
34+
35+
switch (e.key) {
36+
case 'Enter':
37+
case ' ':
38+
e.preventDefault();
39+
toggleAccordion(header, content, children);
40+
break;
41+
case 'ArrowDown':
42+
e.preventDefault();
43+
if (currentIndex < headers.length - 1) {
44+
headers[currentIndex + 1].focus();
45+
}
46+
break;
47+
case 'ArrowUp':
48+
e.preventDefault();
49+
if (currentIndex > 0) {
50+
headers[currentIndex - 1].focus();
51+
}
52+
break;
53+
case 'Home':
54+
e.preventDefault();
55+
headers[0].focus();
56+
break;
57+
case 'End':
58+
e.preventDefault();
59+
headers[headers.length - 1].focus();
60+
break;
61+
default:
62+
break;
63+
}
64+
}
65+
166
export default function decorate(block) {
267
const container = block.querySelector(':scope > div');
368
if (!container) return;
469

570
const children = Array.from(container.children);
71+
const headers = [];
72+
73+
// Set up accordion role
74+
block.setAttribute('role', 'presentation');
675

776
for (let i = 0; i < children.length; i += 2) {
877
const header = children[i];
978
const content = children[i + 1];
1079

1180
if (header && content) {
12-
header.addEventListener('click', () => {
13-
const isActive = header.classList.contains('active');
81+
const panelId = generateId();
1482

15-
// Close all
16-
children.forEach((child) => child.classList.remove('active'));
83+
// Set up header accessibility attributes
84+
header.setAttribute('role', 'button');
85+
header.setAttribute('tabindex', '0');
86+
header.setAttribute('aria-expanded', 'false');
87+
header.setAttribute('aria-controls', panelId);
88+
header.classList.add('accordion-header');
89+
90+
// Set up content accessibility attributes
91+
content.setAttribute('id', panelId);
92+
content.setAttribute('role', 'region');
93+
content.setAttribute('aria-labelledby', `${panelId}-header`);
94+
header.setAttribute('id', `${panelId}-header`);
95+
content.classList.add('accordion-content');
96+
97+
headers.push(header);
98+
99+
// Click handler
100+
header.addEventListener('click', () => {
101+
toggleAccordion(header, content, children);
102+
});
17103

18-
// Open clicked if wasn't active
19-
if (!isActive) {
20-
header.classList.add('active');
21-
content.classList.add('active');
22-
}
104+
// Keyboard handler
105+
header.addEventListener('keydown', (e) => {
106+
handleKeydown(e, header, content, children, headers);
23107
});
24108

25-
// Open first by default
109+
// Open first panel by default
26110
if (i === 0) {
27111
header.classList.add('active');
112+
header.setAttribute('aria-expanded', 'true');
28113
content.classList.add('active');
29114
}
30115
}

blocks/cards/cards.css

Lines changed: 44 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,58 +1,89 @@
11
/* blocks/cards/cards.css */
22
.cards {
3-
background: linear-gradient(135deg, #2c5f7c 0%, #1a3a4d 100%);
3+
--cards-bg-start: #2c5f7c;
4+
--cards-bg-end: #1a3a4d;
5+
--cards-card-bg: #fff;
6+
--cards-accent-color: #ff6b35;
7+
--cards-accent-hover: #e55a28;
8+
--cards-heading-color: #2c5f7c;
9+
--cards-focus-ring: #fff;
10+
11+
background: linear-gradient(135deg, var(--cards-bg-start) 0%, var(--cards-bg-end) 100%);
412
padding: 60px 20px;
513
}
614

7-
.cards > div {
15+
.cards .cards-list {
816
display: grid;
917
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
1018
gap: 32px;
1119
max-width: 1200px;
1220
margin: 0 auto;
21+
padding: 0;
22+
list-style: none;
1323
}
1424

15-
.cards > div > div {
16-
background: white;
25+
.cards .cards-card {
26+
background: var(--cards-card-bg);
1727
padding: 40px 32px;
1828
border-radius: 12px;
1929
box-shadow: 0 4px 16px rgb(0 0 0 / 15%);
2030
text-align: center;
21-
border-top: 4px solid #ff6b35;
31+
border-top: 4px solid var(--cards-accent-color);
2232
transition: transform 0.3s, box-shadow 0.3s;
2333
}
2434

25-
.cards > div > div:hover {
35+
.cards .cards-card:hover {
2636
transform: translateY(-4px);
2737
box-shadow: 0 8px 24px rgb(0 0 0 / 20%);
2838
}
2939

30-
.cards img {
40+
.cards .cards-card:focus-within {
41+
box-shadow: 0 0 0 3px var(--cards-focus-ring), 0 8px 24px rgb(0 0 0 / 20%);
42+
}
43+
44+
.cards .cards-card-image img {
3145
width: 80px;
3246
height: 80px;
47+
object-fit: contain;
3348
}
3449

3550
.cards h4 {
3651
font-size: 18px;
3752
font-weight: bold;
38-
color: #2c5f7c;
53+
color: var(--cards-heading-color);
3954
margin: 16px 0;
4055
text-transform: uppercase;
4156
letter-spacing: 0.5px;
4257
}
4358

44-
.cards a {
59+
.cards .cards-card-link {
4560
display: inline-block;
4661
padding: 12px 32px;
47-
background: #ff6b35;
62+
background: var(--cards-accent-color);
4863
color: white;
4964
text-decoration: none;
5065
border-radius: 4px;
5166
font-weight: bold;
5267
text-transform: uppercase;
53-
transition: background 0.3s;
68+
transition: background 0.3s, box-shadow 0.2s;
69+
}
70+
71+
.cards .cards-card-link:hover {
72+
background: var(--cards-accent-hover);
73+
}
74+
75+
.cards .cards-card-link:focus-visible {
76+
outline: none;
77+
box-shadow: 0 0 0 3px var(--cards-heading-color);
5478
}
5579

56-
.cards a:hover {
57-
background: #e55a28;
80+
@media (width < 768px) {
81+
.cards .cards-list {
82+
grid-template-columns: 1fr;
83+
gap: 24px;
84+
}
85+
86+
.cards .cards-card {
87+
padding: 32px 24px;
88+
}
5889
}

0 commit comments

Comments
 (0)