date:desc or title:asc.',
+ 'widget_collection_title_instructions' => 'Override the widget title. Defaults to the collection title.',
+ 'widget_form_description' => 'Shows recent submissions for a form.',
+ 'widget_form_form_instructions' => 'The handle of the form to display submissions for.',
+ 'widget_template_description' => 'Renders a custom Blade or Antlers template.',
+ 'widget_template_template_instructions' => 'Path to the template, relative to the views directory.',
+ 'widget_updater_description' => 'Shows available updates for Statamic and your installed addons.',
'width_x_height' => ':width × :height',
];
diff --git a/resources/css/components/dashboard.css b/resources/css/components/dashboard.css
new file mode 100644
index 00000000000..7323634b90b
--- /dev/null
+++ b/resources/css/components/dashboard.css
@@ -0,0 +1,40 @@
+/* ==========================================================================
+ DASHBOARD WIDGET EDITING
+ ========================================================================== */
+
+.dashboard-widget-inner {
+ @apply @container/widget relative rounded-xl;
+}
+
+/* Hide the chrome on the dragging source — the mirror is what the user sees */
+.dashboard-widget-sortable.draggable-source--is-dragging .dashboard-widget-inner {
+ @apply opacity-50;
+}
+
+.dashboard-widget-sortable.draggable-source--is-dragging [data-widget-edit-chrome] {
+ @apply opacity-0;
+}
+
+.dashboard-widget-sortable.draggable-source--is-dragging .dashboard-widget-inner::after {
+ content: '';
+ @apply pointer-events-none absolute inset-0 rounded-xl border-2 border-dashed border-ui-accent-bg/40 bg-ui-accent-bg/5 dark:bg-ui-accent-bg/10;
+}
+
+/* Mirror — the floating element that follows the cursor while dragging */
+body > .draggable-mirror.dashboard-widget-mirror {
+ @apply rounded-xl shadow-ui-lg ring-1 ring-black/5 dark:ring-white/10;
+ transition: none !important;
+ will-change: transform;
+}
+
+body > .draggable-mirror.dashboard-widget-mirror [data-widget-edit-chrome] {
+ @apply bg-white/95 dark:bg-gray-850/95;
+}
+
+.dashboard-widget-placeholder {
+ @apply flex min-h-54 flex-col items-center justify-center gap-3 rounded-xl bg-gray-50 px-6 py-10 text-center ring-1 ring-black/5 dark:bg-gray-900/40 dark:ring-white/10;
+}
+
+.dashboard-widget-empty {
+ @apply flex min-h-64 flex-col items-center justify-center gap-4 rounded-xl bg-gray-50 px-8 py-16 text-center ring-1 ring-black/5 dark:bg-gray-900/30 dark:ring-white/10;
+}
diff --git a/resources/css/cp.css b/resources/css/cp.css
index dd8ab0f1172..542e7b73093 100644
--- a/resources/css/cp.css
+++ b/resources/css/cp.css
@@ -13,6 +13,7 @@
@import './components/assets.css';
@import './components/blueprints.css';
@import './components/configure.css';
+@import './components/dashboard.css';
@import './components/focal-point.css';
@import './components/index-fields.css';
@import './components/notifications.css';
diff --git a/resources/js/components/dashboard/WidgetConfigStack.vue b/resources/js/components/dashboard/WidgetConfigStack.vue
new file mode 100644
index 00000000000..3b664b187c2
--- /dev/null
+++ b/resources/js/components/dashboard/WidgetConfigStack.vue
@@ -0,0 +1,54 @@
+
+
+
+