diff --git a/docs/reference-guides/core-blocks.md b/docs/reference-guides/core-blocks.md
index 38ad3e2e11bd13..4cb85864e57195 100644
--- a/docs/reference-guides/core-blocks.md
+++ b/docs/reference-guides/core-blocks.md
@@ -35,6 +35,15 @@ Add a user’s avatar. ([Source](https://github.com/WordPress/gutenberg/tree/tru
- **Supports:** align, color (~~background~~, ~~text~~), interactivity (clientNavigation), spacing (margin, padding), ~~alignWide~~, ~~html~~
- **Attributes:** isLink, linkTarget, size, userId
+## Back to top
+
+A link that takes you back to the top. ([Source](https://github.com/WordPress/gutenberg/tree/trunk/packages/block-library/src/back-to-top))
+
+- **Name:** core/back-to-top
+- **Category:** design
+- **Supports:** ~~html~~
+- **Attributes:** text
+
## Pattern
Reuse this design across your site. ([Source](https://github.com/WordPress/gutenberg/tree/trunk/packages/block-library/src/block))
diff --git a/lib/blocks.php b/lib/blocks.php
index e1d4622a0f23da..a6e97f8aa3e924 100644
--- a/lib/blocks.php
+++ b/lib/blocks.php
@@ -15,6 +15,7 @@ function gutenberg_reregister_core_block_types() {
__DIR__ . '/../build/block-library/blocks/' => array(
'block_folders' => array(
'audio',
+ 'back-to-top',
'button',
'buttons',
'freeform',
@@ -49,6 +50,7 @@ function gutenberg_reregister_core_block_types() {
'block_names' => array(
'archives.php' => 'core/archives',
'avatar.php' => 'core/avatar',
+ 'back-to-top.php' => 'core/back-to-top',
'block.php' => 'core/block',
'calendar.php' => 'core/calendar',
'categories.php' => 'core/categories',
diff --git a/packages/block-library/src/back-to-top/block.json b/packages/block-library/src/back-to-top/block.json
new file mode 100644
index 00000000000000..28ce04434e997c
--- /dev/null
+++ b/packages/block-library/src/back-to-top/block.json
@@ -0,0 +1,18 @@
+{
+ "$schema": "https://schemas.wp.org/trunk/block.json",
+ "apiVersion": 3,
+ "name": "core/back-to-top",
+ "title": "Back to top",
+ "category": "design",
+ "description": "A link that takes you back to the top.",
+ "keywords": [ "top", "skip link" ],
+ "textdomain": "default",
+ "attributes": {
+ "text": {
+ "type": "string"
+ }
+ },
+ "supports": {
+ "html": false
+ }
+}
diff --git a/packages/block-library/src/back-to-top/edit.js b/packages/block-library/src/back-to-top/edit.js
new file mode 100644
index 00000000000000..e1b1be66cdf87a
--- /dev/null
+++ b/packages/block-library/src/back-to-top/edit.js
@@ -0,0 +1,23 @@
+/**
+ * WordPress dependencies
+ */
+import { __ } from '@wordpress/i18n';
+import { useBlockProps, RichText } from '@wordpress/block-editor';
+
+export default function BackToTopEdit( { attributes, setAttributes } ) {
+ const { text } = attributes;
+ return (
+
+
+ setAttributes( { text: newLinkText } )
+ }
+ />
+
+ );
+}
diff --git a/packages/block-library/src/back-to-top/index.js b/packages/block-library/src/back-to-top/index.js
new file mode 100644
index 00000000000000..05e2c05bffe9bd
--- /dev/null
+++ b/packages/block-library/src/back-to-top/index.js
@@ -0,0 +1,20 @@
+/**
+ * WordPress dependencies
+ */
+import { arrowUp as icon } from '@wordpress/icons';
+
+/**
+ * Internal dependencies
+ */
+import initBlock from '../utils/init-block';
+import edit from './edit';
+import metadata from './block.json';
+
+const { name } = metadata;
+export { metadata, name };
+export const settings = {
+ icon,
+ edit,
+};
+
+export const init = () => initBlock( { name, metadata, settings } );
diff --git a/packages/block-library/src/back-to-top/index.php b/packages/block-library/src/back-to-top/index.php
new file mode 100644
index 00000000000000..39be602f4dbc21
--- /dev/null
+++ b/packages/block-library/src/back-to-top/index.php
@@ -0,0 +1,89 @@
+%2$s',
+ $wrapper_attributes,
+ wp_kses_post( $link_text )
+ );
+}
+
+/**
+ * Registers the `core/back_to_top` block on the server.
+ */
+function register_block_core_back_to_top() {
+ register_block_type_from_metadata(
+ __DIR__ . '/back-to-top',
+ array(
+ 'render_callback' => 'render_block_core_back_to_top',
+ )
+ );
+}
+add_action( 'init', 'register_block_core_back_to_top' );
+
+/**
+ * Adds the target id 'wp-back-to-top' to the top of the page, so that focus can be moved.
+ * Block themes: Add the target id if the back to top block exists on the page.
+ * Classic themes with 'wp_body_open()': Always add the target id.
+ */
+function block_core_back_to_top_target() {
+ echo '';
+}
+if ( wp_is_block_theme() ) {
+ add_filter(
+ 'render_block',
+ function ( $html, $block ) {
+ if ( 'core/back-to-top' === $block['blockName'] ) {
+ add_action( 'wp_body_open', 'block_core_back_to_top_target' );
+ }
+ return $html;
+ },
+ 10,
+ 2
+ );
+} else {
+ add_action( 'wp_body_open', 'block_core_back_to_top_target' );
+}
+
+/**
+ * For classic themes that do not use `wp_body_open()`,
+ * view.js is needed to move focus to the first focusable element on the top of the page.
+ */
+function block_core_back_to_top_classic_fallback() {
+ if ( ! wp_is_block_theme() && 0 === did_action( 'wp_body_open' ) ) {
+ // If the Gutenberg plugin is active, use the script from the plugin.
+ if ( defined( 'IS_GUTENBERG_PLUGIN' ) && IS_GUTENBERG_PLUGIN ) {
+ wp_enqueue_script(
+ 'wp-block-library-back-to-top-fallback',
+ plugins_url( 'back-to-top/view.min.js', __FILE__ ),
+ array(),
+ filemtime( plugin_dir_path( __FILE__ ) . 'back-to-top/view.min.js' ),
+ true
+ );
+ } else {
+ wp_enqueue_script(
+ 'wp-block-library-back-to-top-fallback',
+ includes_url( 'blocks/back-to-top/view.min.js', __DIR__ ),
+ array(),
+ '',
+ true
+ );
+ }
+ }
+}
diff --git a/packages/block-library/src/back-to-top/init.js b/packages/block-library/src/back-to-top/init.js
new file mode 100644
index 00000000000000..79f0492c2cb2f8
--- /dev/null
+++ b/packages/block-library/src/back-to-top/init.js
@@ -0,0 +1,6 @@
+/**
+ * Internal dependencies
+ */
+import { init } from './';
+
+export default init();
diff --git a/packages/block-library/src/back-to-top/view.js b/packages/block-library/src/back-to-top/view.js
new file mode 100644
index 00000000000000..ae0b058b1048c2
--- /dev/null
+++ b/packages/block-library/src/back-to-top/view.js
@@ -0,0 +1,40 @@
+/**
+ * This script is needed to move the focus to the first focusable element on the page.
+ * It is only intended to be loaded on the front of classic themes that does not include wp_body_open().
+ */
+window.addEventListener( 'load', () => {
+ const backToTopBlocks = document.querySelectorAll(
+ '.wp-block-back-to-top'
+ );
+ function moveFocusToTop() {
+ const topAnchor = document.getElementById( '#wp-back-to-top' );
+ if ( topAnchor ) {
+ topAnchor.querySelector( 'a' ).focus();
+ return;
+ }
+ // This list is not exhaustive, but covers common elements that can recieve focus.
+ const focusable = [
+ 'a[href]',
+ 'audio',
+ 'button:not([disabled])',
+ '[contenteditable]',
+ 'details',
+ 'embed',
+ 'iframe',
+ 'input:not([disabled]):not([type="hidden"])',
+ 'object',
+ '[role="button"][tabindex="0"]',
+ '[role="link"][tabindex="0"]',
+ 'select:not([disabled])',
+ 'summary',
+ 'textarea:not([disabled])',
+ 'video',
+ '[tabindex]:not([tabindex="-1"])',
+ ];
+ document.querySelector( focusable.join( ', ' ) ).focus();
+ }
+
+ backToTopBlocks.forEach( ( backToTop ) => {
+ backToTop.addEventListener( 'click', moveFocusToTop );
+ } );
+} );
diff --git a/packages/block-library/src/index.js b/packages/block-library/src/index.js
index e2e0fd9e414ef3..5be66033014d9b 100644
--- a/packages/block-library/src/index.js
+++ b/packages/block-library/src/index.js
@@ -23,6 +23,7 @@ import {
import * as archives from './archives';
import * as avatar from './avatar';
import * as audio from './audio';
+import * as backToTop from './back-to-top';
import * as button from './button';
import * as buttons from './buttons';
import * as calendar from './calendar';
@@ -142,6 +143,7 @@ const getAllBlocks = () => {
// Register all remaining core blocks.
archives,
audio,
+ backToTop,
button,
buttons,
calendar,
diff --git a/test/integration/fixtures/blocks/core__back-to-top.html b/test/integration/fixtures/blocks/core__back-to-top.html
new file mode 100644
index 00000000000000..efbe819d59be4f
--- /dev/null
+++ b/test/integration/fixtures/blocks/core__back-to-top.html
@@ -0,0 +1 @@
+
diff --git a/test/integration/fixtures/blocks/core__back-to-top.json b/test/integration/fixtures/blocks/core__back-to-top.json
new file mode 100644
index 00000000000000..8f8325f34e6d5d
--- /dev/null
+++ b/test/integration/fixtures/blocks/core__back-to-top.json
@@ -0,0 +1,8 @@
+[
+ {
+ "name": "core/back-to-top",
+ "isValid": true,
+ "attributes": {},
+ "innerBlocks": []
+ }
+]
diff --git a/test/integration/fixtures/blocks/core__back-to-top.parsed.json b/test/integration/fixtures/blocks/core__back-to-top.parsed.json
new file mode 100644
index 00000000000000..dbc8929d2bc31a
--- /dev/null
+++ b/test/integration/fixtures/blocks/core__back-to-top.parsed.json
@@ -0,0 +1,9 @@
+[
+ {
+ "blockName": "core/back-to-top",
+ "attrs": {},
+ "innerBlocks": [],
+ "innerHTML": "",
+ "innerContent": []
+ }
+]
diff --git a/test/integration/fixtures/blocks/core__back-to-top.serialized.html b/test/integration/fixtures/blocks/core__back-to-top.serialized.html
new file mode 100644
index 00000000000000..efbe819d59be4f
--- /dev/null
+++ b/test/integration/fixtures/blocks/core__back-to-top.serialized.html
@@ -0,0 +1 @@
+