From f14e5839b866cd2a147ff014bf30b1158dd5cc41 Mon Sep 17 00:00:00 2001 From: Yuriy Bakhtin Date: Fri, 28 Aug 2026 14:26:35 +0300 Subject: [PATCH] Make the "Edit message" button accessible via keyboard --- docs/CHANGELOG.md | 1 + resources/css/humhub.mail.css | 10 +++++++--- resources/css/humhub.mail.min.css | 2 +- resources/css/humhub.mail.scss | 18 +++++++++++++++--- widgets/ConversationEntryMenu.php | 9 +++++++-- 5 files changed, 31 insertions(+), 9 deletions(-) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 80a0f827..e8489e90 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -6,6 +6,7 @@ Changelog - Fix #512: Validate `from` pagination cursor to prevent unbounded conversation loading - Fix #514: Optimize pin icon rendering on the inbox message list - Fix #515: Reduce database queries in the inbox list, conversation loading, reply action and unread count polling +- Fix #519: Make the "Edit message" button accessible via keyboard 3.3.12 (July 16, 2026) ---------------------- diff --git a/resources/css/humhub.mail.css b/resources/css/humhub.mail.css index 16273408..fe25b474 100644 --- a/resources/css/humhub.mail.css +++ b/resources/css/humhub.mail.css @@ -248,7 +248,7 @@ position: relative; } .mail-conversation-entry .conversation-menu { - visibility: hidden; + opacity: 0; padding-left: 35px; } .mail-conversation-entry .conversation-menu .conversation-menu-item { @@ -276,8 +276,12 @@ background-color: var(--hh-background-color-secondary); color: inherit; } -.mail-conversation-entry:hover .conversation-menu { - visibility: visible; +.mail-conversation-entry .conversation-menu .conversation-edit-button:focus-visible { + outline: 0; + box-shadow: 0 0 0 2px var(--accent); +} +.mail-conversation-entry:hover .conversation-menu, .mail-conversation-entry:focus-within .conversation-menu { + opacity: 1; } .mail-conversation-entry .author-image .img-user { max-width: 100%; diff --git a/resources/css/humhub.mail.min.css b/resources/css/humhub.mail.min.css index 05721c42..1f5da361 100644 --- a/resources/css/humhub.mail.min.css +++ b/resources/css/humhub.mail.min.css @@ -1 +1 @@ -:root{--hh-mail-offset-top:0px;--hh-mail-top-conversation-height:0px}@media (max-width:991.98px){:root{--hh-mail-top-conversation-height:60px}}@media (max-width:767.98px){:root{--hh-mail-top-conversation-height:50px}}#dropdown-messages .dropdown-header{color:#000;margin-bottom:12px;font-weight:600}#dropdown-messages.dropdown-menu li a{font-size:inherit!important}#dropdown-messages .text-break{font-weight:200}.modal-body #createmessage-message .ProseMirror{min-height:100px!important}#create-message-button .fa,#mail-conversation-create-button .fa{margin:0}#conversation-tags-root{border-bottom:1px solid #eee}#conversation-tags-root .my-tags-label{padding-right:10px}.conversation-edit-button{border-bottom-right-radius:0;border-top-right-radius:0}.conversation-scroll-down-button{position:absolute;z-index:1;cursor:pointer;bottom:58px;right:25px;width:38px;height:38px;background:#fff;border-radius:50%;box-shadow:1px 1px 2px #999}.conversation-scroll-down-button .fa{font-size:26px;margin:7px 0 0 11px}.conversation-entry-list{height:100%}.conversation-entry-content{display:table;float:left;background-color:var(--hh-background-color-secondary);border-radius:10px;padding:10px}.conversation-entry-content pre{max-width:485px}.conversation-entry-content.own{background:var(--hh-background-color-highlight)}.conversation-entry-content .markdown-render{float:left;width:100%;min-width:230px}.conversation-blocked-recipient{-webkit-filter:grayscale(100%);filter:grayscale(100%)}#mail-conversation-root hr{border-top:1px solid #eee}#mail-conversation-root .ProsemirrorEditor.focusMenu .ProseMirror-menubar{margin-top:0}#mail-conversation-root>.panel{display:flex;flex-direction:column;height:calc(100vh - 15px - var(--hh-fixed-header-height) - var(--hh-fixed-footer-height) - var(--hh-mobile-app-safe-area-inset-bottom,env(safe-area-inset-bottom)) - var(--hh-mail-top-conversation-height))}#mail-conversation-root>.panel>.conversation-entry-container{flex:1;overflow:auto}#mail-conversation-root>.panel .content_create .richtext-create-input-group>.mb-3 [data-ui-markdown]{max-height:calc(50vh - var(--hh-fixed-header-height) - var(--hh-fixed-footer-height) - var(--hh-mobile-app-safe-area-inset-bottom,env(safe-area-inset-bottom)) - var(--hh-mail-top-conversation-height));overflow-y:auto}#mail-conversation-header{padding:6px 10px;border-bottom:1px solid var(--hh-background3);border-bottom-left-radius:0;border-bottom-right-radius:0}#mail-conversation-header h1{font-weight:600;font-size:16px;display:inline-block}@media (min-width:576px){#mail-conversation-header{padding:18px}#mail-conversation-header h1{font-size:18px}}#mail-conversation-header small{display:block;font-size:11px;font-weight:400}#mail-conversation-header small a{color:var(--hh-text-color-main)}@media (max-height:500px){#mail-conversation-header h1{margin-bottom:0}#mail-conversation-header small{display:none}}#conversation-settings-button{padding:6px;font-size:12px;cursor:pointer}#mail-filter-root{margin-top:5px}.mail-inbox-messages .hh-list,.mail-inbox-messages .mail-link,.mail-inbox-messages .messagePreviewEntry{overflow-x:hidden}.mail-inbox-messages .panel-heading>a{font-weight:600}.mail-inbox-messages .panel-heading #mail-filter-root>a{font-weight:400}.mail-inbox-messages .text-break{min-width:0}.mail-inbox-messages .text-break h4{font-weight:600;font-size:16px;display:flex;gap:8px}.mail-inbox-messages .text-break h4 time{font-size:11px!important;font-weight:400;flex:0 0 auto;margin-left:auto;text-align:right}.mail-inbox-messages .text-break h5{font-size:14px;line-height:16px;font-weight:500!important;color:var(--hh-text-color-highlight)!important;display:flex;justify-content:space-between;margin:7px 0}.mail-inbox-messages .text-break h5 span:first-child{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.mail-inbox-messages .text-break .mail-last-entry{font-size:14px;font-weight:500;color:var(--hh-text-color-secondary);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.mail-message-form{width:100%;padding:10px;box-shadow:none;border:none;margin-bottom:0!important}.mail-message-form>panel-body{margin:0 10px}.mail-message-form .content-create-input-group{position:relative}.mail-message-form .humhub-ui-richtext{border-radius:10px}@media (min-width:768px){.mail-message-form .humhub-ui-richtext{padding-right:115px}.mail-message-form .richtext-create-buttons{position:absolute;right:24px;bottom:24px;z-index:500}}.mail-message-form .reply-button{margin-left:0!important}.mail-message-form .form-text{margin:0}.mail-message-form .alert{margin-bottom:0}.mail-conversation-entry{margin-top:3px;position:relative}.mail-conversation-entry .conversation-menu{visibility:hidden;padding-left:35px}.mail-conversation-entry .conversation-menu .conversation-menu-item{margin-bottom:10px;margin-left:5px}.mail-conversation-entry .conversation-menu .conversation-menu-item a{cursor:pointer}.mail-conversation-entry .conversation-menu .time{color:inherit;text-transform:none}.mail-conversation-entry .conversation-menu .badge{background-color:var(--hh-background-color-secondary);border-radius:10px;color:inherit}.mail-conversation-entry .conversation-menu .conversation-edit-button{background-color:var(--hh-background-color-secondary);border-radius:50%;color:inherit}.mail-conversation-entry .conversation-menu .conversation-edit-button:hover{background-color:var(--hh-background-color-secondary);color:inherit}.mail-conversation-entry:hover .conversation-menu{visibility:visible}.mail-conversation-entry .author-image .img-user{max-width:100%}.mail-conversation-entry .author-label{font-size:10px;font-weight:600}.mail-conversation-entry.hideUserInfo .author-label{display:none}.mail-conversation-entry.hideUserInfo .author-image a{visibility:hidden;margin-top:0}.mail-conversation-entry .conversation-entry-time{float:right;font-size:10px;color:var(--hh-text-color-main)}.mail-conversation-entry .conversation-entry-time>span{font-style:italic}.conversation-entry-badge{padding:10px 0 8px;text-align:center}.conversation-entry-badge span{display:inline-block;box-shadow:1px 1px 3px rgba(0,0,0,.3);border-radius:7px;padding:5px 20px}.conversation-entry-badge.conversation-date-badge span{text-transform:uppercase}#inbox{max-height:calc(100vh - var(--hh-fixed-header-height) - var(--hh-fixed-footer-height) - var(--hh-mobile-app-safe-area-inset-bottom,env(safe-area-inset-bottom)) - var(--hh-mail-offset-top));overflow:auto;border-radius:4px}.messagePreviewEntry{cursor:pointer}.messagePreviewEntry time{font-size:10px;color:var(--hh-text-color-secondary)}.messagePreviewEntry .new-message-badge{display:none}.messagePreviewEntry.unread .new-message-badge{display:block}.messagePreviewEntry.unread time{color:var(--bs-info)}.messagePreviewEntry.unread .mail-last-entry{color:var(--hh-text-color-highlight)}.message-tag-filter-group .select2-selection{border-bottom-right-radius:0}.message-tag-filter-group .manage-tags-link{font-weight:600;font-size:.8em;border:1px solid #ededed;border-top:0;border-bottom-left-radius:4px;border-bottom-right-radius:4px;padding:2px 5px}.new-message-badge{float:right;min-width:14px;height:14px;border-radius:50%;background:var(--bs-info);margin-left:2px}.inbox-entry-title{font-weight:600}.field-replyform-message{margin:0}.device-ios-mobile #createmessage-message.ProsemirrorEditor.fullscreen,.device-ios-mobile .mail-message-form .ProsemirrorEditor.fullscreen{top:var(--hh-fixed-header-height);height:calc(100vh - var(--hh-fixed-header-height) - var(--hh-mobile-app-safe-area-inset-bottom,env(safe-area-inset-bottom)));height:calc(100dvh - var(--hh-fixed-header-height) - var(--hh-mobile-app-safe-area-inset-bottom,env(safe-area-inset-bottom)))}.device-ios-mobile #createmessage-message.ProsemirrorEditor.fullscreen .ProseMirror-menubar-wrapper,.device-ios-mobile .mail-message-form .ProsemirrorEditor.fullscreen .ProseMirror-menubar-wrapper{display:flex;flex-direction:column;height:100%}.device-ios-mobile #createmessage-message.ProsemirrorEditor.fullscreen .ProseMirror-menubar,.device-ios-mobile .mail-message-form .ProsemirrorEditor.fullscreen .ProseMirror-menubar{position:sticky!important;top:0!important;z-index:1}.device-ios-mobile #createmessage-message.ProsemirrorEditor.fullscreen .ProseMirror,.device-ios-mobile .mail-message-form .ProsemirrorEditor.fullscreen .ProseMirror{flex:1 1 auto;height:auto;min-height:0}@media (max-width:991.98px){#inbox{max-height:500px!important}}@media (max-width:767.98px){#dropdown-messages{width:300px!important}.arrow{margin-left:-101px!important}#inbox{max-height:none!important}.mail-conversation-single-message #inbox{max-height:none!important}.mail-conversation-single-message .inbox-wrapper{display:none}.conversation-entry-content pre{max-width:245px;padding:0}} \ No newline at end of file +:root{--hh-mail-offset-top:0px;--hh-mail-top-conversation-height:0px}@media (max-width:991.98px){:root{--hh-mail-top-conversation-height:60px}}@media (max-width:767.98px){:root{--hh-mail-top-conversation-height:50px}}#dropdown-messages .dropdown-header{color:#000;margin-bottom:12px;font-weight:600}#dropdown-messages.dropdown-menu li a{font-size:inherit!important}#dropdown-messages .text-break{font-weight:200}.modal-body #createmessage-message .ProseMirror{min-height:100px!important}#create-message-button .fa,#mail-conversation-create-button .fa{margin:0}#conversation-tags-root{border-bottom:1px solid #eee}#conversation-tags-root .my-tags-label{padding-right:10px}.conversation-edit-button{border-bottom-right-radius:0;border-top-right-radius:0}.conversation-scroll-down-button{position:absolute;z-index:1;cursor:pointer;bottom:58px;right:25px;width:38px;height:38px;background:#fff;border-radius:50%;box-shadow:1px 1px 2px #999}.conversation-scroll-down-button .fa{font-size:26px;margin:7px 0 0 11px}.conversation-entry-list{height:100%}.conversation-entry-content{display:table;float:left;background-color:var(--hh-background-color-secondary);border-radius:10px;padding:10px}.conversation-entry-content pre{max-width:485px}.conversation-entry-content.own{background:var(--hh-background-color-highlight)}.conversation-entry-content .markdown-render{float:left;width:100%;min-width:230px}.conversation-blocked-recipient{-webkit-filter:grayscale(100%);filter:grayscale(100%)}#mail-conversation-root hr{border-top:1px solid #eee}#mail-conversation-root .ProsemirrorEditor.focusMenu .ProseMirror-menubar{margin-top:0}#mail-conversation-root>.panel{display:flex;flex-direction:column;height:calc(100vh - 15px - var(--hh-fixed-header-height) - var(--hh-fixed-footer-height) - var(--hh-mobile-app-safe-area-inset-bottom,env(safe-area-inset-bottom)) - var(--hh-mail-top-conversation-height))}#mail-conversation-root>.panel>.conversation-entry-container{flex:1;overflow:auto}#mail-conversation-root>.panel .content_create .richtext-create-input-group>.mb-3 [data-ui-markdown]{max-height:calc(50vh - var(--hh-fixed-header-height) - var(--hh-fixed-footer-height) - var(--hh-mobile-app-safe-area-inset-bottom,env(safe-area-inset-bottom)) - var(--hh-mail-top-conversation-height));overflow-y:auto}#mail-conversation-header{padding:6px 10px;border-bottom:1px solid var(--hh-background3);border-bottom-left-radius:0;border-bottom-right-radius:0}#mail-conversation-header h1{font-weight:600;font-size:16px;display:inline-block}@media (min-width:576px){#mail-conversation-header{padding:18px}#mail-conversation-header h1{font-size:18px}}#mail-conversation-header small{display:block;font-size:11px;font-weight:400}#mail-conversation-header small a{color:var(--hh-text-color-main)}@media (max-height:500px){#mail-conversation-header h1{margin-bottom:0}#mail-conversation-header small{display:none}}#conversation-settings-button{padding:6px;font-size:12px;cursor:pointer}#mail-filter-root{margin-top:5px}.mail-inbox-messages .hh-list,.mail-inbox-messages .mail-link,.mail-inbox-messages .messagePreviewEntry{overflow-x:hidden}.mail-inbox-messages .panel-heading>a{font-weight:600}.mail-inbox-messages .panel-heading #mail-filter-root>a{font-weight:400}.mail-inbox-messages .text-break{min-width:0}.mail-inbox-messages .text-break h4{font-weight:600;font-size:16px;display:flex;gap:8px}.mail-inbox-messages .text-break h4 time{font-size:11px!important;font-weight:400;flex:0 0 auto;margin-left:auto;text-align:right}.mail-inbox-messages .text-break h5{font-size:14px;line-height:16px;font-weight:500!important;color:var(--hh-text-color-highlight)!important;display:flex;justify-content:space-between;margin:7px 0}.mail-inbox-messages .text-break h5 span:first-child{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.mail-inbox-messages .text-break .mail-last-entry{font-size:14px;font-weight:500;color:var(--hh-text-color-secondary);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.mail-message-form{width:100%;padding:10px;box-shadow:none;border:none;margin-bottom:0!important}.mail-message-form>panel-body{margin:0 10px}.mail-message-form .content-create-input-group{position:relative}.mail-message-form .humhub-ui-richtext{border-radius:10px}@media (min-width:768px){.mail-message-form .humhub-ui-richtext{padding-right:115px}.mail-message-form .richtext-create-buttons{position:absolute;right:24px;bottom:24px;z-index:500}}.mail-message-form .reply-button{margin-left:0!important}.mail-message-form .form-text{margin:0}.mail-message-form .alert{margin-bottom:0}.mail-conversation-entry{margin-top:3px;position:relative}.mail-conversation-entry .conversation-menu{opacity:0;padding-left:35px}.mail-conversation-entry .conversation-menu .conversation-menu-item{margin-bottom:10px;margin-left:5px}.mail-conversation-entry .conversation-menu .conversation-menu-item a{cursor:pointer}.mail-conversation-entry .conversation-menu .time{color:inherit;text-transform:none}.mail-conversation-entry .conversation-menu .badge{background-color:var(--hh-background-color-secondary);border-radius:10px;color:inherit}.mail-conversation-entry .conversation-menu .conversation-edit-button{background-color:var(--hh-background-color-secondary);border-radius:50%;color:inherit}.mail-conversation-entry .conversation-menu .conversation-edit-button:hover{background-color:var(--hh-background-color-secondary);color:inherit}.mail-conversation-entry .conversation-menu .conversation-edit-button:focus-visible{outline:0;box-shadow:0 0 0 2px var(--accent)}.mail-conversation-entry:focus-within .conversation-menu,.mail-conversation-entry:hover .conversation-menu{opacity:1}.mail-conversation-entry .author-image .img-user{max-width:100%}.mail-conversation-entry .author-label{font-size:10px;font-weight:600}.mail-conversation-entry.hideUserInfo .author-label{display:none}.mail-conversation-entry.hideUserInfo .author-image a{visibility:hidden;margin-top:0}.mail-conversation-entry .conversation-entry-time{float:right;font-size:10px;color:var(--hh-text-color-main)}.mail-conversation-entry .conversation-entry-time>span{font-style:italic}.conversation-entry-badge{padding:10px 0 8px;text-align:center}.conversation-entry-badge span{display:inline-block;box-shadow:1px 1px 3px rgba(0,0,0,.3);border-radius:7px;padding:5px 20px}.conversation-entry-badge.conversation-date-badge span{text-transform:uppercase}#inbox{max-height:calc(100vh - var(--hh-fixed-header-height) - var(--hh-fixed-footer-height) - var(--hh-mobile-app-safe-area-inset-bottom,env(safe-area-inset-bottom)) - var(--hh-mail-offset-top));overflow:auto;border-radius:4px}.messagePreviewEntry{cursor:pointer}.messagePreviewEntry time{font-size:10px;color:var(--hh-text-color-secondary)}.messagePreviewEntry .new-message-badge{display:none}.messagePreviewEntry.unread .new-message-badge{display:block}.messagePreviewEntry.unread time{color:var(--bs-info)}.messagePreviewEntry.unread .mail-last-entry{color:var(--hh-text-color-highlight)}.message-tag-filter-group .select2-selection{border-bottom-right-radius:0}.message-tag-filter-group .manage-tags-link{font-weight:600;font-size:.8em;border:1px solid #ededed;border-top:0;border-bottom-left-radius:4px;border-bottom-right-radius:4px;padding:2px 5px}.new-message-badge{float:right;min-width:14px;height:14px;border-radius:50%;background:var(--bs-info);margin-left:2px}.inbox-entry-title{font-weight:600}.field-replyform-message{margin:0}.device-ios-mobile #createmessage-message.ProsemirrorEditor.fullscreen,.device-ios-mobile .mail-message-form .ProsemirrorEditor.fullscreen{top:var(--hh-fixed-header-height);height:calc(100vh - var(--hh-fixed-header-height) - var(--hh-mobile-app-safe-area-inset-bottom,env(safe-area-inset-bottom)));height:calc(100dvh - var(--hh-fixed-header-height) - var(--hh-mobile-app-safe-area-inset-bottom,env(safe-area-inset-bottom)))}.device-ios-mobile #createmessage-message.ProsemirrorEditor.fullscreen .ProseMirror-menubar-wrapper,.device-ios-mobile .mail-message-form .ProsemirrorEditor.fullscreen .ProseMirror-menubar-wrapper{display:flex;flex-direction:column;height:100%}.device-ios-mobile #createmessage-message.ProsemirrorEditor.fullscreen .ProseMirror-menubar,.device-ios-mobile .mail-message-form .ProsemirrorEditor.fullscreen .ProseMirror-menubar{position:sticky!important;top:0!important;z-index:1}.device-ios-mobile #createmessage-message.ProsemirrorEditor.fullscreen .ProseMirror,.device-ios-mobile .mail-message-form .ProsemirrorEditor.fullscreen .ProseMirror{flex:1 1 auto;height:auto;min-height:0}@media (max-width:991.98px){#inbox{max-height:500px!important}}@media (max-width:767.98px){#dropdown-messages{width:300px!important}.arrow{margin-left:-101px!important}#inbox{max-height:none!important}.mail-conversation-single-message #inbox{max-height:none!important}.mail-conversation-single-message .inbox-wrapper{display:none}.conversation-entry-content pre{max-width:245px;padding:0}} \ No newline at end of file diff --git a/resources/css/humhub.mail.scss b/resources/css/humhub.mail.scss index 4a4a1531..d2f5114a 100644 --- a/resources/css/humhub.mail.scss +++ b/resources/css/humhub.mail.scss @@ -317,7 +317,10 @@ $radiusNone: 0; position: relative; .conversation-menu { - visibility: hidden; // Visible on .mail-conversation-entry:hover + // Kept in the layout and only visually hidden (not visibility:hidden/display:none), + // so the edit button stays focusable and reachable via keyboard Tab. + // Revealed on hover or when it (or a child) receives focus below. + opacity: 0; padding-left: 35px; .conversation-menu-item { @@ -349,12 +352,21 @@ $radiusNone: 0; background-color: $bgColorDefault; color: inherit; } + + // Now that the button is a real, focusable link (see ConversationEntryMenu::initMenus()), + // give it its own visible focus indicator - the round shape/badge background can make the + // browser's default outline hard to notice. Same treatment as .task-list-edit etc. in the + // tasks module. + &:focus-visible { + outline: 0; + box-shadow: 0 0 0 2px var(--accent); + } } } - &:hover { + &:hover, &:focus-within { .conversation-menu { - visibility: visible; + opacity: 1; } } diff --git a/widgets/ConversationEntryMenu.php b/widgets/ConversationEntryMenu.php index c158d0e5..0544907a 100644 --- a/widgets/ConversationEntryMenu.php +++ b/widgets/ConversationEntryMenu.php @@ -35,10 +35,15 @@ public function init() public function initMenus() { if ($this->entry->canEdit()) { - $this->menus[] = ModalButton::none()->link() + $editUrl = Url::toEditMessageEntry($this->entry); + + $this->menus[] = ModalButton::none() + // href is required so the link is keyboard-focusable (Tab) and + // activatable with Enter - without it, browsers skip it entirely. + ->link($editUrl) ->icon('pencil') ->tooltip(Yii::t('MailModule.base', 'Edit')) - ->load(Url::toEditMessageEntry($this->entry)) + ->load($editUrl) ->cssClass('conversation-edit-button time badge'); } }