diff --git a/packages/block-library/src/details/block.json b/packages/block-library/src/details/block.json
new file mode 100644
index 00000000000000..ee8cbd0ab8aac6
--- /dev/null
+++ b/packages/block-library/src/details/block.json
@@ -0,0 +1,13 @@
+{
+ "name": "core/details",
+ "category": "layout",
+ "attributes": {
+ "initialOpen": {
+ "type": "boolean",
+ "default": false
+ },
+ "summaryContent": {
+ "type": "string"
+ }
+ }
+}
diff --git a/packages/block-library/src/details/edit.js b/packages/block-library/src/details/edit.js
new file mode 100644
index 00000000000000..962045ee397eb6
--- /dev/null
+++ b/packages/block-library/src/details/edit.js
@@ -0,0 +1,91 @@
+/**
+ * WordPress dependencies
+ */
+import {
+ InnerBlocks,
+ RichText,
+ InspectorControls,
+} from '@wordpress/block-editor';
+import { __ } from '@wordpress/i18n';
+import { useSelect } from '@wordpress/data';
+import {
+ ToggleControl,
+ Panel,
+ PanelBody,
+ PanelRow,
+} from '@wordpress/components';
+import { useEffect, useRef } from '@wordpress/element';
+import { SPACE } from '@wordpress/keycodes';
+
+export default function DetalsEdit( {
+ attributes,
+ className,
+ clientId,
+ isSelected,
+ setAttributes,
+} ) {
+ const summaryRef = useRef( null );
+
+ const keyUpListener = ( e ) => {
+ if ( e.keyCode === SPACE ) {
+ e.preventDefault();
+ }
+ };
+
+ const clickListener = ( e ) => e.preventDefault();
+
+ useEffect( () => {
+ if ( ! summaryRef.current ) {
+ return;
+ }
+
+ summaryRef.current.addEventListener( 'keyup', keyUpListener );
+ summaryRef.current.addEventListener( 'click', clickListener );
+ return () => {
+ summaryRef.current.removeEventListener( 'keyup', keyUpListener );
+ summaryRef.current.removeEventListener( 'click', clickListener );
+ };
+ }, [ summaryRef.current ] );
+
+ const isInnerBlockSelected = useSelect(
+ ( select ) =>
+ select( 'core/block-editor' ).hasSelectedInnerBlock( clientId ),
+ [ clientId ]
+ );
+
+ const showInnerBlocks =
+ attributes.initialOpen || isSelected || isInnerBlockSelected;
+
+ return (
+ <>
+
+
+
+
+
+ setAttributes( { initialOpen } )
+ }
+ checked={ attributes.initialOpen }
+ />
+
+
+
+
+
+
+ setAttributes( { summaryContent } )
+ }
+ ref={ summaryRef }
+ placeholder={ __( 'Write a summary…' ) }
+ aria-label={ __( 'Summary text' ) }
+ />
+
+
+ >
+ );
+}
diff --git a/packages/block-library/src/details/editor.scss b/packages/block-library/src/details/editor.scss
new file mode 100644
index 00000000000000..5c06909f94520f
--- /dev/null
+++ b/packages/block-library/src/details/editor.scss
@@ -0,0 +1,5 @@
+.wp-block-details {
+ summary .rich-text {
+ display: inline;
+ }
+}
diff --git a/packages/block-library/src/details/index.js b/packages/block-library/src/details/index.js
new file mode 100644
index 00000000000000..a439035fd61880
--- /dev/null
+++ b/packages/block-library/src/details/index.js
@@ -0,0 +1,28 @@
+/**
+ * WordPress dependencies
+ */
+import { __ } from '@wordpress/i18n';
+import { details as icon } from '@wordpress/icons';
+
+/**
+ * Internal dependencies
+ */
+import edit from './edit';
+import metadata from './block.json';
+import save from './save';
+
+const { name, category, attributes } = metadata;
+
+export { metadata, name };
+
+export const settings = {
+ title: __( 'Details' ),
+ description: __( 'Create a toggle to show and hide content.' ),
+ icon,
+ category,
+ edit,
+ save,
+ // details and summary are not translated because they are the HTML tags
+ keywords: [ 'details', 'summary', __( 'faq' ) ],
+ attributes,
+};
diff --git a/packages/block-library/src/details/save.js b/packages/block-library/src/details/save.js
new file mode 100644
index 00000000000000..c3c1d2d03108d5
--- /dev/null
+++ b/packages/block-library/src/details/save.js
@@ -0,0 +1,16 @@
+/**
+ * WordPress dependencies
+ */
+import { InnerBlocks, RichText } from '@wordpress/block-editor';
+
+export default ( { attributes } ) => {
+ return (
+
+
+
+
+ );
+};
diff --git a/packages/block-library/src/editor.scss b/packages/block-library/src/editor.scss
index c7c23d390dca10..e01d42131994b4 100644
--- a/packages/block-library/src/editor.scss
+++ b/packages/block-library/src/editor.scss
@@ -12,6 +12,7 @@
@import "./code/editor.scss";
@import "./columns/editor.scss";
@import "./cover/editor.scss";
+@import "./details/editor.scss";
@import "./embed/editor.scss";
@import "./file/editor.scss";
@import "./classic/editor.scss";
diff --git a/packages/block-library/src/index.js b/packages/block-library/src/index.js
index d92ab8f3a05429..1ce85cc737c643 100644
--- a/packages/block-library/src/index.js
+++ b/packages/block-library/src/index.js
@@ -31,6 +31,7 @@ import * as code from './code';
import * as columns from './columns';
import * as column from './column';
import * as cover from './cover';
+import * as details from './details';
import * as embed from './embed';
import * as file from './file';
import * as html from './html';
@@ -133,6 +134,7 @@ export const registerCoreBlocks = () => {
columns,
column,
cover,
+ details,
embed,
file,
group,
diff --git a/packages/e2e-tests/fixtures/blocks/core__details.html b/packages/e2e-tests/fixtures/blocks/core__details.html
new file mode 100644
index 00000000000000..fa943245021d17
--- /dev/null
+++ b/packages/e2e-tests/fixtures/blocks/core__details.html
@@ -0,0 +1,12 @@
+
+
+ This is the summary
+
+ More here
+
+
+
+
+
+
+
diff --git a/packages/e2e-tests/fixtures/blocks/core__details.json b/packages/e2e-tests/fixtures/blocks/core__details.json
new file mode 100644
index 00000000000000..03beb29ac4a817
--- /dev/null
+++ b/packages/e2e-tests/fixtures/blocks/core__details.json
@@ -0,0 +1,36 @@
+[
+ {
+ "clientId": "_clientId_0",
+ "name": "core/details",
+ "isValid": true,
+ "attributes": {
+ "initialOpen": false,
+ "summaryContent": "This is the summary "
+ },
+ "innerBlocks": [
+ {
+ "clientId": "_clientId_0",
+ "name": "core/heading",
+ "isValid": true,
+ "attributes": {
+ "content": "More here",
+ "level": 2
+ },
+ "innerBlocks": [],
+ "originalContent": "
More here
"
+ },
+ {
+ "clientId": "_clientId_1",
+ "name": "core/paragraph",
+ "isValid": true,
+ "attributes": {
+ "content": "",
+ "dropCap": false
+ },
+ "innerBlocks": [],
+ "originalContent": ""
+ }
+ ],
+ "originalContent": "\n\tThis is the summary
\n\t\n\n\t\n "
+ }
+]
diff --git a/packages/e2e-tests/fixtures/blocks/core__details.parsed.json b/packages/e2e-tests/fixtures/blocks/core__details.parsed.json
new file mode 100644
index 00000000000000..4c8575353bb0d8
--- /dev/null
+++ b/packages/e2e-tests/fixtures/blocks/core__details.parsed.json
@@ -0,0 +1,46 @@
+[
+ {
+ "blockName": "core/details",
+ "attrs": {
+ "summaryContent": "This is the summary ",
+ "initialOpen": false
+ },
+ "innerBlocks": [
+ {
+ "blockName": "core/heading",
+ "attrs": {},
+ "innerBlocks": [],
+ "innerHTML": "\n\tMore here
\n\t",
+ "innerContent": [
+ "\n\tMore here
\n\t"
+ ]
+ },
+ {
+ "blockName": "core/paragraph",
+ "attrs": {},
+ "innerBlocks": [],
+ "innerHTML": "\n\t\n\t",
+ "innerContent": [
+ "\n\t\n\t"
+ ]
+ }
+ ],
+ "innerHTML": "\n\n\tThis is the summary
\n\t\n\n\t\n \n",
+ "innerContent": [
+ "\n\n\tThis is the summary
\n\t",
+ null,
+ "\n\n\t",
+ null,
+ "\n \n"
+ ]
+ },
+ {
+ "blockName": null,
+ "attrs": {},
+ "innerBlocks": [],
+ "innerHTML": "\n",
+ "innerContent": [
+ "\n"
+ ]
+ }
+]
diff --git a/packages/e2e-tests/fixtures/blocks/core__details.serialized.html b/packages/e2e-tests/fixtures/blocks/core__details.serialized.html
new file mode 100644
index 00000000000000..8fa6ac9c91cf5e
--- /dev/null
+++ b/packages/e2e-tests/fixtures/blocks/core__details.serialized.html
@@ -0,0 +1,9 @@
+
+This is the summary
+More here
+
+
+
+
+
+
diff --git a/packages/icons/src/index.js b/packages/icons/src/index.js
index 9054f43a6c4286..f0fcca0acbdbb0 100644
--- a/packages/icons/src/index.js
+++ b/packages/icons/src/index.js
@@ -49,6 +49,7 @@ export { default as currencyDollar } from './library/currency-dollar';
export { default as currencyEuro } from './library/currency-euro';
export { default as currencyPound } from './library/currency-pound';
export { default as desktop } from './library/desktop';
+export { default as details } from './library/details';
export { default as edit } from './library/edit';
export { default as external } from './library/external';
export { default as file } from './library/file';
diff --git a/packages/icons/src/library/details.js b/packages/icons/src/library/details.js
new file mode 100644
index 00000000000000..ef9d3c95051077
--- /dev/null
+++ b/packages/icons/src/library/details.js
@@ -0,0 +1,12 @@
+/**
+ * WordPress dependencies
+ */
+import { SVG, Path } from '@wordpress/primitives';
+
+const details = (
+
+);
+
+export default details;
diff --git a/packages/rich-text/src/component/index.js b/packages/rich-text/src/component/index.js
index 76d8fc64d13268..ad70861aa0c549 100644
--- a/packages/rich-text/src/component/index.js
+++ b/packages/rich-text/src/component/index.js
@@ -527,6 +527,11 @@ function RichText( {
function handleSpace( event ) {
const { keyCode, shiftKey, altKey, metaKey, ctrlKey } = event;
+ if ( keyCode === SPACE && TagName === 'summary' ) {
+ handleChange( insert( createRecord(), ' ' ) );
+ return;
+ }
+
if (
// Only override when no modifiers are pressed.
shiftKey ||