diff --git src/wp-admin/css/customize-controls.css src/wp-admin/css/customize-controls.css
index b35bc3b..6b9d910 100644
--- src/wp-admin/css/customize-controls.css
+++ src/wp-admin/css/customize-controls.css
@@ -123,13 +123,17 @@ body {
 	color: #0073aa;
 }
 
-#customize-controls .customize-info .customize-panel-description {
+#customize-controls .customize-info .customize-panel-description,
+#customize-controls .no-widget-areas-rendered-notice {
 	color: #555;
 	display: none;
 	background: #fff;
 	padding: 12px 15px;
 	border-top: 1px solid #ddd;
 }
+#customize-controls .customize-info .customize-panel-description.open + .no-widget-areas-rendered-notice {
+	border-top: none;
+}
 
 #customize-controls .customize-info .customize-panel-description p:first-child {
 	margin-top: 0;
diff --git src/wp-admin/js/customize-controls.js src/wp-admin/js/customize-controls.js
index e0e4a4d..8394d60 100644
--- src/wp-admin/js/customize-controls.js
+++ src/wp-admin/js/customize-controls.js
@@ -378,39 +378,48 @@
 		},
 
 		/**
-		 * @param {Boolean} expanded
-		 * @param {Object} [params]
-		 * @returns {Boolean} false if state already applied
+		 * Handle the toggle logic for expand/collapse.
+		 *
+		 * @param {Boolean}  expanded - The new state to apply.
+		 * @param {Object}   [params] - Object containing options for expand/collapse.
+		 * @param {Function} [params.completeCallback] - Function to call when expansion/collapse is complete.
+		 * @returns {Boolean} false if state already applied or active state is false
 		 */
-		_toggleExpanded: function ( expanded, params ) {
-			var self = this;
+		_toggleExpanded: function( expanded, params ) {
+			var instance = this, previousCompleteCallback;
 			params = params || {};
-			var section = this, previousCompleteCallback = params.completeCallback;
-			params.completeCallback = function () {
+			previousCompleteCallback = params.completeCallback;
+
+			// Short-circuit expand() if the instance is not active.
+			if ( expanded && ! instance.active() ) {
+				return false;
+			}
+
+			params.completeCallback = function() {
 				if ( previousCompleteCallback ) {
-					previousCompleteCallback.apply( section, arguments );
+					previousCompleteCallback.apply( instance, arguments );
 				}
 				if ( expanded ) {
-					section.container.trigger( 'expanded' );
+					instance.container.trigger( 'expanded' );
 				} else {
-					section.container.trigger( 'collapsed' );
+					instance.container.trigger( 'collapsed' );
 				}
 			};
-			if ( ( expanded && this.expanded.get() ) || ( ! expanded && ! this.expanded.get() ) ) {
+			if ( ( expanded && instance.expanded.get() ) || ( ! expanded && ! instance.expanded.get() ) ) {
 				params.unchanged = true;
-				self.onChangeExpanded( self.expanded.get(), params );
+				instance.onChangeExpanded( instance.expanded.get(), params );
 				return false;
 			} else {
 				params.unchanged = false;
-				this.expandedArgumentsQueue.push( params );
-				this.expanded.set( expanded );
+				instance.expandedArgumentsQueue.push( params );
+				instance.expanded.set( expanded );
 				return true;
 			}
 		},
 
 		/**
 		 * @param {Object} [params]
-		 * @returns {Boolean} false if already expanded
+		 * @returns {Boolean} false if already expanded or if inactive.
 		 */
 		expand: function ( params ) {
 			return this._toggleExpanded( true, params );
@@ -418,7 +427,7 @@
 
 		/**
 		 * @param {Object} [params]
-		 * @returns {Boolean} false if already collapsed
+		 * @returns {Boolean} false if already collapsed.
 		 */
 		collapse: function ( params ) {
 			return this._toggleExpanded( false, params );
@@ -539,6 +548,13 @@
 			};
 			section.panel.bind( inject );
 			inject( section.panel.get() ); // Since a section may never get a panel, assume that it won't ever get one
+
+			section.deferred.embedded.done(function() {
+				// Fix the top margin after reflow.
+				api.bind( 'pane-contents-reflowed', _.debounce( function() {
+					section._recalculateTopMargin();
+				}, 100 ) );
+			});
 		},
 
 		/**
@@ -646,13 +662,7 @@
 						// Fix the height after browser resize.
 						$( window ).on( 'resize.customizer-section', _.debounce( resizeContentHeight, 100 ) );
 
-						// Fix the top margin after reflow.
-						api.bind( 'pane-contents-reflowed', _.debounce( function() {
-							var offset = ( content.offset().top - headerActionsHeight );
-							if ( 0 < offset ) {
-								content.css( 'margin-top', ( parseInt( content.css( 'margin-top' ), 10 ) - offset ) );
-							}
-						}, 100 ) );
+						section._recalculateTopMargin();
 					};
 				}
 
@@ -693,6 +703,25 @@
 					args.completeCallback();
 				}
 			}
+		},
+
+		/**
+		 * Recalculate the top margin.
+		 *
+		 * @since 4.4.0
+		 * @private
+		 */
+		_recalculateTopMargin: function() {
+			var section = this, content, offset, headerActionsHeight;
+			content = section.container.find( '.accordion-section-content' );
+			if ( 0 === content.length ) {
+				return;
+			}
+			headerActionsHeight = $( '#customize-header-actions' ).height();
+			offset = ( content.offset().top - headerActionsHeight );
+			if ( 0 < offset ) {
+				content.css( 'margin-top', ( parseInt( content.css( 'margin-top' ), 10 ) - offset ) );
+			}
 		}
 	});
 
@@ -1155,6 +1184,11 @@
 				parentContainer.append( panel.container );
 				panel.renderContent();
 			}
+
+			api.bind( 'pane-contents-reflowed', _.debounce( function() {
+				panel._recalculateTopMargin();
+			}, 100 ) );
+
 			panel.deferred.embedded.resolve();
 		},
 
@@ -1253,7 +1287,7 @@
 		 * @param {Boolean}  expanded
 		 * @param {Object}   args
 		 * @param {Boolean}  args.unchanged
-		 * @param {Callback} args.completeCallback
+		 * @param {Function} args.completeCallback
 		 */
 		onChangeExpanded: function ( expanded, args ) {
 
@@ -1268,14 +1302,14 @@
 			// Note: there is a second argument 'args' passed
 			var position, scroll,
 				panel = this,
-				section = panel.container.closest( '.accordion-section' ), // This is actually the panel.
-				overlay = section.closest( '.wp-full-overlay' ),
-				container = section.closest( '.wp-full-overlay-sidebar-content' ),
+				accordionSection = panel.container.closest( '.accordion-section' ),
+				overlay = accordionSection.closest( '.wp-full-overlay' ),
+				container = accordionSection.closest( '.wp-full-overlay-sidebar-content' ),
 				siblings = container.find( '.open' ),
 				topPanel = overlay.find( '#customize-theme-controls > ul > .accordion-section > .accordion-section-title' ),
-				backBtn = section.find( '.customize-panel-back' ),
-				panelTitle = section.find( '.accordion-section-title' ).first(),
-				content = section.find( '.control-panel-content' ),
+				backBtn = accordionSection.find( '.customize-panel-back' ),
+				panelTitle = accordionSection.find( '.accordion-section-title' ).first(),
+				content = accordionSection.find( '.control-panel-content' ),
 				headerActionsHeight = $( '#customize-header-actions' ).height();
 
 			if ( expanded ) {
@@ -1297,7 +1331,7 @@
 					position = content.offset().top;
 					scroll = container.scrollTop();
 					content.css( 'margin-top', ( headerActionsHeight - position - scroll ) );
-					section.addClass( 'current-panel' );
+					accordionSection.addClass( 'current-panel' );
 					overlay.addClass( 'in-sub-panel' );
 					container.scrollTop( 0 );
 					if ( args.completeCallback ) {
@@ -1307,14 +1341,10 @@
 				topPanel.attr( 'tabindex', '-1' );
 				backBtn.attr( 'tabindex', '0' );
 				backBtn.focus();
-
-				// Fix the top margin after reflow.
-				api.bind( 'pane-contents-reflowed', _.debounce( function() {
-					content.css( 'margin-top', ( parseInt( content.css( 'margin-top' ), 10 ) - ( content.offset().top - headerActionsHeight ) ) );
-				}, 100 ) );
+				panel._recalculateTopMargin();
 			} else {
 				siblings.removeClass( 'open' );
-				section.removeClass( 'current-panel' );
+				accordionSection.removeClass( 'current-panel' );
 				overlay.removeClass( 'in-sub-panel' );
 				content.delay( 180 ).hide( 0, function() {
 					content.css( 'margin-top', 'inherit' ); // Reset
@@ -1330,6 +1360,20 @@
 		},
 
 		/**
+		 * Recalculate the top margin.
+		 *
+		 * @since 4.4.0
+		 * @private
+		 */
+		_recalculateTopMargin: function() {
+			var panel = this, headerActionsHeight, content, accordionSection;
+			headerActionsHeight = $( '#customize-header-actions' ).height();
+			accordionSection = panel.container.closest( '.accordion-section' )
+			content = accordionSection.find( '.control-panel-content' );
+			content.css( 'margin-top', ( parseInt( content.css( 'margin-top' ), 10 ) - ( content.offset().top - headerActionsHeight ) ) );
+		},
+
+		/**
 		 * Render the panel from its JS template, if it exists.
 		 *
 		 * The panel's container must already exist in the DOM.
diff --git src/wp-admin/js/customize-widgets.js src/wp-admin/js/customize-widgets.js
index bd25757..144365e 100644
--- src/wp-admin/js/customize-widgets.js
+++ src/wp-admin/js/customize-widgets.js
@@ -1506,6 +1506,80 @@
 	} );
 
 	/**
+	 * wp.customize.Widgets.WidgetsPanel
+	 *
+	 * Customizer panel containing the widget area sections.
+	 *
+	 * @since 4.4.0
+	 */
+	api.Widgets.WidgetsPanel = api.Panel.extend({
+
+		/**
+		 * Add and manage the display of the no-rendered-areas notice.
+		 *
+		 * @since 4.4.0
+		 */
+		ready: function () {
+			var panel = this;
+
+			api.Panel.prototype.ready.call( panel );
+
+			panel.deferred.embedded.done(function() {
+				var panelMetaContainer, noRenderedAreasNotice, shouldShowNotice;
+				panelMetaContainer = panel.container.find( '.panel-meta' );
+				noRenderedAreasNotice = $( '<div></div>', {
+					'class': 'no-widget-areas-rendered-notice'
+				});
+				noRenderedAreasNotice.append( $( '<em></em>', {
+					text: l10n.noAreasRendered
+				} ) );
+				panelMetaContainer.append( noRenderedAreasNotice );
+
+				shouldShowNotice = function() {
+					return ( 0 === _.filter( panel.sections(), function( section ) {
+						return section.active();
+					} ).length );
+				};
+
+				/*
+				 * Defer setting visibility of no-rendered-areas-notice until
+				 * preview finishes loading since this is when the active
+				 * sections become available.
+				 */
+				api.previewer.deferred.active.done( function () {
+					noRenderedAreasNotice.toggle( shouldShowNotice() );
+
+					// Update the visibility of the notice whenever a reflow happens.
+					api.bind( 'pane-contents-reflowed', function() {
+						api.previewer.deferred.active.done( function () {
+							if ( shouldShowNotice() ) {
+								noRenderedAreasNotice.slideDown( 'fast' );
+							} else {
+								noRenderedAreasNotice.slideUp( 'fast' );
+							}
+						});
+					});
+				});
+			});
+		},
+
+		/**
+		 * Allow an active widgets panel to be contextually active even when it has no active sections (widget areas).
+		 *
+		 * This ensures that the widgets panel appears even when there are no
+		 * sidebars displayed on the URL currently being previewed.
+		 *
+		 * @since 4.4.0
+		 *
+		 * @returns {boolean}
+		 */
+		isContextuallyActive: function() {
+			var panel = this;
+			return panel.active();
+		}
+	});
+
+	/**
 	 * wp.customize.Widgets.SidebarSection
 	 *
 	 * Customizer section representing a widget area widget
@@ -1968,7 +2042,10 @@
 		}
 	} );
 
-	// Register models for custom section and control types
+	// Register models for custom panel, section, and control types
+	$.extend( api.panelConstructor, {
+		widgets: api.Widgets.WidgetsPanel
+	});
 	$.extend( api.sectionConstructor, {
 		sidebar: api.Widgets.SidebarSection
 	});
diff --git src/wp-includes/class-wp-customize-widgets.php src/wp-includes/class-wp-customize-widgets.php
index a003393..7b7c064 100644
--- src/wp-includes/class-wp-customize-widgets.php
+++ src/wp-includes/class-wp-customize-widgets.php
@@ -355,9 +355,11 @@ final class WP_Customize_Widgets {
 		}
 
 		$this->manager->add_panel( 'widgets', array(
-			'title'       => __( 'Widgets' ),
-			'description' => __( 'Widgets are independent sections of content that can be placed into widgetized areas provided by your theme (commonly called sidebars).' ),
-			'priority'    => 110,
+			'type'            => 'widgets',
+			'title'           => __( 'Widgets' ),
+			'description'     => __( 'Widgets are independent sections of content that can be placed into widgetized areas provided by your theme (commonly called sidebars).' ),
+			'priority'        => 110,
+			'active_callback' => array( $this, 'is_panel_active' ),
 		) );
 
 		foreach ( $sidebars_widgets as $sidebar_id => $sidebar_widget_ids ) {
@@ -455,6 +457,22 @@ final class WP_Customize_Widgets {
 	}
 
 	/**
+	 * Return whether the widgets panel is active, based on whether there are sidebars registered.
+	 *
+	 * @since 4.4.0
+	 * @access public
+	 *
+	 * @see WP_Customize_Panel::$active_callback
+	 *
+	 * @global array $wp_registered_sidebars
+	 * @return bool Active.
+	 */
+	public function is_panel_active() {
+		global $wp_registered_sidebars;
+		return ! empty( $wp_registered_sidebars );
+	}
+
+	/**
 	 * Covert a widget_id into its corresponding Customizer setting ID (option name).
 	 *
 	 * @since 3.9.0
@@ -655,6 +673,7 @@ final class WP_Customize_Widgets {
 				'error'            => __( 'An error has occurred. Please reload the page and try again.' ),
 				'widgetMovedUp'    => __( 'Widget moved up' ),
 				'widgetMovedDown'  => __( 'Widget moved down' ),
+				'noAreasRendered'  => __( 'There are no widget areas currently rendered in the preview. Navigate in the preview to a template that makes use of a widget area in order to access its widgets here.' ),
 			),
 			'tpl' => array(
 				'widgetReorderNav' => $widget_reorder_nav_tpl,
