@@ -391,8 +391,8 @@ const Block: React.FC<BlockProps> = ({
391391 aria-label = "Resize block"
392392 data-resize-handle = "true"
393393 className = { `absolute bottom-2 right-2 z-30 transition-opacity pointer-events-auto ${
394- isResizing ? 'opacity-100' : 'opacity-0 group-hover:opacity-100'
395- } `}
394+ isResizing ? 'opacity-100' : 'opacity-0 group-hover:opacity-100 focus:opacity-100 '
395+ } focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 rounded-lg `}
396396 onPointerDown = { ( e ) => {
397397 e . preventDefault ( ) ;
398398 e . stopPropagation ( ) ;
@@ -432,6 +432,16 @@ const Block: React.FC<BlockProps> = ({
432432 if ( block . type === BlockType . SPACER ) {
433433 return (
434434 < motion . div
435+ role = "button"
436+ tabIndex = { 0 }
437+ aria-label = { `Edit block ${ block . title ?? block . type } ` }
438+ aria-grabbed = { isDragging ? 'true' : 'false' }
439+ onKeyDown = { ( e ) => {
440+ if ( e . key === 'Enter' || e . key === ' ' ) {
441+ e . preventDefault ( ) ;
442+ onEdit ( block ) ;
443+ }
444+ } }
435445 layoutId = { block . id }
436446 layout
437447 draggable = { ! isResizing }
@@ -462,6 +472,7 @@ const Block: React.FC<BlockProps> = ({
462472 ${ isDragging ? 'opacity-40 scale-95' : '' }
463473 transition-all duration-200 group
464474 flex items-center justify-center
475+ focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2
465476 ` }
466477 style = { { borderRadius, ...gridPositionStyle } }
467478 >
@@ -499,6 +510,15 @@ const Block: React.FC<BlockProps> = ({
499510
500511 return (
501512 < motion . a
513+ tabIndex = { 0 }
514+ aria-label = { `Edit block ${ block . title ?? block . type } ` }
515+ aria-grabbed = { isDragging ? 'true' : 'false' }
516+ onKeyDown = { ( e ) => {
517+ if ( e . key === 'Enter' || e . key === ' ' ) {
518+ e . preventDefault ( ) ;
519+ onEdit ( block ) ;
520+ }
521+ } }
502522 layoutId = { block . id }
503523 layout
504524 href = { url || undefined }
@@ -538,6 +558,7 @@ const Block: React.FC<BlockProps> = ({
538558 transition-all duration-200 group
539559 flex items-center justify-center
540560 shadow-sm border border-gray-100
561+ focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2
541562 ` }
542563 style = { {
543564 ...gridPositionStyle ,
@@ -563,27 +584,31 @@ const Block: React.FC<BlockProps> = ({
563584
564585 { /* Action buttons */ }
565586 { showActionButtons && (
566- < div className = "absolute top-1 right-1 flex gap-1 opacity-0 group-hover:opacity-100 transition-opacity z-20" >
587+ < div className = "absolute top-1 right-1 flex gap-1 opacity-0 group-hover:opacity-100 focus-within:opacity-100 transition-opacity z-20" >
567588 { onDuplicate && (
568589 < button
590+ type = "button"
591+ aria-label = { `Duplicate ${ block . title ?? 'block' } ` }
569592 onClick = { ( e ) => {
570593 e . preventDefault ( ) ;
571594 e . stopPropagation ( ) ;
572595 onDuplicate ( block . id ) ;
573596 } }
574- className = "p-1 bg-white/90 text-gray-800 rounded-md shadow-sm hover:bg-white"
597+ className = "p-1 bg-white/90 text-gray-800 rounded-md shadow-sm hover:bg-white focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-400 "
575598 title = "Duplicate block"
576599 >
577600 < CopyPlus size = { 12 } />
578601 </ button >
579602 ) }
580603 < button
604+ type = "button"
605+ aria-label = { `Delete ${ block . title ?? 'block' } ` }
581606 onClick = { ( e ) => {
582607 e . preventDefault ( ) ;
583608 e . stopPropagation ( ) ;
584609 onDelete ( block . id ) ;
585610 } }
586- className = "p-1 bg-red-500/80 hover:bg-red-600 text-white rounded-md shadow-sm"
611+ className = "p-1 bg-red-500/80 hover:bg-red-600 text-white rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-400 "
587612 title = "Delete block"
588613 >
589614 < Trash2 size = { 12 } />
@@ -643,6 +668,15 @@ const Block: React.FC<BlockProps> = ({
643668
644669 return (
645670 < motion . div
671+ tabIndex = { 0 }
672+ aria-label = { `Edit block ${ block . title ?? block . type } ` }
673+ aria-grabbed = { isDragging ? 'true' : 'false' }
674+ onKeyDown = { ( e ) => {
675+ if ( e . key === 'Enter' || e . key === ' ' ) {
676+ e . preventDefault ( ) ;
677+ onEdit ( block ) ;
678+ }
679+ } }
646680 layoutId = { block . id }
647681 layout
648682 draggable = { ! isResizing }
@@ -686,6 +720,7 @@ const Block: React.FC<BlockProps> = ({
686720 ${ isDragTarget ? 'ring-2 ring-violet-500 z-20 scale-[1.02]' : '' }
687721 ${ isDragging ? 'opacity-40 scale-95' : '' }
688722 transition-all duration-300 select-none
723+ focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2
689724 ` }
690725 >
691726 { /* Drop indicator */ }
@@ -725,8 +760,9 @@ const Block: React.FC<BlockProps> = ({
725760 href = { `https://youtube.com/watch?v=${ vid . id } ` }
726761 target = "_blank"
727762 rel = "noopener noreferrer"
763+ aria-label = { `Watch video: ${ vid . title } ` }
728764 onClick = { ( e ) => e . stopPropagation ( ) }
729- className = "relative overflow-hidden group/vid rounded bg-gray-100 block"
765+ className = "relative overflow-hidden group/vid rounded bg-gray-100 block focus:outline-none focus:ring-2 focus:ring-blue-500 "
730766 >
731767 < img
732768 src = { vid . thumbnail }
@@ -767,6 +803,15 @@ const Block: React.FC<BlockProps> = ({
767803
768804 return (
769805 < motion . div
806+ tabIndex = { 0 }
807+ aria-label = { `Edit block ${ block . title ?? block . type } ` }
808+ aria-grabbed = { isDragging ? 'true' : 'false' }
809+ onKeyDown = { ( e ) => {
810+ if ( e . key === 'Enter' || e . key === ' ' ) {
811+ e . preventDefault ( ) ;
812+ onEdit ( block ) ;
813+ }
814+ } }
770815 layoutId = { block . id }
771816 layout
772817 draggable = { ! isResizing }
@@ -824,6 +869,7 @@ const Block: React.FC<BlockProps> = ({
824869 ${ ! isSelected && ! enableTiltEffect ? 'shadow-sm hover:shadow-xl' : 'shadow-sm' }
825870 ${ isDragTarget ? 'ring-2 ring-violet-500' : '' }
826871 transition-all duration-300
872+ focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2
827873 ` }
828874 >
829875 { /* Drop indicator */ }
@@ -841,27 +887,31 @@ const Block: React.FC<BlockProps> = ({
841887 ) }
842888 { /* Action buttons */ }
843889 { showActionButtons && (
844- < div className = "absolute top-2 right-2 flex gap-2 opacity-0 group-hover:opacity-100 transition-opacity z-30 pointer-events-auto" >
890+ < div className = "absolute top-2 right-2 flex gap-2 opacity-0 group-hover:opacity-100 focus-within:opacity-100 transition-opacity z-30 pointer-events-auto" >
845891 { onDuplicate && (
846892 < button
893+ type = "button"
894+ aria-label = { `Duplicate ${ block . title ?? 'block' } ` }
847895 onClick = { ( e ) => {
848896 e . preventDefault ( ) ;
849897 e . stopPropagation ( ) ;
850898 onDuplicate ( block . id ) ;
851899 } }
852- className = "p-2 bg-white/80 hover:bg-white text-gray-800 rounded-lg shadow-sm backdrop-blur-sm"
900+ className = "p-2 bg-white/80 hover:bg-white text-gray-800 rounded-lg shadow-sm backdrop-blur-sm focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-400 "
853901 title = "Duplicate block"
854902 >
855903 < CopyPlus size = { 14 } />
856904 </ button >
857905 ) }
858906 < button
907+ type = "button"
908+ aria-label = { `Delete ${ block . title ?? 'block' } ` }
859909 onClick = { ( e ) => {
860910 e . preventDefault ( ) ;
861911 e . stopPropagation ( ) ;
862912 onDelete ( block . id ) ;
863913 } }
864- className = "p-2 bg-red-500/80 hover:bg-red-600 text-white rounded-lg backdrop-blur-sm shadow-sm"
914+ className = "p-2 bg-red-500/80 hover:bg-red-600 text-white rounded-lg backdrop-blur-sm shadow-sm focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-400 "
865915 title = "Delete block"
866916 >
867917 < Trash2 size = { 14 } />
@@ -884,12 +934,14 @@ const Block: React.FC<BlockProps> = ({
884934 { /* Reposition button - appears on hover */ }
885935 { ! isRepositioning && onInlineUpdate && (
886936 < button
937+ type = "button"
938+ aria-label = "Reposition background image"
887939 onClick = { ( e ) => {
888940 e . preventDefault ( ) ;
889941 e . stopPropagation ( ) ;
890942 setIsRepositioning ( true ) ;
891943 } }
892- className = { `absolute ${ repositionButtonOffsetClass } right-2 p-2 bg-black/60 hover:bg-black/80 text-white rounded-lg opacity-0 group-hover:opacity-100 transition-opacity z-20 backdrop-blur-sm` }
944+ className = { `absolute ${ repositionButtonOffsetClass } right-2 p-2 bg-black/60 hover:bg-black/80 text-white rounded-lg opacity-0 group-hover:opacity-100 focus:opacity-100 transition-opacity z-20 backdrop-blur-sm focus:ring-2 focus:ring-offset-2 focus:ring-white focus:outline-none ` }
893945 title = "Reposition image"
894946 >
895947 < Move size = { 16 } />
@@ -910,23 +962,27 @@ const Block: React.FC<BlockProps> = ({
910962 </ div >
911963 < div className = "absolute bottom-2 left-1/2 -translate-x-1/2 flex gap-2" >
912964 < button
965+ type = "button"
966+ aria-label = "Cancel image repositioning"
913967 onClick = { ( e ) => {
914968 e . preventDefault ( ) ;
915969 e . stopPropagation ( ) ;
916970 handleCancelMediaPosition ( ) ;
917971 } }
918- className = "p-2 bg-gray-800 hover:bg-gray-700 text-white rounded-lg transition-colors"
972+ className = "p-2 bg-gray-800 hover:bg-gray-700 text-white rounded-lg transition-colors focus:ring-2 focus:ring-offset-2 focus:ring-gray-500 focus:outline-none "
919973 title = "Cancel"
920974 >
921975 < X size = { 16 } />
922976 </ button >
923977 < button
978+ type = "button"
979+ aria-label = "Save image position"
924980 onClick = { ( e ) => {
925981 e . preventDefault ( ) ;
926982 e . stopPropagation ( ) ;
927983 handleSaveMediaPosition ( ) ;
928984 } }
929- className = "p-2 bg-green-600 hover:bg-green-500 text-white rounded-lg transition-colors"
985+ className = "p-2 bg-green-600 hover:bg-green-500 text-white rounded-lg transition-colors focus:ring-2 focus:ring-offset-2 focus:ring-green-400 focus:outline-none "
930986 title = "Save position"
931987 >
932988 < Check size = { 16 } />
@@ -970,12 +1026,14 @@ const Block: React.FC<BlockProps> = ({
9701026 { /* Reposition button - appears on hover */ }
9711027 { ! isRepositioning && onInlineUpdate && (
9721028 < button
1029+ type = "button"
1030+ aria-label = "Reposition media content"
9731031 onClick = { ( e ) => {
9741032 e . preventDefault ( ) ;
9751033 e . stopPropagation ( ) ;
9761034 setIsRepositioning ( true ) ;
9771035 } }
978- className = { `absolute ${ repositionButtonOffsetClass } right-2 p-2 bg-black/60 hover:bg-black/80 text-white rounded-lg opacity-0 group-hover:opacity-100 transition-opacity pointer-events-auto z-20 backdrop-blur-sm` }
1036+ className = { `absolute ${ repositionButtonOffsetClass } right-2 p-2 bg-black/60 hover:bg-black/80 text-white rounded-lg opacity-0 group-hover:opacity-100 focus:opacity-100 transition-opacity pointer-events-auto z-20 backdrop-blur-sm focus:ring-2 focus:ring-offset-2 focus:ring-white focus:outline-none ` }
9791037 title = "Reposition media"
9801038 >
9811039 < Move size = { 16 } />
@@ -994,23 +1052,27 @@ const Block: React.FC<BlockProps> = ({
9941052 { /* Save/Cancel buttons */ }
9951053 < div className = "absolute bottom-2 left-1/2 -translate-x-1/2 flex gap-2 pointer-events-auto z-20" >
9961054 < button
1055+ type = "button"
1056+ aria-label = "Cancel media repositioning"
9971057 onClick = { ( e ) => {
9981058 e . preventDefault ( ) ;
9991059 e . stopPropagation ( ) ;
10001060 handleCancelMediaPosition ( ) ;
10011061 } }
1002- className = "p-2 bg-gray-800 hover:bg-gray-700 text-white rounded-lg transition-colors"
1062+ className = "p-2 bg-gray-800 hover:bg-gray-700 text-white rounded-lg transition-colors focus:ring-2 focus:ring-offset-2 focus:ring-gray-500 focus:outline-none "
10031063 title = "Cancel"
10041064 >
10051065 < X size = { 16 } />
10061066 </ button >
10071067 < button
1068+ type = "button"
1069+ aria-label = "Save media position"
10081070 onClick = { ( e ) => {
10091071 e . preventDefault ( ) ;
10101072 e . stopPropagation ( ) ;
10111073 handleSaveMediaPosition ( ) ;
10121074 } }
1013- className = "p-2 bg-green-600 hover:bg-green-500 text-white rounded-lg transition-colors"
1075+ className = "p-2 bg-green-600 hover:bg-green-500 text-white rounded-lg transition-colors focus:ring-2 focus:ring-offset-2 focus:ring-green-400 focus:outline-none "
10141076 title = "Save position"
10151077 >
10161078 < Check size = { 16 } />
@@ -1131,6 +1193,7 @@ const Block: React.FC<BlockProps> = ({
11311193 < input
11321194 ref = { titleInputRef }
11331195 type = "text"
1196+ aria-label = { `Edit block title${ block . title ? `: ${ block . title } ` : '' } ` }
11341197 value = { editTitleValue }
11351198 onChange = { ( e ) => setEditTitleValue ( e . target . value ) }
11361199 onBlur = { handleTitleSave }
@@ -1168,6 +1231,7 @@ const Block: React.FC<BlockProps> = ({
11681231 < input
11691232 ref = { subtextInputRef }
11701233 type = "text"
1234+ aria-label = { `Edit block subtitle${ block . subtext ? `: ${ block . subtext } ` : '' } ` }
11711235 value = { editSubtextValue }
11721236 onChange = { ( e ) => setEditSubtextValue ( e . target . value ) }
11731237 onBlur = { handleSubtextSave }
0 commit comments