diff --git a/assets/stylesheets/_z-index.scss b/assets/stylesheets/_z-index.scss
index 7b7e0aedd55301..698398ec9f83e3 100644
--- a/assets/stylesheets/_z-index.scss
+++ b/assets/stylesheets/_z-index.scss
@@ -24,6 +24,7 @@ $z-layers: (
".block-editor-warning": 5,
".block-library-gallery-item__inline-menu": 20,
".block-editor-url-input__suggestions": 30,
+ ".edit-post-layout__footer": 30,
".edit-post-header": 30,
".edit-widgets-header": 30,
".block-library-button__inline-link .block-editor-url-input__suggestions": 6, // URL suggestions for button block above sibling inserter
diff --git a/packages/block-editor/README.md b/packages/block-editor/README.md
index d9c7818065c0b5..7e32a9dfbabe75 100644
--- a/packages/block-editor/README.md
+++ b/packages/block-editor/README.md
@@ -82,6 +82,20 @@ _Related_
Undocumented declaration.
+# **BlockBreadcrumb**
+
+Block breadcrumb component, displaying the label of the block. If the block
+descends from a root block, a button is displayed enabling the user to select
+the root block.
+
+_Parameters_
+
+- _props.clientId_ `string`: Client ID of block.
+
+_Returns_
+
+- `WPElement`: Block Breadcrumb.
+
# **BlockControls**
Undocumented declaration.
diff --git a/packages/block-editor/src/components/block-breadcrumb/index.js b/packages/block-editor/src/components/block-breadcrumb/index.js
new file mode 100644
index 00000000000000..57b46acafe7416
--- /dev/null
+++ b/packages/block-editor/src/components/block-breadcrumb/index.js
@@ -0,0 +1,52 @@
+/**
+ * WordPress dependencies
+ */
+import { Button } from '@wordpress/components';
+import { useSelect, useDispatch } from '@wordpress/data';
+
+/**
+ * Internal dependencies
+ */
+import BlockTitle from '../block-title';
+
+/**
+ * Block breadcrumb component, displaying the label of the block. If the block
+ * descends from a root block, a button is displayed enabling the user to select
+ * the root block.
+ *
+ * @param {string} props.clientId Client ID of block.
+ * @return {WPElement} Block Breadcrumb.
+ */
+const BlockBreadcrumb = function() {
+ const { selectBlock } = useDispatch( 'core/block-editor' );
+ const { clientId, parents } = useSelect( ( select ) => {
+ const selectedBlockClientId = select( 'core/block-editor' ).getSelectedBlockClientId();
+ return {
+ parents: select( 'core/block-editor' ).getBlockParents( selectedBlockClientId ),
+ clientId: selectedBlockClientId,
+ };
+ }, [] );
+
+ return (
+
+ { parents.map( ( parent ) => (
+ -
+
+
+ ) ) }
+ { !! clientId && (
+ -
+
+
+ ) }
+
+ );
+};
+
+export default BlockBreadcrumb;
diff --git a/packages/block-editor/src/components/block-breadcrumb/style.scss b/packages/block-editor/src/components/block-breadcrumb/style.scss
new file mode 100644
index 00000000000000..fa71051fee331b
--- /dev/null
+++ b/packages/block-editor/src/components/block-breadcrumb/style.scss
@@ -0,0 +1,24 @@
+.block-editor-block-breadcrumb {
+ list-style: none;
+ padding: 0;
+ margin: 0;
+
+ li {
+ display: inline-block;
+ margin: 0;
+
+ &:not(.block-editor-block-breadcrumb__current)::after {
+ content: "▸";
+ }
+ }
+}
+
+.block-editor-block-breadcrumb__button.components-button {
+ color: inherit;
+ font-size: inherit;
+}
+
+.block-editor-block-breadcrumb__current {
+ padding: 0 10px;
+ font-size: inherit;
+}
diff --git a/packages/block-editor/src/components/index.js b/packages/block-editor/src/components/index.js
index 6e9bb592378678..89de32071c330f 100644
--- a/packages/block-editor/src/components/index.js
+++ b/packages/block-editor/src/components/index.js
@@ -7,6 +7,7 @@ export * from './font-sizes';
export { default as AlignmentToolbar } from './alignment-toolbar';
export { default as Autocomplete } from './autocomplete';
export { default as BlockAlignmentToolbar } from './block-alignment-toolbar';
+export { default as BlockBreadcrumb } from './block-breadcrumb';
export { default as BlockControls } from './block-controls';
export { default as BlockEdit } from './block-edit';
export { default as BlockFormatControls } from './block-format-controls';
diff --git a/packages/block-editor/src/store/selectors.js b/packages/block-editor/src/store/selectors.js
index d0dd7a69a15973..7b52e58cdade06 100644
--- a/packages/block-editor/src/store/selectors.js
+++ b/packages/block-editor/src/store/selectors.js
@@ -418,6 +418,22 @@ export function getBlockRootClientId( state, clientId ) {
null;
}
+export const getBlockParents = createSelector(
+ ( state, clientId ) => {
+ const parents = [];
+ let current = clientId;
+ while ( !! state.blocks.parents[ current ] ) {
+ current = state.blocks.parents[ current ];
+ parents.push( current );
+ }
+
+ return parents.reverse();
+ },
+ ( state ) => [
+ state.blocks.parents,
+ ]
+);
+
/**
* Given a block client ID, returns the root of the hierarchy from which the block is nested, return the block itself for root level blocks.
*
diff --git a/packages/block-editor/src/style.scss b/packages/block-editor/src/style.scss
index 8ebb2e487a4f29..7296c0fa788620 100644
--- a/packages/block-editor/src/style.scss
+++ b/packages/block-editor/src/style.scss
@@ -3,6 +3,7 @@
@import "./components/block-inspector/style.scss";
@import "./components/block-list/style.scss";
@import "./components/block-list-appender/style.scss";
+@import "./components/block-breadcrumb/style.scss";
@import "./components/block-card/style.scss";
@import "./components/block-compare/style.scss";
@import "./components/block-mover/style.scss";
diff --git a/packages/edit-post/src/components/layout/index.js b/packages/edit-post/src/components/layout/index.js
index e29011c8cec529..22e570423c4493 100644
--- a/packages/edit-post/src/components/layout/index.js
+++ b/packages/edit-post/src/components/layout/index.js
@@ -21,6 +21,7 @@ import {
EditorNotices,
PostPublishPanel,
} from '@wordpress/editor';
+import { BlockBreadcrumb } from '@wordpress/block-editor';
import { withDispatch, withSelect } from '@wordpress/data';
import { PluginArea } from '@wordpress/plugins';
import { withViewportMatch } from '@wordpress/viewport';
@@ -56,6 +57,7 @@ function Layout( {
isSaving,
isMobileViewport,
isRichEditingEnabled,
+ showFooter,
} ) {
const sidebarIsOpened = editorSidebarOpened || pluginSidebarOpened || publishSidebarOpened;
@@ -92,7 +94,16 @@ function Layout( {
{ ( mode === 'text' || ! isRichEditingEnabled ) && }
- { isRichEditingEnabled && mode === 'visual' && }
+ { isRichEditingEnabled && mode === 'visual' && (
+ <>
+
+ { showFooter && (
+
+
+
+ ) }
+ >
+ ) }
@@ -145,6 +156,7 @@ export default compose(
hasActiveMetaboxes: select( 'core/edit-post' ).hasMetaBoxes(),
isSaving: select( 'core/edit-post' ).isSavingMetaBoxes(),
isRichEditingEnabled: select( 'core/editor' ).getEditorSettings().richEditingEnabled,
+ showFooter: !! select( 'core/block-editor' ).getSelectedBlockClientId(),
} ) ),
withDispatch( ( dispatch ) => {
const { closePublishSidebar, togglePublishSidebar } = dispatch( 'core/edit-post' );
diff --git a/packages/edit-post/src/components/layout/style.scss b/packages/edit-post/src/components/layout/style.scss
index 43996d98f8c8f0..7e3d4cf30e5320 100644
--- a/packages/edit-post/src/components/layout/style.scss
+++ b/packages/edit-post/src/components/layout/style.scss
@@ -234,3 +234,28 @@
}
}
}
+
+.edit-post-layout__footer {
+ display: none;
+ z-index: z-index(".edit-post-layout__footer");
+
+ // Stretch to mimic outline padding on desktop.
+ @include break-medium() {
+ display: inline-flex;
+ position: fixed;
+ bottom: 0;
+ right: 0;
+ background: $white;
+ height: 30px;
+ padding: 0 10px;
+ align-items: center;
+ border-top: $border-width solid $light-gray-500;
+ font-size: 12px;
+
+ .edit-post-layout.is-sidebar-opened & {
+ right: $sidebar-width;
+ }
+ }
+}
+@include editor-left(".edit-post-layout__footer");
+