diff --git a/packages/base-styles/_z-index.scss b/packages/base-styles/_z-index.scss index 0cec716ac360d1..14305a93611977 100644 --- a/packages/base-styles/_z-index.scss +++ b/packages/base-styles/_z-index.scss @@ -187,6 +187,8 @@ $z-layers: ( // Appear under the topbar. ".customize-widgets__block-toolbar": 7, + + ".is-focusing-regions [role='region']:focus .interface-navigable-region__stacker": -1, ); @function z-index( $key ) { diff --git a/packages/components/src/higher-order/navigate-regions/style.scss b/packages/components/src/higher-order/navigate-regions/style.scss index 739cb1a9b6175c..86f089b68ba3d9 100644 --- a/packages/components/src/higher-order/navigate-regions/style.scss +++ b/packages/components/src/higher-order/navigate-regions/style.scss @@ -3,30 +3,82 @@ position: relative; } -.is-focusing-regions [role="region"] { - // For browsers that don't support outline-offset (IE11). - &:focus::after { - content: ""; - position: absolute; - top: 0; - bottom: 0; - left: 0; - right: 0; - pointer-events: none; - outline: 4px solid transparent; // Shown in Windows High Contrast mode. - box-shadow: inset 0 0 0 4px $components-color-accent; +.is-focusing-regions { + [role="region"]:focus { + outline: 4px solid $components-color-accent; + outline-offset: -4px; + + .interface-navigable-region__stacker { + position: relative; + z-index: z-index(".is-focusing-regions [role='region']:focus .interface-navigable-region__stacker"); + } + } + + // Fixes for edge cases. + // Some of the regions are currently used for layout purposes as 'interface skeleton' + // items. When they're absolutely positioned or when they contain absolutely + // positioned elements, they may have no size therefore the focus style is not + // visible. For the future, it's important to take into consideration that + // the navigable regions should always have a computed size. For now, we can + // fix some edge cases but these CSS rules should be later removed in favor of + // a more abstracted approach to make the navigabel regions focus style work + // regardles of the CSS used on other components. + + // Header top bar when Distraction free mode is on. + &.is-distraction-free .interface-interface-skeleton__header { + .interface-navigable-region__stacker, + .edit-post-header { + outline: inherit; + outline-offset: inherit; + } } - @supports ( outline-offset: 1px ) { - &:focus::after { - content: none; + // Sidebar toggle button shown when navigating regions. + .interface-interface-skeleton__sidebar { + .interface-navigable-region__stacker, + .edit-post-layout__toggle-sidebar-panel { + outline: inherit; + outline-offset: inherit; } + } - &:focus { - outline-style: solid; - outline-color: $components-color-accent; - outline-width: 4px; - outline-offset: -4px; + // Publish sidebar toggle button shown when navigating regions. + .interface-interface-skeleton__actions { + .interface-navigable-region__stacker, + .edit-post-layout__toggle-publish-panel { + outline: inherit; + outline-offset: inherit; } } + + // Publish sidebar. + [role="region"].interface-interface-skeleton__actions:focus .editor-post-publish-panel { + outline: 4px solid $components-color-accent; + outline-offset: -4px; + } + + // Edit site Navigation Drawer. + .interface-interface-skeleton__drawer { + z-index: z-index(".edit-site-navigation-toggle"); + + .interface-navigable-region__stacker, + .edit-site-navigation-toggle { + outline: inherit; + outline-offset: inherit; + } + + .edit-site-navigation-toggle.is-open { + outline: none; + } + + .edit-site-navigation-toggle__button { + z-index: -1; + } + } +} + +// Fixes for edge cases. +// Edit site Drawer. +.interface-interface-skeleton__drawer .interface-navigable-region__stacker { + height: 100%; } diff --git a/packages/edit-post/src/components/layout/style.scss b/packages/edit-post/src/components/layout/style.scss index f785b695489e31..473dae279473e9 100644 --- a/packages/edit-post/src/components/layout/style.scss +++ b/packages/edit-post/src/components/layout/style.scss @@ -60,7 +60,6 @@ justify-content: center; } - .edit-post-layout__toggle-publish-panel, .edit-post-layout__toggle-sidebar-panel, .edit-post-layout__toggle-entities-saved-states-panel { @@ -70,6 +69,7 @@ bottom: auto; left: auto; right: 0; + box-sizing: border-box; width: $sidebar-width; background-color: $white; border: 1px dotted $gray-300; @@ -77,7 +77,18 @@ padding: $grid-unit-30; display: flex; justify-content: center; +} + +.edit-post-layout__toggle-sidebar-panel { + .interface-interface-skeleton__sidebar:focus &, + .interface-interface-skeleton__sidebar:focus-within & { + top: auto; + bottom: 0; + } +} +.edit-post-layout__toggle-entities-saved-states-panel, +.edit-post-layout__toggle-publish-panel { .interface-interface-skeleton__actions:focus &, .interface-interface-skeleton__actions:focus-within & { top: auto; diff --git a/packages/edit-site/src/components/editor/style.scss b/packages/edit-site/src/components/editor/style.scss index 580268d00e9679..80a41bbb30be0d 100644 --- a/packages/edit-site/src/components/editor/style.scss +++ b/packages/edit-site/src/components/editor/style.scss @@ -7,16 +7,10 @@ html.wp-toolbar { } .edit-site-editor__toggle-save-panel { - z-index: z-index(".edit-site-editor__toggle-save-panel"); - position: fixed !important; // Need to override the default relative positioning - top: -9999em; - bottom: auto; - left: auto; - right: 0; + box-sizing: border-box; width: $sidebar-width; background-color: $white; border: 1px dotted $gray-300; - height: auto !important; // Need to override the default sidebar positioning padding: $grid-unit-30; display: flex; justify-content: center; diff --git a/packages/edit-site/src/components/navigation-sidebar/navigation-panel/style.scss b/packages/edit-site/src/components/navigation-sidebar/navigation-panel/style.scss index 6bab3a634fe783..a66a90876c8d4f 100644 --- a/packages/edit-site/src/components/navigation-sidebar/navigation-panel/style.scss +++ b/packages/edit-site/src/components/navigation-sidebar/navigation-panel/style.scss @@ -7,13 +7,6 @@ color: $white; transition: width 100ms linear; @include reduce-motion("transition"); - - // Footer is visible from medium so we subtract footer's height - .interface-interface-skeleton.has-footer & { - @include break-medium() { - height: calc(100% - #{$button-size-small + $border-width}); - } - } } .edit-site-navigation-panel__inner { diff --git a/packages/interface/complementary-area-context/index.js b/packages/interface/complementary-area-context/index.js new file mode 100644 index 00000000000000..bdfc6869e1e51d --- /dev/null +++ b/packages/interface/complementary-area-context/index.js @@ -0,0 +1,12 @@ +/** + * WordPress dependencies + */ +import { withPluginContext } from '@wordpress/plugins'; + +export default withPluginContext( ( context, ownProps ) => { + return { + icon: ownProps.icon || context.icon, + identifier: + ownProps.identifier || `${ context.name }/${ ownProps.name }`, + }; +} ); diff --git a/packages/interface/src/components/index.js b/packages/interface/src/components/index.js index aa03fa34ae8c6b..c9c2d09b3b3ab0 100644 --- a/packages/interface/src/components/index.js +++ b/packages/interface/src/components/index.js @@ -10,3 +10,4 @@ export { default as PreferencesModal } from './preferences-modal'; export { default as PreferencesModalTabs } from './preferences-modal-tabs'; export { default as PreferencesModalSection } from './preferences-modal-section'; export { default as ___unstablePreferencesModalBaseOption } from './preferences-modal-base-option'; +export { default as NavigableRegion } from './navigable-region'; diff --git a/packages/interface/src/components/interface-skeleton/index.js b/packages/interface/src/components/interface-skeleton/index.js index 766ef73a2fcf56..560943aa5d9634 100644 --- a/packages/interface/src/components/interface-skeleton/index.js +++ b/packages/interface/src/components/interface-skeleton/index.js @@ -7,13 +7,15 @@ import classnames from 'classnames'; * WordPress dependencies */ import { forwardRef, useEffect } from '@wordpress/element'; -import { - __unstableUseNavigateRegions as useNavigateRegions, - __unstableMotion as motion, -} from '@wordpress/components'; +import { __unstableUseNavigateRegions as useNavigateRegions } from '@wordpress/components'; import { __ } from '@wordpress/i18n'; import { useMergeRefs } from '@wordpress/compose'; +/** + * Internal dependencies + */ +import NavigableRegion from '../navigable-region'; + function useHTMLClass( className ) { useEffect( () => { const element = @@ -89,39 +91,35 @@ function InterfaceSkeleton( ) } > { !! drawer && ( -
{ drawer } -
+ ) }
{ !! header && isDistractionFree && ( - { header } - + ) } { !! header && ! isDistractionFree && ( -
{ header } -
+ ) } { isDistractionFree && (
@@ -130,59 +128,49 @@ function InterfaceSkeleton( ) }
{ !! secondarySidebar && ( -
{ secondarySidebar } -
+ ) } { !! notices && (
{ notices }
) } -
{ content } -
+ { !! sidebar && ( -
{ sidebar } -
+ ) } { !! actions && ( -
{ actions } -
+ ) }
{ !! footer && ( -
{ footer } -
+ ) }
); diff --git a/packages/interface/src/components/interface-skeleton/style.scss b/packages/interface/src/components/interface-skeleton/style.scss index 12c14cfcd2e319..a877371f8d5098 100644 --- a/packages/interface/src/components/interface-skeleton/style.scss +++ b/packages/interface/src/components/interface-skeleton/style.scss @@ -72,6 +72,10 @@ html.interface-interface-skeleton__html-container { } .interface-interface-skeleton__content { + .interface-navigable-region__stacker { + flex-grow: 1; + } + flex-grow: 1; // Treat as flex container to allow children to grow to occupy full @@ -93,7 +97,6 @@ html.interface-interface-skeleton__html-container { .interface-interface-skeleton__secondary-sidebar, .interface-interface-skeleton__sidebar { - display: block; flex-shrink: 0; position: absolute; z-index: z-index(".interface-interface-skeleton__sidebar"); @@ -107,8 +110,14 @@ html.interface-interface-skeleton__html-container { // On Mobile the header is fixed to keep HTML as scrollable. @include break-medium() { position: relative !important; - z-index: z-index(".interface-interface-skeleton__sidebar {greater than small}"); width: auto; // Keep the sidebar width flexible. + + // Set this z-index only when the sidebar is opened. When it's closed, the + // button to open the sidebar that is shown when navigating regions needs to + // be above the footer. See `edit-post-layout__toggle-sidebar-panel`. + .is-sidebar-opened & { + z-index: z-index(".interface-interface-skeleton__sidebar {greater than small}"); + } } } @@ -173,8 +182,16 @@ html.interface-interface-skeleton__html-container { width: $sidebar-width; color: $gray-900; - &:focus { + &:focus, + &:focus-within { top: auto; bottom: 0; } } + +// Footer is visible from medium so we subtract footer's height +.interface-interface-skeleton.has-footer .interface-interface-skeleton__drawer { + @include break-medium() { + height: calc(100% - #{$button-size-small + $border-width}); + } +} diff --git a/packages/interface/src/components/navigable-region/README.md b/packages/interface/src/components/navigable-region/README.md new file mode 100644 index 00000000000000..6f98d17d79c45a --- /dev/null +++ b/packages/interface/src/components/navigable-region/README.md @@ -0,0 +1,38 @@ +# NavigableRegion + +`NavigableRegion` renders an ARIA landmark region that's meant to be used together with the `useNavigateRegions` component from the `@wordpress/components` package. The ARIA landmark is a `div` element with a `role="region"` attribute and an `aria-label` attribute. It's made focusable by the means of a `tabindex="-1"` attribute so that `useNavigateRegions` can set focus on it and allow keyboard navigation through the regions in the editor. + +It also renders a child `div` element that is responsible to set a negative `z-index` stack level, to make sure the focus style is always visible, regardless of other elements that may cut-off the focus style outline otherwise. + +It can use a CSS animation via the `motion` component. + +## Props + +### children + +The component that should be rendered as content. + +- Type: React Element +- Required: Yes + +### className + +The CSS class that will be added to the classes of the wrapper div. + +- Type: `String` +- Required: No + +### ariaLabel + +A meaningful name for the ARIA landmark region. + +- Type: `String` +- Required: Yes + +### motionProps + +Properties of `motionProps` object will be used by the `motion` component to set a CSS animation on the wrapper div. + +- Type: `Object` +- Required: No +- Default: `{}` diff --git a/packages/interface/src/components/navigable-region/index.js b/packages/interface/src/components/navigable-region/index.js new file mode 100644 index 00000000000000..3b86b856b1d3f6 --- /dev/null +++ b/packages/interface/src/components/navigable-region/index.js @@ -0,0 +1,32 @@ +/** + * External dependencies + */ +import classnames from 'classnames'; + +/** + * WordPress dependencies + */ +import { __unstableMotion as motion } from '@wordpress/components'; + +export default function NavigableRegion( { + children, + className, + ariaLabel, + motionProps = {}, +} ) { + const Tag = Object.keys( motionProps ).length ? motion.div : 'div'; + + return ( + +
+ { children } +
+
+ ); +}