From 3c0559a71d6c92f19b3fa260e78a6510e83128c9 Mon Sep 17 00:00:00 2001 From: Jan Drewniak Date: Fri, 15 Jul 2022 14:27:57 -0400 Subject: [PATCH] Refactor chevron across components + separate watchstar Consolidates the CSS responsible for styling chevrons into one implementation. This removes the need for custom padding and background positioning for the following components: - "more" menu - user menu - watchstar - language button - languge button in sticky header Instead of absolutely positioning the chevron on these components, the parent label element is set to `display: inline-flex` so that the chevron is vertically aligned and given enough space by default. The watchstar, although not a chevron, is also given the `display: inline-flex` treatment so that it can be aligned with other elements in the toolbar. This new implementation requires splitting the watchstar component into legacy and modern due to a quirk in Firefox that causes a bug with the watchstar in legacy Vector. NOTE: This change causes visual changes due to the difference in centering the chevrons via flexbox vs percentage positions. Bug: T308344, T310838 Change-Id: Ie9e0fce1366cd25a5899fee49770de4a09424fe2 --- resources/common/common.less | 1 - resources/common/components/MenuDropdown.less | 68 +++++++++--------- .../components/MenuDropdown.less | 10 +-- .../components/MenuTabs.less | 23 ++++++ .../components/TabWatchstarLink.less | 14 ++-- .../skin-legacy.less | 1 + .../components/LanguageButton.less | 31 -------- .../components/MenuTabs.less | 72 +++++++++---------- .../components/TabWatchstarLink.less | 61 ++++++++++++++++ .../components/UserLinks.less | 17 ++--- .../skins.vector.styles/layouts/screen.less | 4 +- resources/skins.vector.styles/skin.less | 1 + stories/MenuTabs.stories.js | 2 +- 13 files changed, 170 insertions(+), 135 deletions(-) rename resources/{common => skins.vector.styles.legacy}/components/TabWatchstarLink.less (81%) create mode 100644 resources/skins.vector.styles/components/TabWatchstarLink.less diff --git a/resources/common/common.less b/resources/common/common.less index 13661c4f0..468cbab98 100644 --- a/resources/common/common.less +++ b/resources/common/common.less @@ -15,7 +15,6 @@ @import './components/Indicators.less'; @import './components/SiteNotice.less'; @import './components/Menu.less'; - @import './components/TabWatchstarLink.less'; @import './components/MenuDropdown.less'; @import './components/MenuPortal.less'; @import './components/SearchBox.less'; diff --git a/resources/common/components/MenuDropdown.less b/resources/common/components/MenuDropdown.less index 89f384ed0..31497e9d0 100644 --- a/resources/common/components/MenuDropdown.less +++ b/resources/common/components/MenuDropdown.less @@ -1,43 +1,43 @@ @import '../variables.less'; @import 'mediawiki.mixins.less'; -/* Variants and Actions */ +/** + * Targets: + * - language variants, Actions menus + * - more menu, user menu + * - ULS button in sticky header + */ +.emptyPortlet .vector-menu-heading, +.vector-menu-dropdown .vector-menu-heading, +.mw-interlanguage-selector { + display: flex; + color: @color-base--subtle; + + &:after { + content: ''; + background: url( images/arrow-down.svg ) 100% 50% no-repeat; + width: unit( 16 / @font-size-tabs / @font-size-browser, em ); + height: unit( 16 / @font-size-tabs / @font-size-browser, em ); + // Modify the color of the image from the default #202122 to approx. #404244 to match the text. + opacity: 0.84; + } + + &:hover, + &:focus { + color: @color-base; + + &:after { + opacity: 1; + } + } +} + .vector-menu-dropdown { direction: ltr; float: left; cursor: pointer; position: relative; - .vector-menu-heading { - color: @color-base--subtle; - position: relative; - display: block; - box-sizing: border-box; - - &:after { - content: ''; - background-image: url( images/arrow-down.svg ); - background-position: 100% 50%; - background-repeat: no-repeat; - position: absolute; - top: unit( 10 / @font-size-tabs / @font-size-browser, em ); - right: 8px; - bottom: 0; - width: unit( 16 / @font-size-tabs / @font-size-browser, em ); - // Modify the color of the image from the default #202122 to approx. #404244 to match the text. - opacity: 0.84; - } - - &:hover, - &:focus { - color: @color-base; - - &:after { - opacity: 1; - } - } - } - // The menu itself. .vector-menu-content { background-color: @background-color-base; @@ -46,7 +46,7 @@ // Match the width of the dropdown "heading" (the tab) min-width: 100%; position: absolute; - top: 2.5em; + top: 100%; left: -@border-width-base; margin: 0; border: @border-width-base @border-style-base @border-color-base; @@ -117,9 +117,7 @@ .vector-menu-dropdown-noicon { .vector-menu-heading { // `padding-top` needs to scale with font-size. - padding-top: 1.25em; - padding-left: 8px; - padding-right: unit( 24 / @font-size-tabs / @font-size-browser, em ); + padding: 1.25em 8px 6px; font-weight: normal; } diff --git a/resources/skins.vector.styles.legacy/components/MenuDropdown.less b/resources/skins.vector.styles.legacy/components/MenuDropdown.less index 9d0140542..b483818a6 100644 --- a/resources/skins.vector.styles.legacy/components/MenuDropdown.less +++ b/resources/skins.vector.styles.legacy/components/MenuDropdown.less @@ -20,7 +20,7 @@ line-height: 1.125em; li { - span:not( .mw-ui-icon ) { + a:not( .mw-ui-icon ) { font-size: @font-size-tabs; } @@ -51,12 +51,6 @@ #mw-head .vector-menu-dropdown .vector-menu-heading { // Tab separator: Outer end (right in LTR) border of "Actions" menu. background-position: right bottom; - float: left; - height: unit( 40 / @font-size-tabs / @font-size-browser, em ); margin: 0 -@border-width-base 0 0; - // `padding-right` >= `1px` effectively moves the "background border" outside of the element to - // act like a real border. It is necessary for `.vector-menu-dropdown .vector-menu-content-list` dropdown to align well. - // 0.5em equals `8px` at computed `font-size` of `14px` as visually harmonically with - // `padding-left` in `.vector-menu-dropdown .vector-menu-heading a` - padding-right: unit( 24 / @font-size-tabs / @font-size-browser, em ); + float: left; } diff --git a/resources/skins.vector.styles.legacy/components/MenuTabs.less b/resources/skins.vector.styles.legacy/components/MenuTabs.less index 65cda1eb3..82acd32fa 100644 --- a/resources/skins.vector.styles.legacy/components/MenuTabs.less +++ b/resources/skins.vector.styles.legacy/components/MenuTabs.less @@ -95,3 +95,26 @@ // Contain gradient to 1px × 100% size and draw from top to bottom-left or -right corner. background-size: @border-width-base 100%; } + +#mw-head .vector-menu-dropdown .vector-menu-heading { + // Tab separator: Outer end (right in LTR) border of "Actions" menu. + background-position: right bottom; +} + +.vector-menu-dropdown-noicon { + .vector-menu-heading { + // `padding-top` needs to scale with font-size. + padding-top: 1.25em; + padding-left: 8px; + padding-right: 8px; + font-size: @font-size-tabs; + font-weight: normal; + } + + // Add focus state to legacy menu dropdown buttons (i.e. p-variants, p-cactions) + .vector-menu-checkbox:focus + .vector-menu-heading { + // Simulate browser focus ring + outline: dotted 1px; // Firefox style + outline: auto -webkit-focus-ring-color; // Webkit style + } +} diff --git a/resources/common/components/TabWatchstarLink.less b/resources/skins.vector.styles.legacy/components/TabWatchstarLink.less similarity index 81% rename from resources/common/components/TabWatchstarLink.less rename to resources/skins.vector.styles.legacy/components/TabWatchstarLink.less index 41bac1781..f2e510733 100644 --- a/resources/common/components/TabWatchstarLink.less +++ b/resources/skins.vector.styles.legacy/components/TabWatchstarLink.less @@ -1,4 +1,4 @@ -@import '../variables.less'; +@import '../../common/variables.less'; @import 'mediawiki.mixins.less'; @import 'mediawiki.mixins.rotation.less'; @@ -33,30 +33,30 @@ } #ca-unwatch.icon a:before { - background-image: url( images/unwatch-icon.svg ); + background-image: url( ../common/images/unwatch-icon.svg ); } #ca-unwatch.mw-watchlink-temp.icon a:before { - background-image: url( images/unwatch-temp-icon.svg ); + background-image: url( ../common/images/unwatch-temp-icon.svg ); } #ca-watch.icon a:before { - background-image: url( images/watch-icon.svg ); + background-image: url( ../common/images/watch-icon.svg ); } #ca-unwatch.icon a:hover:before, #ca-unwatch.icon a:focus:before { - background-image: url( images/unwatch-icon-hl.svg ); + background-image: url( ../common/images/unwatch-icon-hl.svg ); } #ca-unwatch.mw-watchlink-temp.icon a:hover:before, #ca-unwatch.mw-watchlink-temp.icon a:focus:before { - background-image: url( images/unwatch-temp-icon-hl.svg ); + background-image: url( ../common/images/unwatch-temp-icon-hl.svg ); } #ca-watch.icon a:hover:before, #ca-watch.icon a:focus:before { - background-image: url( images/watch-icon-hl.svg ); + background-image: url( ../common/images/watch-icon-hl.svg ); } // Loading watchstar link class. diff --git a/resources/skins.vector.styles.legacy/skin-legacy.less b/resources/skins.vector.styles.legacy/skin-legacy.less index 6a5c5a3df..87eb872eb 100644 --- a/resources/skins.vector.styles.legacy/skin-legacy.less +++ b/resources/skins.vector.styles.legacy/skin-legacy.less @@ -12,6 +12,7 @@ @import './components/MenuTabs.less'; @import './components/SearchBox.less'; @import './components/Sidebar.less'; + @import './components/TabWatchstarLink.less'; @import './components/UserLinks.less'; // Overrides diff --git a/resources/skins.vector.styles/components/LanguageButton.less b/resources/skins.vector.styles/components/LanguageButton.less index ef95ad82e..cfc2f7ad6 100644 --- a/resources/skins.vector.styles/components/LanguageButton.less +++ b/resources/skins.vector.styles/components/LanguageButton.less @@ -18,11 +18,6 @@ } .vector-menu-heading { - // stylelint-disable-next-line plugin/no-unsupported-browser-features - font-size: initial; - // reset padding styles in MenuDropdown.less with right padding for arrow. - padding-right: 30px; - padding-left: 8px; // Prevent select of span text "X languages" user-select: none; // Remove opacity on language button (it applies to more menu because of label color). @@ -33,10 +28,6 @@ font-size: @font-size-base; } - &:after { - top: 0; - } - // T291286: Temporarily use progressive ULS style &.mw-ui-progressive.mw-ui-quiet { .mw-ui-icon:before { @@ -98,25 +89,3 @@ #p-lang-btn.mw-portlet-empty { display: none; } - -#p-lang-btn-sticky-header { - @button-padding: 12px; - @arrow-width: 18px; - position: relative; - padding-right: ~'calc( @{button-padding} + @{arrow-width} )'; - white-space: nowrap; - - &:after { - content: ''; - background-image: url( ../common/images/arrow-down.svg ); - background-position: 100% 50%; - background-repeat: no-repeat; - position: absolute; - top: 0; - right: @button-padding; - bottom: 0; - width: @arrow-width; - // Modify the color of the image from the default #202122 to approx. #404244 to match the text. - opacity: 0.84; - } -} diff --git a/resources/skins.vector.styles/components/MenuTabs.less b/resources/skins.vector.styles/components/MenuTabs.less index bc14acd1f..2f44ce18c 100644 --- a/resources/skins.vector.styles/components/MenuTabs.less +++ b/resources/skins.vector.styles/components/MenuTabs.less @@ -10,56 +10,52 @@ float: left; padding-left: @border-width-base; - li { - display: inline-block; - vertical-align: middle; - white-space: nowrap; - margin: 0; - - // Make first and last elements flush with edge of header. - &:first-child { - margin-left: -8px; - } - - &:last-child { - margin-right: -8px; - } - } - /* focus and hover have outlines. Text underline interferes with bottom border */ - li a:focus, - li a:hover { + .mw-list-item a:focus, + .mw-list-item a:hover { text-decoration: none; border-bottom: @border-width-base @border-style-base; } - .new a, - .new a:visited { + .mw-list-item.new a, + .mw-list-item.new a:visited { color: @color-link-new; } - .selected a, - .selected a:visited { + .mw-list-item.selected a, + .mw-list-item.selected a:visited { color: @color-link-selected; border-bottom: @border-width-base @border-style-base; } } /** - * Tab link appearance, applies to - * - inside vector-menu-tabs (e.g. read, edit, view, history) - * - vector-menu-dropdown headings (e.g. more menu, language variants, gadgets) -*/ -.vector-menu-tabs li a, -.vector-article-toolbar .vector-menu-heading { - display: block; - position: relative; - // Top bottom padding to increase clickable area. - padding-top: 18px; - padding-bottom: 7px; - // left & right margin separate bottom border between words, - // bottom margin makes link border overlap toolbar border. - margin: 0 8px -1px 8px; - cursor: pointer; - border-bottom: @border-width-base @border-style-base transparent; + * Tab list item appearance. Applies to both
  • 's inside .vector-menu-tabs + * and dropdown menus inside the article toolbar + */ +.vector-menu-tabs .mw-list-item, +.mw-article-toolbar-container .vector-menu-dropdown { + float: left; + white-space: nowrap; + margin: 0 @padding-horizontal-tabs; + + // target links inside of .vector-tab-menu + // and dropdown menu headings inside the article toolbar. + // NOTE: Consider adding a single selector to define both items, + // since they both appear beside each other in the article toolbar. + & > a, + .vector-menu-heading { + display: inline-flex; + position: relative; + // Top & bottom padding to increase clickable area. + padding: 18px 0 7px 0; + // bottom margin to overlap border with toolbar border. + margin-bottom: -1px; + cursor: pointer; + border-bottom: @border-width-base @border-style-base transparent; + // max-height & box-sizing to make link, watchstar & dropdown height consistent. + // NOTE: Was 40px instead of 41, but changed to avoid visual regressions. + max-height: unit( 41 / @font-size-tabs / @font-size-browser, em ); + box-sizing: border-box; + } } diff --git a/resources/skins.vector.styles/components/TabWatchstarLink.less b/resources/skins.vector.styles/components/TabWatchstarLink.less new file mode 100644 index 000000000..5619d0e61 --- /dev/null +++ b/resources/skins.vector.styles/components/TabWatchstarLink.less @@ -0,0 +1,61 @@ +@import '../../common/variables.less'; +@import 'mediawiki.mixins.less'; +@import 'mediawiki.mixins.rotation.less'; + +/* Watch/Unwatch Icon Styling */ +/* Only use icon if the menu item is not collapsed into the "More" dropdown + * (in which case it is inside `.vector-menu-dropdown` instead of `.vector-menu-tabs`). */ +.vector-menu-tabs { + @size-watchlink-icon: unit( 16 / @font-size-tabs / @font-size-browser, em ); + + .mw-watchlink.icon a { + overflow: hidden; + text-indent: -99999px; + color: transparent; + + &:before { + content: ''; + display: block; + width: @size-watchlink-icon; + height: @size-watchlink-icon; + } + } + + #ca-unwatch.icon a:before { + background-image: url( ../common/images/unwatch-icon.svg ); + } + + #ca-unwatch.mw-watchlink-temp.icon a:before { + background-image: url( ../common/images/unwatch-icon.svg ); + } + + #ca-watch.icon a:before { + background-image: url( ../common/images/unwatch-icon.svg ); + } + + #ca-unwatch.icon a:hover:before, + #ca-unwatch.icon a:focus:before { + background-image: url( ../common/images/unwatch-icon.svg ); + } + + #ca-unwatch.mw-watchlink-temp.icon a:hover:before, + #ca-unwatch.mw-watchlink-temp.icon a:focus:before { + background-image: url( ../common/images/unwatch-icon.svg ); + } + + #ca-watch.icon a:hover:before, + #ca-watch.icon a:focus:before { + background-image: url( ../common/images/watch-icon-hl.svg ); + } + + // Loading watchstar link class. + #ca-unwatch.icon .loading:before, + #ca-watch.icon .loading:before { + .rotation( 700ms ); + /* Suppress the hilarious rotating focus outline on Firefox */ + outline: 0; + cursor: default; + pointer-events: none; + transform-origin: 50% 50%; + } +} diff --git a/resources/skins.vector.styles/components/UserLinks.less b/resources/skins.vector.styles/components/UserLinks.less index 421d3973b..d84905358 100644 --- a/resources/skins.vector.styles/components/UserLinks.less +++ b/resources/skins.vector.styles/components/UserLinks.less @@ -178,20 +178,13 @@ } .vector-user-menu-logged-in .vector-menu-heading { - // FIXME: Ideally this variable should be accessible from mediawiki.skin.variables - // Remove it when we can. - @icon-padding-md: unit( 12 / @font-size-browser, em ); - @icon-arrow-size: unit( 12px / @font-size-browser, em ); - padding-right: @icon-padding-md + @icon-arrow-size; + // override user menu (.mw-ui-icon) fixed width, + // so chevron beside icon is visible. + width: auto; + // and override again to ensure the user icon is 20px wide. &:before { - color: #54595d; - } - - &:after { - background-position: 100% 0%; - top: @icon-padding-md + ( @icon-arrow-size / 2 ); - right: @icon-arrow-size; + width: auto; } } } diff --git a/resources/skins.vector.styles/layouts/screen.less b/resources/skins.vector.styles/layouts/screen.less index 4f44cd0d4..72268776d 100644 --- a/resources/skins.vector.styles/layouts/screen.less +++ b/resources/skins.vector.styles/layouts/screen.less @@ -114,7 +114,6 @@ body { /* Main column */ .mw-body, #mw-data-after-content, -#left-navigation, .mw-footer { margin-left: 0; } @@ -156,11 +155,12 @@ body { #left-navigation { float: left; + margin-left: -@padding-horizontal-tabs; } #right-navigation { float: right; - + margin-right: -@padding-horizontal-tabs; // Any dropdowns inside the right navigation in modern Vector (e.g. "more" menu). // should be right-aligned to prevent horizontal scrolling. .vector-menu-content { diff --git a/resources/skins.vector.styles/skin.less b/resources/skins.vector.styles/skin.less index 3f83c8dbd..8308372d1 100644 --- a/resources/skins.vector.styles/skin.less +++ b/resources/skins.vector.styles/skin.less @@ -20,6 +20,7 @@ @import './components/Header.less'; @import './components/MenuTabs.less'; @import './components/StickyHeader.less'; + @import './components/TabWatchstarLink.less'; @import './components/TableOfContents.less'; @import './components/TableOfContentsCollapsed.less'; } diff --git a/stories/MenuTabs.stories.js b/stories/MenuTabs.stories.js index 0d4cca31e..c22319652 100644 --- a/stories/MenuTabs.stories.js +++ b/stories/MenuTabs.stories.js @@ -2,7 +2,7 @@ import mustache from 'mustache'; import { menuTemplate as vectorTabsTemplate } from './Menu.stories.data'; import { namespaceTabsData, pageActionsData } from './MenuTabs.stories.data'; import '../resources/skins.vector.styles/components/MenuTabs.less'; -import '../resources/skins.vector.styles/TabWatchstarLink.less'; +import '../resources/skins.vector.styles/components/TabWatchstarLink.less'; export default { title: 'MenuTabs'