Index: /trunk/src/wp-includes/rest-api/class-wp-rest-server.php
===================================================================
--- /trunk/src/wp-includes/rest-api/class-wp-rest-server.php	(revision 49924)
+++ /trunk/src/wp-includes/rest-api/class-wp-rest-server.php	(revision 49925)
@@ -1245,6 +1245,6 @@
 
 		$response = new WP_REST_Response( $available );
-
 		$response->add_link( 'help', 'http://v2.wp-api.org/' );
+		$this->add_active_theme_link_to_index( $response );
 
 		/**
@@ -1260,4 +1260,33 @@
 		 */
 		return apply_filters( 'rest_index', $response );
+	}
+
+	/**
+	 * Adds a link to the active theme for users who have proper permissions.
+	 *
+	 * @since 5.7.0
+	 *
+	 * @param WP_REST_Response $response REST API response.
+	 */
+	protected function add_active_theme_link_to_index( WP_REST_Response $response ) {
+		$should_add = current_user_can( 'switch_themes' ) || current_user_can( 'manage_network_themes' );
+
+		if ( ! $should_add && current_user_can( 'edit_posts' ) ) {
+			$should_add = true;
+		}
+
+		if ( ! $should_add ) {
+			foreach ( get_post_types( array( 'show_in_rest' => true ), 'objects' ) as $post_type ) {
+				if ( current_user_can( $post_type->cap->edit_posts ) ) {
+					$should_add = true;
+					break;
+				}
+			}
+		}
+
+		if ( $should_add ) {
+			$theme = wp_get_theme();
+			$response->add_link( 'https://api.w.org/active-theme', rest_url( 'wp/v2/themes/' . $theme->get_stylesheet() ) );
+		}
 	}
 
Index: /trunk/src/wp-includes/rest-api/endpoints/class-wp-rest-themes-controller.php
===================================================================
--- /trunk/src/wp-includes/rest-api/endpoints/class-wp-rest-themes-controller.php	(revision 49924)
+++ /trunk/src/wp-includes/rest-api/endpoints/class-wp-rest-themes-controller.php	(revision 49925)
@@ -48,4 +48,23 @@
 			)
 		);
+
+		register_rest_route(
+			$this->namespace,
+			'/' . $this->rest_base . '/(?P<stylesheet>[\w-]+)',
+			array(
+				'args'   => array(
+					'stylesheet' => array(
+						'description' => __( "The theme's stylesheet. This uniquely identifies the theme." ),
+						'type'        => 'string',
+					),
+				),
+				array(
+					'methods'             => WP_REST_Server::READABLE,
+					'callback'            => array( $this, 'get_item' ),
+					'permission_callback' => array( $this, 'get_item_permissions_check' ),
+				),
+				'schema' => array( $this, 'get_public_item_schema' ),
+			)
+		);
 	}
 
@@ -59,4 +78,55 @@
 	 */
 	public function get_items_permissions_check( $request ) {
+		if ( current_user_can( 'switch_themes' ) || current_user_can( 'manage_network_themes' ) ) {
+			return true;
+		}
+
+		$registered = $this->get_collection_params();
+		if ( isset( $registered['status'], $request['status'] ) && is_array( $request['status'] ) && array( 'active' ) === $request['status'] ) {
+			return $this->check_read_active_theme_permission();
+		}
+
+		return new WP_Error(
+			'rest_cannot_view_themes',
+			__( 'Sorry, you are not allowed to view themes.' ),
+			array( 'status' => rest_authorization_required_code() )
+		);
+	}
+
+	/**
+	 * Checks if a given request has access to read the theme.
+	 *
+	 * @since 5.7.0
+	 *
+	 * @param WP_REST_Request $request Full details about the request.
+	 * @return bool|WP_Error True if the request has read access for the item, otherwise WP_Error object.
+	 */
+	public function get_item_permissions_check( $request ) {
+		if ( current_user_can( 'switch_themes' ) || current_user_can( 'manage_network_themes' ) ) {
+			return true;
+		}
+
+		$wp_theme      = wp_get_theme( $request['stylesheet'] );
+		$current_theme = wp_get_theme();
+
+		if ( $this->is_same_theme( $wp_theme, $current_theme ) ) {
+			return $this->check_read_active_theme_permission();
+		}
+
+		return new WP_Error(
+			'rest_cannot_view_themes',
+			__( 'Sorry, you are not allowed to view themes.' ),
+			array( 'status' => rest_authorization_required_code() )
+		);
+	}
+
+	/**
+	 * Checks if a theme can be read.
+	 *
+	 * @since 5.7.0
+	 *
+	 * @return bool|WP_Error Whether the theme can be read.
+	 */
+	protected function check_read_active_theme_permission() {
 		if ( current_user_can( 'edit_posts' ) ) {
 			return true;
@@ -70,6 +140,6 @@
 
 		return new WP_Error(
-			'rest_user_cannot_view',
-			__( 'Sorry, you are not allowed to view themes.' ),
+			'rest_cannot_view_active_theme',
+			__( 'Sorry, you are not allowed to view the active theme.' ),
 			array( 'status' => rest_authorization_required_code() )
 		);
@@ -77,20 +147,48 @@
 
 	/**
-	 * Retrieves a collection of themes.
-	 *
-	 * @since 5.0.0
+	 * Retrieves a single theme.
+	 *
+	 * @since 5.7.0
 	 *
 	 * @param WP_REST_Request $request Full details about the request.
 	 * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
 	 */
+	public function get_item( $request ) {
+		$wp_theme = wp_get_theme( $request['stylesheet'] );
+		if ( ! $wp_theme->exists() ) {
+			return new WP_Error(
+				'rest_theme_not_found',
+				__( 'Theme not found.' ),
+				array( 'status' => 404 )
+			);
+		}
+		$data = $this->prepare_item_for_response( $wp_theme, $request );
+
+		return rest_ensure_response( $data );
+	}
+
+	/**
+	 * Retrieves a collection of themes.
+	 *
+	 * @since 5.0.0
+	 *
+	 * @param WP_REST_Request $request Full details about the request.
+	 * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
+	 */
 	public function get_items( $request ) {
-		// Retrieve the list of registered collection query parameters.
-		$registered = $this->get_collection_params();
-		$themes     = array();
-
-		if ( isset( $registered['status'], $request['status'] ) && in_array( 'active', $request['status'], true ) ) {
-			$active_theme = wp_get_theme();
-			$active_theme = $this->prepare_item_for_response( $active_theme, $request );
-			$themes[]     = $this->prepare_response_for_collection( $active_theme );
+		$themes = array();
+
+		$active_themes = wp_get_themes();
+		$current_theme = wp_get_theme();
+		$status        = $request['status'];
+
+		foreach ( $active_themes as $theme_name => $theme ) {
+			$theme_status = ( $this->is_same_theme( $theme, $current_theme ) ) ? 'active' : 'inactive';
+			if ( is_array( $status ) && ! in_array( $theme_status, $status, true ) ) {
+				continue;
+			}
+
+			$prepared = $this->prepare_item_for_response( $theme, $request );
+			$themes[] = $this->prepare_response_for_collection( $prepared );
 		}
 
@@ -98,5 +196,5 @@
 
 		$response->header( 'X-WP-Total', count( $themes ) );
-		$response->header( 'X-WP-TotalPages', count( $themes ) );
+		$response->header( 'X-WP-TotalPages', 1 );
 
 		return $response;
@@ -167,5 +265,10 @@
 		}
 
-		if ( rest_is_field_included( 'theme_supports', $fields ) ) {
+		$current_theme = wp_get_theme();
+		if ( rest_is_field_included( 'status', $fields ) ) {
+			$data['status'] = ( $this->is_same_theme( $theme, $current_theme ) ) ? 'active' : 'inactive';
+		}
+
+		if ( rest_is_field_included( 'theme_supports', $fields ) && $this->is_same_theme( $theme, $current_theme ) ) {
 			foreach ( get_registered_theme_features() as $feature => $config ) {
 				if ( ! is_array( $config['show_in_rest'] ) ) {
@@ -206,4 +309,6 @@
 		// Wrap the data in a response object.
 		$response = rest_ensure_response( $data );
+
+		$response->add_links( $this->prepare_links( $theme ) );
 
 		/**
@@ -220,4 +325,37 @@
 
 	/**
+	 * Prepares links for the request.
+	 *
+	 * @since 5.7.0
+	 *
+	 * @param WP_Theme $theme Theme data.
+	 * @return array Links for the given block type.
+	 */
+	protected function prepare_links( $theme ) {
+		return array(
+			'self'       => array(
+				'href' => rest_url( sprintf( '%s/%s/%s', $this->namespace, $this->rest_base, $theme->get_stylesheet() ) ),
+			),
+			'collection' => array(
+				'href' => rest_url( sprintf( '%s/%s', $this->namespace, $this->rest_base ) ),
+			),
+		);
+	}
+
+	/**
+	 * Helper function to compare two themes.
+	 *
+	 * @since 5.7.0
+	 *
+	 * @param WP_Theme $theme_a First theme to compare.
+	 * @param WP_Theme $theme_b Second theme to compare.
+	 *
+	 * @return bool
+	 */
+	protected function is_same_theme( $theme_a, $theme_b ) {
+		return $theme_a->get_stylesheet() === $theme_b->get_stylesheet();
+	}
+
+	/**
 	 * Prepares the theme support value for inclusion in the REST API response.
 	 *
@@ -400,4 +538,9 @@
 					'readonly'    => true,
 				),
+				'status'         => array(
+					'description' => __( 'A named status for the theme.' ),
+					'type'        => 'string',
+					'enum'        => array( 'inactive', 'active' ),
+				),
 			),
 		);
@@ -426,15 +569,13 @@
 	 */
 	public function get_collection_params() {
-		$query_params = parent::get_collection_params();
-
-		$query_params['status'] = array(
-			'description'       => __( 'Limit result set to themes assigned one or more statuses.' ),
-			'type'              => 'array',
-			'items'             => array(
-				'enum' => array( 'active' ),
-				'type' => 'string',
+		$query_params = array(
+			'status' => array(
+				'description' => __( 'Limit result set to themes assigned one or more statuses.' ),
+				'type'        => 'array',
+				'items'       => array(
+					'enum' => array( 'active', 'inactive' ),
+					'type' => 'string',
+				),
 			),
-			'required'          => true,
-			'sanitize_callback' => array( $this, 'sanitize_theme_status' ),
 		);
 
@@ -453,4 +594,5 @@
 	 *
 	 * @since 5.0.0
+	 * @deprecated 5.7.0
 	 *
 	 * @param string|array    $statuses  One or more theme statuses.
@@ -460,4 +602,6 @@
 	 */
 	public function sanitize_theme_status( $statuses, $request, $parameter ) {
+		_deprecated_function( __METHOD__, '5.7.0' );
+
 		$statuses = wp_parse_slug_list( $statuses );
 
Index: /trunk/tests/phpunit/tests/rest-api/rest-schema-setup.php
===================================================================
--- /trunk/tests/phpunit/tests/rest-api/rest-schema-setup.php	(revision 49924)
+++ /trunk/tests/phpunit/tests/rest-api/rest-schema-setup.php	(revision 49925)
@@ -130,4 +130,5 @@
 			'/wp/v2/settings',
 			'/wp/v2/themes',
+			'/wp/v2/themes/(?P<stylesheet>[\w-]+)',
 			'/wp/v2/plugins',
 			'/wp/v2/plugins/(?P<plugin>[^.\/]+(?:\/[^.\/]+)?)',
Index: /trunk/tests/phpunit/tests/rest-api/rest-server.php
===================================================================
--- /trunk/tests/phpunit/tests/rest-api/rest-server.php	(revision 49924)
+++ /trunk/tests/phpunit/tests/rest-api/rest-server.php	(revision 49925)
@@ -960,4 +960,7 @@
 		$this->assertContains( 'DELETE', $route['methods'] );
 		$this->assertArrayHasKey( '_links', $route );
+
+		$this->assertArrayHasKey( 'help', $index->get_links() );
+		$this->assertArrayNotHasKey( 'wp:active-theme', $index->get_links() );
 	}
 
@@ -1997,4 +2000,14 @@
 	}
 
+	/**
+	 * @ticket 50152
+	 */
+	public function test_index_includes_link_to_active_theme_if_authenticated() {
+		wp_set_current_user( self::factory()->user->create( array( 'role' => 'administrator' ) ) );
+
+		$index = rest_do_request( '/' );
+		$this->assertArrayHasKey( 'https://api.w.org/active-theme', $index->get_links() );
+	}
+
 	public function _validate_as_integer_123( $value, $request, $key ) {
 		if ( ! is_int( $value ) ) {
Index: /trunk/tests/phpunit/tests/rest-api/rest-themes-controller.php
===================================================================
--- /trunk/tests/phpunit/tests/rest-api/rest-themes-controller.php	(revision 49924)
+++ /trunk/tests/phpunit/tests/rest-api/rest-themes-controller.php	(revision 49925)
@@ -31,4 +31,13 @@
 
 	/**
+	 * Admin user ID.
+	 *
+	 * @since 5.7.0
+	 *
+	 * @var int $admin_id
+	 */
+	protected static $admin_id;
+
+	/**
 	 * The current theme object.
 	 *
@@ -92,4 +101,9 @@
 	 */
 	public static function wpSetUpBeforeClass( WP_UnitTest_Factory $factory ) {
+		self::$admin_id       = $factory->user->create(
+			array(
+				'role' => 'administrator',
+			)
+		);
 		self::$subscriber_id  = $factory->user->create(
 			array(
@@ -115,4 +129,5 @@
 		self::delete_user( self::$subscriber_id );
 		self::delete_user( self::$contributor_id );
+		self::delete_user( self::$admin_id );
 	}
 
@@ -137,4 +152,5 @@
 		$routes = rest_get_server()->get_routes();
 		$this->assertArrayHasKey( self::$themes_route, $routes );
+		$this->assertArrayHasKey( self::$themes_route . '/(?P<stylesheet>[\\w-]+)', $routes );
 	}
 
@@ -152,4 +168,5 @@
 		$this->check_get_theme_response( $response );
 		$fields = array(
+			'_links',
 			'author',
 			'author_uri',
@@ -159,4 +176,5 @@
 			'requires_wp',
 			'screenshot',
+			'status',
 			'stylesheet',
 			'tags',
@@ -171,10 +189,72 @@
 
 	/**
+	 * Test retrieving a collection of inactive themes.
+	 *
+	 * @ticket 50152
+	 */
+	public function test_get_items_inactive() {
+		wp_set_current_user( self::$admin_id );
+		$request = new WP_REST_Request( 'GET', self::$themes_route );
+		$request->set_param( 'status', 'inactive' );
+
+		$response = rest_get_server()->dispatch( $request );
+
+		$this->assertEquals( 200, $response->get_status() );
+		$data = $response->get_data();
+
+		$fields = array(
+			'_links',
+			'author',
+			'author_uri',
+			'description',
+			'name',
+			'requires_php',
+			'requires_wp',
+			'screenshot',
+			'status',
+			'stylesheet',
+			'tags',
+			'template',
+			'textdomain',
+			'theme_uri',
+			'version',
+		);
+		$this->assertEqualSets( $fields, array_keys( $data[0] ) );
+
+		$this->assertContains( 'twentytwenty', wp_list_pluck( $data, 'stylesheet' ) );
+		$this->assertNotContains( get_stylesheet(), wp_list_pluck( $data, 'stylesheet' ) );
+	}
+
+	/**
+	 * Test retrieving a collection of inactive themes.
+	 *
+	 * @ticket 50152
+	 */
+	public function test_get_items_active_and_inactive() {
+		wp_set_current_user( self::$admin_id );
+		$request = new WP_REST_Request( 'GET', self::$themes_route );
+		$request->set_param( 'status', array( 'active', 'inactive' ) );
+
+		$response = rest_get_server()->dispatch( $request );
+
+		$this->assertEquals( 200, $response->get_status() );
+		$data = $response->get_data();
+
+		$this->assertContains( 'twentytwenty', wp_list_pluck( $data, 'stylesheet' ) );
+		$this->assertContains( get_stylesheet(), wp_list_pluck( $data, 'stylesheet' ) );
+	}
+
+	/**
 	 * @ticket 46723
-	 */
-	public function test_get_items_logged_out() {
+	 * @ticket 50152
+	 * @dataProvider data_get_items_by_status
+	 */
+	public function test_get_items_logged_out( $status, $error_code ) {
 		wp_set_current_user( 0 );
-		$response = self::perform_active_theme_request();
-		$this->assertErrorResponse( 'rest_user_cannot_view', $response, 401 );
+		$request = new WP_REST_Request( 'GET', self::$themes_route );
+		$request->set_param( 'status', $status );
+
+		$response = rest_get_server()->dispatch( $request );
+		$this->assertErrorResponse( $error_code, $response, 401 );
 	}
 
@@ -183,9 +263,50 @@
 	 *
 	 * @ticket 45016
-	 */
-	public function test_get_items_no_permission() {
+	 * @ticket 50152
+	 * @dataProvider data_get_items_by_status
+	 */
+	public function test_get_items_no_permission( $status, $error_code ) {
 		wp_set_current_user( self::$subscriber_id );
-		$response = self::perform_active_theme_request();
-		$this->assertErrorResponse( 'rest_user_cannot_view', $response, 403 );
+		$request = new WP_REST_Request( 'GET', self::$themes_route );
+		$request->set_param( 'status', $status );
+
+		$response = rest_get_server()->dispatch( $request );
+		$this->assertErrorResponse( $error_code, $response, 403 );
+	}
+
+	public function data_get_items_by_status() {
+		return array(
+			array( 'active', 'rest_cannot_view_active_theme' ),
+			array( 'active, inactive', 'rest_cannot_view_themes' ),
+			array( 'inactive', 'rest_cannot_view_themes' ),
+			array( '', 'rest_cannot_view_themes' ),
+		);
+	}
+
+	/**
+	 * @ticket 50152
+	 * @dataProvider data_get_items_by_status_for_contributor
+	 */
+	public function test_get_items_contributor( $status, $error_code ) {
+		wp_set_current_user( self::$contributor_id );
+		$request = new WP_REST_Request( 'GET', self::$themes_route );
+		$request->set_param( 'status', $status );
+
+		$response = rest_get_server()->dispatch( $request );
+
+		if ( $error_code ) {
+			$this->assertErrorResponse( $error_code, $response, 403 );
+		} else {
+			$this->assertEquals( 200, $response->get_status() );
+		}
+	}
+
+	public function data_get_items_by_status_for_contributor() {
+		return array(
+			array( 'active', '' ),
+			array( 'active, inactive', 'rest_cannot_view_themes' ),
+			array( 'inactive', 'rest_cannot_view_themes' ),
+			array( '', 'rest_cannot_view_themes' ),
+		);
 	}
 
@@ -222,5 +343,5 @@
 		$data       = $response->get_data();
 		$properties = $data['schema']['properties'];
-		$this->assertSame( 14, count( $properties ) );
+		$this->assertSame( 15, count( $properties ) );
 
 		$this->assertArrayHasKey( 'author', $properties );
@@ -243,4 +364,5 @@
 		$this->assertArrayHasKey( 'requires_wp', $properties );
 		$this->assertArrayHasKey( 'screenshot', $properties );
+		$this->assertArrayHasKey( 'status', $properties );
 		$this->assertArrayHasKey( 'stylesheet', $properties );
 
@@ -1084,7 +1206,79 @@
 
 	/**
-	 * The get_item() method does not exist for themes.
-	 */
-	public function test_get_item() {}
+	 * Test single theme
+	 *
+	 * @ticket 50152
+	 */
+	public function test_get_item() {
+		wp_set_current_user( self::$admin_id );
+		$route    = sprintf( '%s/%s', self::$themes_route, WP_DEFAULT_THEME );
+		$request  = new WP_REST_Request( 'GET', $route );
+		$response = rest_get_server()->dispatch( $request );
+
+		$this->assertEquals( 200, $response->get_status() );
+		$data         = $response->get_data();
+		$links        = $response->get_links();
+		$fields       = array(
+			'author',
+			'author_uri',
+			'description',
+			'name',
+			'requires_php',
+			'requires_wp',
+			'screenshot',
+			'status',
+			'stylesheet',
+			'tags',
+			'template',
+			'textdomain',
+			'theme_uri',
+			'version',
+		);
+		$fields_links = array( 'collection', 'self' );
+
+		$this->assertEqualSets( $fields, array_keys( $data ) );
+		$this->assertEqualSets( $fields_links, array_keys( $links ) );
+	}
+
+	/**
+	 * @ticket 50152
+	 */
+	public function test_get_item_no_permission() {
+		wp_set_current_user( self::$subscriber_id );
+		$request  = new WP_REST_Request( 'GET', self::$themes_route . '/' . WP_DEFAULT_THEME );
+		$response = rest_get_server()->dispatch( $request );
+		$this->assertErrorResponse( 'rest_cannot_view_themes', $response, 403 );
+	}
+
+	/**
+	 * @ticket 50152
+	 */
+	public function test_get_active_item_no_permission() {
+		wp_set_current_user( self::$subscriber_id );
+		$request  = new WP_REST_Request( 'GET', self::$themes_route . '/' . get_stylesheet() );
+		$response = rest_get_server()->dispatch( $request );
+		$this->assertErrorResponse( 'rest_cannot_view_active_theme', $response, 403 );
+	}
+
+	/**
+	 * @ticket 50152
+	 */
+	public function test_get_item_invalid() {
+		wp_set_current_user( self::$admin_id );
+		$request  = new WP_REST_Request( 'GET', self::$themes_route . '/invalid' );
+		$response = rest_get_server()->dispatch( $request );
+		$this->assertErrorResponse( 'rest_theme_not_found', $response, 404 );
+	}
+
+	/**
+	 * @ticket 50152
+	 */
+	public function test_get_active_item_as_contributor() {
+		$route    = sprintf( '%s/%s', self::$themes_route, get_stylesheet() );
+		$request  = new WP_REST_Request( 'GET', $route );
+		$response = rest_get_server()->dispatch( $request );
+
+		$this->assertEquals( 200, $response->get_status() );
+	}
 
 	/**
Index: /trunk/tests/qunit/fixtures/wp-api-generated.js
===================================================================
--- /trunk/tests/qunit/fixtures/wp-api-generated.js	(revision 49924)
+++ /trunk/tests/qunit/fixtures/wp-api-generated.js	(revision 49925)
@@ -5785,29 +5785,4 @@
                     ],
                     "args": {
-                        "context": {
-                            "description": "Scope under which the request is made; determines fields present in response.",
-                            "type": "string",
-                            "required": false
-                        },
-                        "page": {
-                            "description": "Current page of the collection.",
-                            "type": "integer",
-                            "default": 1,
-                            "minimum": 1,
-                            "required": false
-                        },
-                        "per_page": {
-                            "description": "Maximum number of items to be returned in result set.",
-                            "type": "integer",
-                            "default": 10,
-                            "minimum": 1,
-                            "maximum": 100,
-                            "required": false
-                        },
-                        "search": {
-                            "description": "Limit results to those matching a string.",
-                            "type": "string",
-                            "required": false
-                        },
                         "status": {
                             "description": "Limit result set to themes assigned one or more statuses.",
@@ -5815,9 +5790,10 @@
                             "items": {
                                 "enum": [
-                                    "active"
+                                    "active",
+                                    "inactive"
                                 ],
                                 "type": "string"
                             },
-                            "required": true
+                            "required": false
                         }
                     }
@@ -5827,4 +5803,24 @@
                 "self": "http://example.org/index.php?rest_route=/wp/v2/themes"
             }
+        },
+        "/wp/v2/themes/(?P<stylesheet>[\\w-]+)": {
+            "namespace": "wp/v2",
+            "methods": [
+                "GET"
+            ],
+            "endpoints": [
+                {
+                    "methods": [
+                        "GET"
+                    ],
+                    "args": {
+                        "stylesheet": {
+                            "description": "The theme's stylesheet. This uniquely identifies the theme.",
+                            "type": "string",
+                            "required": false
+                        }
+                    }
+                }
+            ]
         },
         "/wp/v2/plugins": {
