@@ -725,6 +725,8 @@ class LandingPageWizard {
725725
726726 const imageList = document . createElement ( 'div' ) ;
727727 imageList . className = 'd-flex gap-2 flex-wrap mb-2' ;
728+ imageList . setAttribute ( 'role' , 'group' ) ;
729+ imageList . setAttribute ( 'aria-label' , this . label ( 'wizard.content.imageSuggestions' ) ) ;
728730
729731 const sectionImages = ( images [ index ] && images [ index ] . length > 0 ) ? images [ index ] : [ ] ;
730732 this . renderImageCards ( imageList , sectionImages , index ) ;
@@ -735,6 +737,8 @@ class LandingPageWizard {
735737 const emptyInfo = document . createElement ( 'div' ) ;
736738 emptyInfo . className = 'alert alert-info py-2 px-3 mb-2' ;
737739 emptyInfo . style . fontSize = '0.85em' ;
740+ emptyInfo . setAttribute ( 'role' , 'status' ) ;
741+ emptyInfo . setAttribute ( 'aria-live' , 'polite' ) ;
738742 emptyInfo . textContent = this . label ( 'wizard.content.imageAutoSearchEmpty' , keywords . join ( ', ' ) ) ;
739743 imageSection . appendChild ( emptyInfo ) ;
740744 }
@@ -749,6 +753,7 @@ class LandingPageWizard {
749753 searchInput . type = 'text' ;
750754 searchInput . className = 'form-control form-control-sm' ;
751755 searchInput . placeholder = this . label ( 'wizard.content.imageSearchPlaceholder' ) ;
756+ searchInput . setAttribute ( 'aria-label' , this . label ( 'wizard.content.imageSearchPlaceholder' ) ) ;
752757 searchInput . style . maxWidth = '250px' ;
753758 if ( keywords . length > 0 ) {
754759 searchInput . value = keywords . join ( ' ' ) ;
@@ -893,9 +898,11 @@ class LandingPageWizard {
893898 const showingSource = source . style . display !== 'none' ;
894899 preview . style . display = showingSource ? 'block' : 'none' ;
895900 source . style . display = showingSource ? 'none' : 'block' ;
901+ toggleBtn . setAttribute ( 'aria-expanded' , showingSource ? 'false' : 'true' ) ;
896902 }
897903 } ,
898904 ) ;
905+ toggleBtn . setAttribute ( 'aria-expanded' , 'false' ) ;
899906
900907 const regenerateBtn = this . createButton (
901908 this . label ( 'wizard.button.regenerate' ) ,
@@ -959,6 +966,8 @@ class LandingPageWizard {
959966
960967 const imageList = document . createElement ( 'div' ) ;
961968 imageList . className = 'd-flex gap-2 flex-wrap mb-2' ;
969+ imageList . setAttribute ( 'role' , 'group' ) ;
970+ imageList . setAttribute ( 'aria-label' , this . label ( 'wizard.content.imageSuggestions' ) ) ;
962971
963972 if ( hasKeywords ) {
964973 const images = WizardState . getImages ( ) ;
@@ -970,6 +979,8 @@ class LandingPageWizard {
970979 const emptyInfo = document . createElement ( 'div' ) ;
971980 emptyInfo . className = 'alert alert-info py-2 px-3 mb-2' ;
972981 emptyInfo . style . fontSize = '0.85em' ;
982+ emptyInfo . setAttribute ( 'role' , 'status' ) ;
983+ emptyInfo . setAttribute ( 'aria-live' , 'polite' ) ;
973984 emptyInfo . textContent = this . label ( 'wizard.content.imageAutoSearchEmpty' , keywords . join ( ', ' ) ) ;
974985 imageSection . appendChild ( emptyInfo ) ;
975986 }
@@ -985,6 +996,7 @@ class LandingPageWizard {
985996 searchInput . type = 'text' ;
986997 searchInput . className = 'form-control form-control-sm' ;
987998 searchInput . placeholder = this . label ( 'wizard.content.imageSearchPlaceholder' ) ;
999+ searchInput . setAttribute ( 'aria-label' , this . label ( 'wizard.content.imageSearchPlaceholder' ) ) ;
9881000 searchInput . style . maxWidth = '250px' ;
9891001 if ( hasKeywords ) {
9901002 searchInput . value = keywords . join ( ' ' ) ;
@@ -1116,6 +1128,7 @@ class LandingPageWizard {
11161128 imgCard . setAttribute ( 'tabindex' , '0' ) ;
11171129 imgCard . setAttribute ( 'aria-label' , img . title || img . name || 'Image' ) ;
11181130 imgCard . dataset . imageUid = String ( img . uid ) ;
1131+ imgCard . style . position = 'relative' ;
11191132
11201133 // Auto-select recommended image when no image is selected yet
11211134 const isRecommended = img . recommended === true ;
@@ -1124,8 +1137,11 @@ class LandingPageWizard {
11241137 sections [ sectionIndex ] . imageUid = img . uid ;
11251138 }
11261139
1127- if ( sections [ sectionIndex ] . imageUid === img . uid ) {
1128- imgCard . classList . add ( 'border-primary' , 'shadow-sm' ) ;
1140+ const isSelected = sections [ sectionIndex ] . imageUid === img . uid ;
1141+ imgCard . setAttribute ( 'aria-pressed' , isSelected ? 'true' : 'false' ) ;
1142+ if ( isSelected ) {
1143+ imgCard . classList . add ( 'border-primary' , 'border-2' , 'shadow-sm' ) ;
1144+ this . _addCheckOverlay ( imgCard ) ;
11291145 }
11301146
11311147 // Thumbnail or placeholder
@@ -1153,7 +1169,7 @@ class LandingPageWizard {
11531169 badge . className = img . generated
11541170 ? 'badge bg-warning text-dark mb-1'
11551171 : 'badge bg-success text-white mb-1' ;
1156- badge . style . fontSize = '0.65rem ' ;
1172+ badge . style . fontSize = '0.75rem ' ;
11571173 badge . textContent = img . generated ? 'AI' : '\u2605 Best' ;
11581174 imgBody . appendChild ( badge ) ;
11591175 }
@@ -1167,16 +1183,21 @@ class LandingPageWizard {
11671183
11681184 const selectImage = ( ) => {
11691185 const secs = WizardState . getContentSections ( ) ;
1170- const isSelected = secs [ sectionIndex ] . imageUid === img . uid ;
1186+ const wasSelected = secs [ sectionIndex ] . imageUid === img . uid ;
11711187
1172- secs [ sectionIndex ] . imageUid = isSelected ? 0 : img . uid ;
1188+ secs [ sectionIndex ] . imageUid = wasSelected ? 0 : img . uid ;
11731189
1174- // Update visual state for all cards in this section's image list
1190+ // Update visual + ARIA state for all cards in this section's image list
11751191 imageList . querySelectorAll ( '.card' ) . forEach ( ( c ) => {
1176- c . classList . remove ( 'border-primary' , 'shadow-sm' ) ;
1192+ c . classList . remove ( 'border-primary' , 'border-2' , 'shadow-sm' ) ;
1193+ c . setAttribute ( 'aria-pressed' , 'false' ) ;
1194+ const check = c . querySelector ( '.image-check-overlay' ) ;
1195+ if ( check ) check . remove ( ) ;
11771196 } ) ;
1178- if ( ! isSelected ) {
1179- imgCard . classList . add ( 'border-primary' , 'shadow-sm' ) ;
1197+ if ( ! wasSelected ) {
1198+ imgCard . classList . add ( 'border-primary' , 'border-2' , 'shadow-sm' ) ;
1199+ imgCard . setAttribute ( 'aria-pressed' , 'true' ) ;
1200+ this . _addCheckOverlay ( imgCard ) ;
11801201 }
11811202 } ;
11821203
@@ -1192,6 +1213,23 @@ class LandingPageWizard {
11921213 } ) ;
11931214 }
11941215
1216+ /**
1217+ * Add a checkmark overlay to an image card to visually indicate selection.
1218+ * Provides a non-color visual indicator (WCAG 1.4.1).
1219+ *
1220+ * @param {HTMLElement } card
1221+ */
1222+ _addCheckOverlay ( card ) {
1223+ const check = document . createElement ( 'span' ) ;
1224+ check . className = 'image-check-overlay' ;
1225+ check . setAttribute ( 'aria-hidden' , 'true' ) ;
1226+ check . textContent = '\u2713' ;
1227+ check . style . cssText = 'position:absolute;top:4px;right:4px;background:#0d6efd;color:#fff;'
1228+ + 'border-radius:50%;width:20px;height:20px;display:flex;align-items:center;'
1229+ + 'justify-content:center;font-size:12px;font-weight:bold;line-height:1;' ;
1230+ card . appendChild ( check ) ;
1231+ }
1232+
11951233 /**
11961234 * Re-render content sections using the appropriate renderer for the current generation mode.
11971235 *
0 commit comments