diff --git a/misskey-assets b/misskey-assets
index cf3ce27b2eb8417233072e3d6d2fb7c5356c2364..0179793ec891856d6f37a3be16ba4c22f67a81b5 160000
--- a/misskey-assets
+++ b/misskey-assets
@@ -1 +1 @@
-Subproject commit cf3ce27b2eb8417233072e3d6d2fb7c5356c2364
+Subproject commit 0179793ec891856d6f37a3be16ba4c22f67a81b5
diff --git a/packages/frontend/src/components/MkWidgets.vue b/packages/frontend/src/components/MkWidgets.vue
index fff89117ce3a29b16b21ed904405dd40caea0f85..3502e090e75025b4ff48a203d03d500c1edee30e 100644
--- a/packages/frontend/src/components/MkWidgets.vue
+++ b/packages/frontend/src/components/MkWidgets.vue
@@ -1,7 +1,7 @@
 <template>
-<div class="vjoppmmu">
+<div :class="$style.root">
 	<template v-if="edit">
-		<header>
+		<header :class="$style['edit-header']">
 			<MkSelect v-model="widgetAdderSelected" style="margin-bottom: var(--margin)" class="mk-widget-select">
 				<template #label>{{ i18n.ts.selectWidget }}</template>
 				<option v-for="widget in widgetDefs" :key="widget" :value="widget">{{ i18n.t(`_widgets.${widget}`) }}</option>
@@ -14,23 +14,34 @@
 			item-key="id"
 			handle=".handle"
 			:animation="150"
+			:group="{ name: 'SortableMkWidgets' }"
 			@update:model-value="v => emit('updateWidgets', v)"
+			:class="$style['edit-editing']"
 		>
 			<template #item="{element}">
-				<div class="customize-container">
-					<button class="config _button" @click.prevent.stop="configWidget(element.id)"><i class="ti ti-settings"></i></button>
-					<button class="remove _button" @click.prevent.stop="removeWidget(element)"><i class="ti ti-x"></i></button>
+				<div :class="[$style.widget, $style['customize-container']]">
+					<button :class="$style['customize-container-config']" class="_button" @click.prevent.stop="configWidget(element.id)"><i class="ti ti-settings"></i></button>
+					<button :class="$style['customize-container-remove']" class="_button" @click.prevent.stop="removeWidget(element)"><i class="ti ti-x"></i></button>
 					<div class="handle">
-						<component :is="`mkw-${element.name}`" :ref="el => widgetRefs[element.id] = el" class="widget" :widget="element" @update-props="updateWidget(element.id, $event)"/>
+						<component :is="`mkw-${element.name}`" :ref="el => widgetRefs[element.id] = el" class="widget" :class="$style['customize-container-handle-widget']" :widget="element" @update-props="updateWidget(element.id, $event)"/>
 					</div>
 				</div>
 			</template>
 		</Sortable>
 	</template>
-	<component :is="`mkw-${widget.name}`" v-for="widget in widgets" v-else :key="widget.id" :ref="el => widgetRefs[widget.id] = el" class="widget" :widget="widget" @update-props="updateWidget(widget.id, $event)" @contextmenu.stop="onContextmenu(widget, $event)"/>
+	<component :is="`mkw-${widget.name}`" v-for="widget in widgets" v-else :key="widget.id" :ref="el => widgetRefs[widget.id] = el" :class="$style.widget" :widget="widget" @update-props="updateWidget(widget.id, $event)" @contextmenu.stop="onContextmenu(widget, $event)"/>
 </div>
 </template>
-
+<script lang="ts">
+export type Widget = {
+	name: string;
+	id: string;
+	data: Record<string, any>;
+};
+export type DefaultStoredWidget = {
+	place: string | null;
+} & Widget;
+</script>
 <script lang="ts" setup>
 import { defineAsyncComponent, reactive, ref, computed } from 'vue';
 import { v4 as uuid } from 'uuid';
@@ -43,12 +54,6 @@ import { deepClone } from '@/scripts/clone';
 
 const Sortable = defineAsyncComponent(() => import('vuedraggable').then(x => x.default));
 
-type Widget = {
-	name: string;
-	id: string;
-	data: Record<string, any>;
-};
-
 const props = defineProps<{
 	widgets: Widget[];
 	edit: boolean;
@@ -109,11 +114,22 @@ function onContextmenu(widget: Widget, ev: MouseEvent) {
 }
 </script>
 
-<style lang="scss" scoped>
-.vjoppmmu {
+<style lang="scss" module>
+.root {
 	container-type: inline-size;
+}
+
+.widget {
+	contain: content;
+	margin: var(--margin) 0;
 
-	> header {
+	&:first-of-type {
+		margin-top: 0;
+	}
+}
+
+.edit {
+	&-header {
 		margin: 16px 0;
 
 		> * {
@@ -122,44 +138,42 @@ function onContextmenu(widget: Widget, ev: MouseEvent) {
 		}
 	}
 
-	> .widget, .customize-container {
-		contain: content;
-		margin: var(--margin) 0;
+	&-editing {
+		min-height: 100px;
+	}
+}
 
-		&:first-of-type {
-			margin-top: 0;
-		}
+.customize-container {
+	position: relative;
+	cursor: move;
+
+	&-config,
+	&-remove {
+		position: absolute;
+		z-index: 10000;
+		top: 8px;
+		width: 32px;
+		height: 32px;
+		color: #fff;
+		background: rgba(#000, 0.7);
+		border-radius: 4px;
 	}
 
-	.customize-container {
-		position: relative;
-		cursor: move;
-
-		> .config,
-		> .remove {
-			position: absolute;
-			z-index: 10000;
-			top: 8px;
-			width: 32px;
-			height: 32px;
-			color: #fff;
-			background: rgba(#000, 0.7);
-			border-radius: 4px;
-		}
+	&-config {
+		right: 8px + 8px + 32px;
+	}
 
-		> .config {
-			right: 8px + 8px + 32px;
-		}
+	&-remove {
+		right: 8px;
+	}
 
-		> .remove {
-			right: 8px;
-		}
+	&-handle {
 
-		> .handle {
-			> .widget {
-				pointer-events: none;
-			}
-		}
+		&-widget {
+			pointer-events: none;
+		} 
 	}
+
 }
+
 </style>
diff --git a/packages/frontend/src/store.ts b/packages/frontend/src/store.ts
index 1bedab5fadccbc5044796b20cd44d680999e644c..8e3e6b36da6f15972202252cbf3cfdb846d131ad 100644
--- a/packages/frontend/src/store.ts
+++ b/packages/frontend/src/store.ts
@@ -122,7 +122,7 @@ export const defaultStore = markRaw(new Storage('base', {
 		}[],
 	},
 	widgets: {
-		where: 'deviceAccount',
+		where: 'account',
 		default: [] as {
 			name: string;
 			id: string;
diff --git a/packages/frontend/src/ui/classic.vue b/packages/frontend/src/ui/classic.vue
index 0e726c11ed96874bcea7e9cc2103e11317530619..44b017ea965b3e293e506e52ba01b572f1a4f01a 100644
--- a/packages/frontend/src/ui/classic.vue
+++ b/packages/frontend/src/ui/classic.vue
@@ -7,7 +7,7 @@
 			<XSidebar/>
 		</div>
 		<div v-else ref="widgetsLeft" class="widgets left">
-			<XWidgets :place="'left'" @mounted="attachSticky(widgetsLeft)"/>
+			<XWidgets place="left" @mounted="attachSticky(widgetsLeft)"/>
 		</div>
 
 		<main class="main" :style="{ background: pageMetadata?.value?.bg }" @contextmenu.stop="onContextmenu">
@@ -17,7 +17,7 @@
 		</main>
 
 		<div v-if="isDesktop" ref="widgetsRight" class="widgets right">
-			<XWidgets :place="null" @mounted="attachSticky(widgetsRight)"/>
+			<XWidgets :place="showMenuOnTop ? 'right' : null" @mounted="attachSticky(widgetsRight)"/>
 		</div>
 	</div>
 
@@ -52,7 +52,7 @@ import { PageMetadata, provideMetadataReceiver, setPageMetadata } from '@/script
 import { defaultStore } from '@/store';
 import { i18n } from '@/i18n';
 const XHeaderMenu = defineAsyncComponent(() => import('./classic.header.vue'));
-const XWidgets = defineAsyncComponent(() => import('./classic.widgets.vue'));
+const XWidgets = defineAsyncComponent(() => import('./universal.widgets.vue'));
 
 const DESKTOP_THRESHOLD = 1100;
 
diff --git a/packages/frontend/src/ui/classic.widgets.vue b/packages/frontend/src/ui/classic.widgets.vue
deleted file mode 100644
index 163ec982cec83bf8a336147d6b95628a6da1b034..0000000000000000000000000000000000000000
--- a/packages/frontend/src/ui/classic.widgets.vue
+++ /dev/null
@@ -1,84 +0,0 @@
-<template>
-<div class="ddiqwdnk">
-	<XWidgets class="widgets" :edit="editMode" :widgets="$store.reactiveState.widgets.value.filter(w => w.place === place)" @add-widget="addWidget" @remove-widget="removeWidget" @update-widget="updateWidget" @update-widgets="updateWidgets" @exit="editMode = false"/>
-	<MkAd class="a" :prefer="['square']"/>
-
-	<button v-if="editMode" class="_textButton edit" style="font-size: 0.9em;" @click="editMode = false"><i class="ti ti-check"></i> {{ $ts.editWidgetsExit }}</button>
-	<button v-else class="_textButton edit" style="font-size: 0.9em;" @click="editMode = true"><i class="ti ti-pencil"></i> {{ $ts.editWidgets }}</button>
-</div>
-</template>
-
-<script lang="ts">
-import { defineComponent, defineAsyncComponent } from 'vue';
-import XWidgets from '@/components/MkWidgets.vue';
-
-export default defineComponent({
-	components: {
-		XWidgets,
-	},
-
-	props: {
-		place: {
-			type: String,
-		},
-	},
-
-	emits: ['mounted'],
-
-	data() {
-		return {
-			editMode: false,
-		};
-	},
-
-	mounted() {
-		this.$emit('mounted', this.$el);
-	},
-
-	methods: {
-		addWidget(widget) {
-			this.$store.set('widgets', [{
-				...widget,
-				place: this.place,
-			}, ...this.$store.state.widgets]);
-		},
-
-		removeWidget(widget) {
-			this.$store.set('widgets', this.$store.state.widgets.filter(w => w.id !== widget.id));
-		},
-
-		updateWidget({ id, data }) {
-			this.$store.set('widgets', this.$store.state.widgets.map(w => w.id === id ? {
-				...w,
-				data,
-			} : w));
-		},
-
-		updateWidgets(widgets) {
-			this.$store.set('widgets', [
-				...this.$store.state.widgets.filter(w => w.place !== this.place),
-				...widgets,
-			]);
-		},
-	},
-});
-</script>
-
-<style lang="scss" scoped>
-.ddiqwdnk {
-	position: sticky;
-	height: min-content;
-	box-sizing: border-box;
-	padding-bottom: 8px;
-
-	> .widgets,
-	> .a {
-		width: 300px;
-	}
-
-	> .edit {
-		display: block;
-		margin: 16px auto;
-	}
-}
-</style>
diff --git a/packages/frontend/src/ui/deck/widgets-column.vue b/packages/frontend/src/ui/deck/widgets-column.vue
index fc61d18ff6d0f73d392d8f59ff1b40dcc553f7d6..edc54c1c2b7971ac40941b9688d7d52b421612c4 100644
--- a/packages/frontend/src/ui/deck/widgets-column.vue
+++ b/packages/frontend/src/ui/deck/widgets-column.vue
@@ -4,7 +4,7 @@
 
 	<div class="wtdtxvec">
 		<div v-if="!(column.widgets && column.widgets.length > 0) && !edit" class="intro">{{ i18n.ts._deck.widgetsIntroduction }}</div>
-		<XWidgets :edit="edit" :widgets="column.widgets" @add-widget="addWidget" @remove-widget="removeWidget" @update-widget="updateWidget" @update-widgets="updateWidgets" @exit="edit = false"/>
+		<XWidgets :edit="edit" :widgets="column.widgets ?? []" @add-widget="addWidget" @remove-widget="removeWidget" @update-widget="updateWidget" @update-widgets="updateWidgets" @exit="edit = false"/>
 	</div>
 </XColumn>
 </template>
diff --git a/packages/frontend/src/ui/universal.vue b/packages/frontend/src/ui/universal.vue
index b91bf476e82f92be1013b65786d8b083f3c94784..2cb8a9d6a6db250c2dcf0acd4bdfa0346cc88056 100644
--- a/packages/frontend/src/ui/universal.vue
+++ b/packages/frontend/src/ui/universal.vue
@@ -273,7 +273,7 @@ const wallpaper = localStorage.getItem('wallpaper') != null;
 		right: 0;
 		z-index: 1001;
 		height: 100dvh;
-		padding: var(--margin);
+		padding: var(--margin) !important;
 		box-sizing: border-box;
 		overflow: auto;
 		overscroll-behavior: contain;
diff --git a/packages/frontend/src/ui/universal.widgets.vue b/packages/frontend/src/ui/universal.widgets.vue
index 33fb49283693af69f937fc71b69d2c5c4907fa66..002aab10901f7d60a0303ded6df92818c1f2b041 100644
--- a/packages/frontend/src/ui/universal.widgets.vue
+++ b/packages/frontend/src/ui/universal.widgets.vue
@@ -1,25 +1,44 @@
 <template>
-<div class="efzpzdvf">
-	<XWidgets :edit="editMode" :widgets="defaultStore.reactiveState.widgets.value" @add-widget="addWidget" @remove-widget="removeWidget" @update-widget="updateWidget" @update-widgets="updateWidgets" @exit="editMode = false"/>
+<div class="efzpzdvf" :class="{ universal: !classic, classic }">
+	<XWidgets :edit="editMode" :widgets="widgets" @add-widget="addWidget" @remove-widget="removeWidget" @update-widget="updateWidget" @update-widgets="updateWidgets" @exit="editMode = false"/>
 
 	<button v-if="editMode" class="_textButton" style="font-size: 0.9em;" @click="editMode = false"><i class="ti ti-check"></i> {{ i18n.ts.editWidgetsExit }}</button>
 	<button v-else class="_textButton mk-widget-edit" style="font-size: 0.9em;" @click="editMode = true"><i class="ti ti-pencil"></i> {{ i18n.ts.editWidgets }}</button>
 </div>
 </template>
 
+<script lang="ts">
+let editMode = $ref(false);
+</script>
 <script lang="ts" setup>
 import { onMounted } from 'vue';
 import XWidgets from '@/components/MkWidgets.vue';
 import { i18n } from '@/i18n';
 import { defaultStore } from '@/store';
 
+const props = withDefaults(defineProps<{
+	// null = 全てのウィジェットを表示
+	// left = place: leftだけを表示
+	// right = rightとnullを表示
+	place?: 'left' | null | 'right';
+	classic?: boolean;
+}>(), {
+	place: null,
+	classic: false,
+});
+
 const emit = defineEmits<{
-	(ev: 'mounted', el: Element): void;
+	(ev: 'mounted', el?: Element): void;
 }>();
 
-let editMode = $ref(false);
 let rootEl = $ref<HTMLDivElement>();
 
+const widgets = $computed(() => {
+	if (props.place === null) return defaultStore.reactiveState.widgets.value;
+	if (props.place === 'left') return defaultStore.reactiveState.widgets.value.filter(w => w.place === 'left');
+	return defaultStore.reactiveState.widgets.value.filter(w => w.place !== 'left');
+});
+
 onMounted(() => {
 	emit('mounted', rootEl);
 });
@@ -27,7 +46,7 @@ onMounted(() => {
 function addWidget(widget) {
 	defaultStore.set('widgets', [{
 		...widget,
-		place: null,
+		place: props.place,
 	}, ...defaultStore.state.widgets]);
 }
 
@@ -39,11 +58,26 @@ function updateWidget({ id, data }) {
 	defaultStore.set('widgets', defaultStore.state.widgets.map(w => w.id === id ? {
 		...w,
 		data,
+		place: props.place,
 	} : w));
 }
 
-function updateWidgets(widgets) {
-	defaultStore.set('widgets', widgets);
+function updateWidgets(thisWidgets) {
+	if (props.place === null) {
+		defaultStore.set('widgets', thisWidgets);
+		return;
+	}
+	if (props.place === 'left') {
+		defaultStore.set('widgets', [
+			...thisWidgets.map(w => ({ ...w, place: 'left' })),
+			...defaultStore.state.widgets.filter(w => w.place !== 'left' && !thisWidgets.some(t => w.id === t.id)),
+		]);
+		return;
+	}
+	defaultStore.set('widgets', [
+		...defaultStore.state.widgets.filter(w => w.place === 'left' && !thisWidgets.some(t => w.id === t.id)),
+		...thisWidgets.map(w => ({ ...w, place: 'right' })),
+	]);
 }
 </script>
 
@@ -52,11 +86,17 @@ function updateWidgets(widgets) {
 	position: sticky;
 	height: min-content;
 	min-height: 100vh;
-	padding: var(--margin) 0;
 	box-sizing: border-box;
 
+	&.universal {
+		padding: var(--margin) 0;
+
+		> * {
+			margin: var(--margin) 0;
+		}
+	}
+
 	> * {
-		margin: var(--margin) 0;
 		width: 300px;
 
 		&:first-child {